提交 6b25e21f 编写于 作者: L Linus Torvalds

Merge tag 'drm-for-v4.9' of git://people.freedesktop.org/~airlied/linux

Pull drm updates from Dave Airlie:
 "Core:
   - Fence destaging work
   - DRIVER_LEGACY to split off legacy drm drivers
   - drm_mm refactoring
   - Splitting drm_crtc.c into chunks and documenting better
   - Display info fixes
   - rbtree support for prime buffer lookup
   - Simple VGA DAC driver

  Panel:
   - Add Nexus 7 panel
   - More simple panels

  i915:
   - Refactoring GEM naming
   - Refactored vma/active tracking
   - Lockless request lookups
   - Better stolen memory support
   - FBC fixes
   - SKL watermark fixes
   - VGPU improvements
   - dma-buf fencing support
   - Better DP dongle support

  amdgpu:
   - Powerplay for Iceland asics
   - Improved GPU reset support
   - UVD/VEC powergating support for CZ/ST
   - Preinitialised VRAM buffer support
   - Virtual display support
   - Initial SI support
   - GTT rework
   - PCI shutdown callback support
   - HPD IRQ storm fixes

  amdkfd:
   - bugfixes

  tilcdc:
   - Atomic modesetting support

  mediatek:
   - AAL + GAMMA engine support
   - Hook up gamma LUT
   - Temporal dithering support

  imx:
   - Pixel clock from devicetree
   - drm bridge support for LVDS bridges
   - active plane reconfiguration
   - VDIC deinterlacer support
   - Frame synchronisation unit support
   - Color space conversion support

  analogix:
   - PSR support
   - Better panel on/off support

  rockchip:
   - rk3399 vop/crtc support
   - PSR support

  vc4:
   - Interlaced vblank timing
   - 3D rendering CPU overhead reduction
   - HDMI output fixes

  tda998x:
   - HDMI audio ASoC support

  sunxi:
   - Allwinner A33 support
   - better TCON support

  msm:
   - DT binding cleanups
   - Explicit fence-fd support

  sti:
   - remove sti415/416 support

  etnaviv:
   - MMUv2 refactoring
   - GC3000 support

  exynos:
   - Refactoring HDMI DCC/PHY
   - G2D pm regression fix
   - Page fault issues with wait for vblank

  There is no nouveau work in this tree, as Ben didn't get a pull
  request in, and he was fighting moving to atomic and adding mst
  support, so maybe best it waits for a cycle"

* tag 'drm-for-v4.9' of git://people.freedesktop.org/~airlied/linux: (1412 commits)
  drm/crtc: constify drm_crtc_index parameter
  drm/i915: Fix conflict resolution from backmerge of v4.8-rc8 to drm-next
  drm/i915/guc: Unwind GuC workqueue reservation if request construction fails
  drm/i915: Reset the breadcrumbs IRQ more carefully
  drm/i915: Force relocations via cpu if we run out of idle aperture
  drm/i915: Distinguish last emitted request from last submitted request
  drm/i915: Allow DP to work w/o EDID
  drm/i915: Move long hpd handling into the hotplug work
  drm/i915/execlists: Reinitialise context image after GPU hang
  drm/i915: Use correct index for backtracking HUNG semaphores
  drm/i915: Unalias obj->phys_handle and obj->userptr
  drm/i915: Just clear the mmiodebug before a register access
  drm/i915/gen9: only add the planes actually affected by ddb changes
  drm/i915: Allow PCH DPLL sharing regardless of DPLL_SDVO_HIGH_SPEED
  drm/i915/bxt: Fix HDMI DPLL configuration
  drm/i915/gen9: fix the watermark res_blocks value
  drm/i915/gen9: fix plane_blocks_per_line on watermarks calculations
  drm/i915/gen9: minimum scanlines for Y tile is not always 4
  drm/i915/gen9: fix the WaWmMemoryReadLatency implementation
  drm/i915/kbl: KBL also needs to run the SAGV code
  ...
Dumb RGB to VGA DAC bridge
---------------------------
This binding is aimed for dumb RGB to VGA DAC based bridges that do not require
any configuration.
Required properties:
- compatible: Must be "dumb-vga-dac"
Required nodes:
This device has two video ports. Their connections are modelled using the OF
graph bindings specified in Documentation/devicetree/bindings/graph.txt.
- Video port 0 for RGB input
- Video port 1 for VGA output
Example
-------
bridge {
compatible = "dumb-vga-dac";
#address-cells = <1>;
#size-cells = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
vga_bridge_in: endpoint {
remote-endpoint = <&tcon0_out_vga>;
};
};
port@1 {
reg = <1>;
vga_bridge_out: endpoint {
remote-endpoint = <&vga_con_in>;
};
};
};
};
......@@ -21,8 +21,19 @@ Optional properties:
- video-ports: 24 bits value which defines how the video controller
output is wired to the TDA998x input - default: <0x230145>
- audio-ports: array of 8-bit values, 2 values per one DAI[1].
The first value defines the DAI type: TDA998x_SPDIF or TDA998x_I2S[2].
The second value defines the tda998x AP_ENA reg content when the DAI
in question is used. The implementation allows one or two DAIs. If two
DAIs are defined, they must be of different type.
[1] Documentation/sound/alsa/soc/DAI.txt
[2] include/dt-bindings/display/tda998x.h
Example:
#include <dt-bindings/display/tda998x.h>
tda998x: hdmi-encoder {
compatible = "nxp,tda998x";
reg = <0x70>;
......@@ -30,4 +41,11 @@ Example:
interrupts = <27 2>; /* falling edge */
pinctrl-0 = <&pmx_camera>;
pinctrl-names = "default";
video-ports = <0x230145>;
#sound-dai-cells = <2>;
/* DAI-format AP_ENA reg value */
audio-ports = < TDA998x_SPDIF 0x04
TDA998x_I2S 0x03>;
};
......@@ -14,17 +14,16 @@ Required properties:
- power-domains: Should be <&mmcc MDSS_GDSC>.
- clocks: device clocks
See ../clocks/clock-bindings.txt for details.
- qcom,hdmi-tx-ddc-clk-gpio: ddc clk pin
- qcom,hdmi-tx-ddc-data-gpio: ddc data pin
- qcom,hdmi-tx-hpd-gpio: hpd pin
- core-vdda-supply: phandle to supply regulator
- hdmi-mux-supply: phandle to mux regulator
- phys: the phandle for the HDMI PHY device
- phy-names: the name of the corresponding PHY device
Optional properties:
- qcom,hdmi-tx-mux-en-gpio: hdmi mux enable pin
- qcom,hdmi-tx-mux-sel-gpio: hdmi mux select pin
- hpd-gpios: hpd pin
- qcom,hdmi-tx-mux-en-gpios: hdmi mux enable pin
- qcom,hdmi-tx-mux-sel-gpios: hdmi mux select pin
- qcom,hdmi-tx-mux-lpm-gpios: hdmi mux lpm pin
- power-domains: reference to the power domain(s), if available.
- pinctrl-names: the pin control state names; should contain "default"
- pinctrl-0: the default pinctrl state (active)
......
Innolux Corporation 10.1" G101ICE-L01 WXGA (1280x800) LVDS panel
Required properties:
- compatible: should be "innolux,g101ice-l01"
This binding is compatible with the simple-panel binding, which is specified
in simple-panel.txt in this directory.
JDI model LT070ME05000 1200x1920 7" DSI Panel
Required properties:
- compatible: should be "jdi,lt070me05000"
- vddp-supply: phandle of the regulator that provides the supply voltage
Power IC supply (3-5V)
- iovcc-supply: phandle of the regulator that provides the supply voltage
IOVCC , power supply for LCM (1.8V)
- enable-gpios: phandle of gpio for enable line
LED_EN, LED backlight enable, High active
- reset-gpios: phandle of gpio for reset line
This should be 8mA, gpio can be configured using mux, pinctrl, pinctrl-names
XRES, Reset, Low active
- dcdc-en-gpios: phandle of the gpio for power ic line
Power IC supply enable, High active
Example:
dsi0: qcom,mdss_dsi@4700000 {
panel@0 {
compatible = "jdi,lt070me05000";
reg = <0>;
vddp-supply = <&pm8921_l17>;
iovcc-supply = <&pm8921_lvs7>;
enable-gpios = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
reset-gpios = <&tlmm_pinmux 54 GPIO_ACTIVE_LOW>;
dcdc-en-gpios = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
};
};
......@@ -6,8 +6,10 @@ buffer to an external LCD interface.
Required properties:
- compatible: value should be one of the following
"rockchip,rk3288-vop";
"rockchip,rk3036-vop";
"rockchip,rk3288-vop";
"rockchip,rk3399-vop-big";
"rockchip,rk3399-vop-lit";
- interrupts: should contain a list of all VOP IP block interrupts in the
order: VSYNC, LCD_SYSTEM. The interrupt specifier
......
......@@ -26,13 +26,14 @@ TCON
The TCON acts as a timing controller for RGB, LVDS and TV interfaces.
Required properties:
- compatible: value should be "allwinner,sun5i-a13-tcon".
- compatible: value must be either:
* allwinner,sun5i-a13-tcon
* allwinner,sun8i-a33-tcon
- reg: base address and size of memory-mapped region
- interrupts: interrupt associated to this IP
- clocks: phandles to the clocks feeding the TCON. Three are needed:
- 'ahb': the interface clocks
- 'tcon-ch0': The clock driving the TCON channel 0
- 'tcon-ch1': The clock driving the TCON channel 1
- resets: phandles to the reset controllers driving the encoder
- "lcd": the reset line for the TCON channel 0
......@@ -49,6 +50,33 @@ Required properties:
second the block connected to the TCON channel 1 (usually the TV
encoder)
On the A13, there is one more clock required:
- 'tcon-ch1': The clock driving the TCON channel 1
DRC
---
The DRC (Dynamic Range Controller), found in the latest Allwinner SoCs
(A31, A23, A33), allows to dynamically adjust pixel
brightness/contrast based on histogram measurements for LCD content
adaptive backlight control.
Required properties:
- compatible: value must be one of:
* allwinner,sun8i-a33-drc
- reg: base address and size of the memory-mapped region.
- interrupts: interrupt associated to this IP
- clocks: phandles to the clocks feeding the DRC
* ahb: the DRC interface clock
* mod: the DRC module clock
* ram: the DRC DRAM clock
- clock-names: the clock names mentioned above
- resets: phandles to the reset line driving the DRC
- ports: A ports node with endpoint definitions as defined in
Documentation/devicetree/bindings/media/video-interfaces.txt. The
first port should be the input endpoints, the second one the outputs
Display Engine Backend
----------------------
......@@ -59,6 +87,7 @@ system.
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-backend
* allwinner,sun8i-a33-display-backend
- reg: base address and size of the memory-mapped region.
- clocks: phandles to the clocks feeding the frontend and backend
* ahb: the backend interface clock
......@@ -71,6 +100,14 @@ Required properties:
Documentation/devicetree/bindings/media/video-interfaces.txt. The
first port should be the input endpoints, the second one the output
On the A33, some additional properties are required:
- reg needs to have an additional region corresponding to the SAT
- reg-names need to be set, with "be" and "sat"
- clocks and clock-names need to have a phandle to the SAT bus
clocks, whose name will be "sat"
- resets and reset-names need to have a phandle to the SAT bus
resets, whose name will be "sat"
Display Engine Frontend
-----------------------
......@@ -80,6 +117,7 @@ deinterlacing and color space conversion.
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-frontend
* allwinner,sun8i-a33-display-frontend
- reg: base address and size of the memory-mapped region.
- interrupts: interrupt associated to this IP
- clocks: phandles to the clocks feeding the frontend and backend
......@@ -104,6 +142,7 @@ extra node.
Required properties:
- compatible: value must be one of:
* allwinner,sun5i-a13-display-engine
* allwinner,sun8i-a33-display-engine
- allwinner,pipelines: list of phandle to the display engine
frontends available.
......
......@@ -17,6 +17,18 @@ Optional properties:
the lcd controller.
- max-pixelclock: The maximum pixel clock that can be supported
by the lcd controller in KHz.
- blue-and-red-wiring: Recognized values "straight" or "crossed".
This property deals with the LCDC revision 2 (found on AM335x)
color errata [1].
- "straight" indicates normal wiring that supports RGB565,
BGR888, and XBGR8888 color formats.
- "crossed" indicates wiring that has blue and red wires
crossed. This setup supports BGR565, RGB888 and XRGB8888
formats.
- If the property is not present or its value is not recognized
the legacy mode is assumed. This configuration supports RGB565,
RGB888 and XRGB8888 formats. However, depending on wiring, the red
and blue colors are swapped in either 16 or 24-bit color modes.
Optional nodes:
......@@ -24,6 +36,18 @@ Optional nodes:
binding follows Documentation/devicetree/bindings/graph.txt and
suppors a single port with a single endpoint.
- See also Documentation/devicetree/bindings/display/tilcdc/panel.txt and
Documentation/devicetree/bindings/display/tilcdc/tfp410.txt for connecting
tfp410 DVI encoder or lcd panel to lcdc
[1] There is an errata about AM335x color wiring. For 16-bit color mode
the wires work as they should (LCD_DATA[0:4] is for Blue[3:7]),
but for 24 bit color modes the wiring of blue and red components is
crossed and LCD_DATA[0:4] is for Red[3:7] and LCD_DATA[11:15] is
for Blue[3-7]. For more details see section 3.1.1 in AM335x
Silicon Errata:
http://www.ti.com/general/docs/lit/getliterature.tsp?baseLiteratureNumber=sprz360
Example:
fb: fb@4830e000 {
......@@ -33,6 +57,8 @@ Example:
interrupts = <36>;
ti,hwmods = "lcdc";
blue-and-red-wiring = "crossed";
port {
lcdc_0: endpoint@0 {
remote-endpoint = <&hdmi_0>;
......
......@@ -53,9 +53,12 @@ u32 driver_features;
DRIVER_USE_AGP
Driver uses AGP interface, the DRM core will manage AGP resources.
DRIVER_REQUIRE_AGP
Driver needs AGP interface to function. AGP initialization failure
will become a fatal error.
DRIVER_LEGACY
Denote a legacy driver using shadow attach. Don't use.
DRIVER_KMS_LEGACY_CONTEXT
Used only by nouveau for backwards compatibility with existing userspace.
Don't use.
DRIVER_PCI_DMA
Driver is capable of PCI DMA, mapping of PCI DMA buffers to
......
......@@ -2,38 +2,45 @@
Mode Setting Helper Functions
=============================
The plane, CRTC, encoder and connector functions provided by the drivers
implement the DRM API. They're called by the DRM core and ioctl handlers
to handle device state changes and configuration request. As
implementing those functions often requires logic not specific to
drivers, mid-layer helper functions are available to avoid duplicating
boilerplate code.
The DRM core contains one mid-layer implementation. The mid-layer
provides implementations of several plane, CRTC, encoder and connector
functions (called from the top of the mid-layer) that pre-process
requests and call lower-level functions provided by the driver (at the
bottom of the mid-layer). For instance, the
:c:func:`drm_crtc_helper_set_config()` function can be used to
fill the :c:type:`struct drm_crtc_funcs <drm_crtc_funcs>`
set_config field. When called, it will split the set_config operation
in smaller, simpler operations and call the driver to handle them.
To use the mid-layer, drivers call
:c:func:`drm_crtc_helper_add()`,
:c:func:`drm_encoder_helper_add()` and
:c:func:`drm_connector_helper_add()` functions to install their
mid-layer bottom operations handlers, and fill the :c:type:`struct
drm_crtc_funcs <drm_crtc_funcs>`, :c:type:`struct
drm_encoder_funcs <drm_encoder_funcs>` and :c:type:`struct
drm_connector_funcs <drm_connector_funcs>` structures with
pointers to the mid-layer top API functions. Installing the mid-layer
bottom operation handlers is best done right after registering the
corresponding KMS object.
The mid-layer is not split between CRTC, encoder and connector
operations. To use it, a driver must provide bottom functions for all of
the three KMS entities.
The DRM subsystem aims for a strong separation between core code and helper
libraries. Core code takes care of general setup and teardown and decoding
userspace requests to kernel internal objects. Everything else is handled by a
large set of helper libraries, which can be combined freely to pick and choose
for each driver what fits, and avoid shared code where special behaviour is
needed.
This distinction between core code and helpers is especially strong in the
modesetting code, where there's a shared userspace ABI for all drivers. This is
in contrast to the render side, where pretty much everything (with very few
exceptions) can be considered optional helper code.
There are a few areas these helpers can grouped into:
* Helpers to implement modesetting. The important ones here are the atomic
helpers. Old drivers still often use the legacy CRTC helpers. They both share
the same set of common helper vtables. For really simple drivers (anything
that would have been a great fit in the deprecated fbdev subsystem) there's
also the simple display pipe helpers.
* There's a big pile of helpers for handling outputs. First the generic bridge
helpers for handling encoder and transcoder IP blocks. Second the panel helpers
for handling panel-related information and logic. Plus then a big set of
helpers for the various sink standards (DisplayPort, HDMI, MIPI DSI). Finally
there's also generic helpers for handling output probing, and for dealing with
EDIDs.
* The last group of helpers concerns itself with the frontend side of a display
pipeline: Planes, handling rectangles for visibility checking and scissoring,
flip queues and assorted bits.
Modeset Helper Reference for Common Vtables
===========================================
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:internal:
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:doc: overview
Atomic Modeset Helper Functions Reference
=========================================
......@@ -62,33 +69,27 @@ Atomic State Reset and Initialization
.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
:export:
Modeset Helper Reference for Common Vtables
===========================================
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:internal:
.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
:doc: overview
Legacy CRTC/Modeset Helper Functions Reference
==============================================
.. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
:export:
:doc: overview
.. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
:doc: overview
:export:
Output Probing Helper Functions Reference
=========================================
Simple KMS Helper Reference
===========================
.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
:doc: output probing helper overview
.. kernel-doc:: include/drm/drm_simple_kms_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
:doc: overview
fbdev Helper Functions Reference
================================
......@@ -110,6 +111,43 @@ Framebuffer CMA Helper Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c
:export:
Bridges
=======
Overview
--------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: overview
Default bridge callback sequence
--------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: bridge callbacks
Bridge Helper Reference
-------------------------
.. kernel-doc:: include/drm/drm_bridge.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:export:
Panel Helper Reference
======================
.. kernel-doc:: include/drm/drm_panel.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:doc: drm panel
Display Port Helper Functions Reference
=======================================
......@@ -158,9 +196,21 @@ MIPI DSI Helper Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_mipi_dsi.c
:export:
Output Probing Helper Functions Reference
=========================================
.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
:doc: output probing helper overview
.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
:export:
EDID Helper Functions Reference
===============================
.. kernel-doc:: include/drm/drm_edid.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_edid.c
:export:
......@@ -176,18 +226,6 @@ Rectangle Utilities Reference
.. kernel-doc:: drivers/gpu/drm/drm_rect.c
:export:
Flip-work Helper Reference
==========================
.. kernel-doc:: include/drm/drm_flip_work.h
:doc: flip utils
.. kernel-doc:: include/drm/drm_flip_work.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_flip_work.c
:export:
HDMI Infoframes Helper Reference
================================
......@@ -202,59 +240,40 @@ libraries and hence is also included here.
.. kernel-doc:: drivers/video/hdmi.c
:export:
Plane Helper Reference
======================
.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
:doc: overview
Flip-work Helper Reference
==========================
Tile group
----------
.. kernel-doc:: include/drm/drm_flip_work.h
:doc: flip utils
.. kernel-doc:: drivers/gpu/drm/drm_crtc.c
:doc: Tile group
.. kernel-doc:: include/drm/drm_flip_work.h
:internal:
Bridges
=======
.. kernel-doc:: drivers/gpu/drm/drm_flip_work.c
:export:
Overview
--------
Plane Helper Reference
======================
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
:doc: overview
Default bridge callback sequence
--------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: bridge callbacks
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
:export:
Panel Helper Reference
======================
.. kernel-doc:: include/drm/drm_panel.h
:internal:
Tile group
==========
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:export:
# FIXME: This should probably be moved into a property documentation section
.. kernel-doc:: drivers/gpu/drm/drm_panel.c
:doc: drm panel
.. kernel-doc:: drivers/gpu/drm/drm_crtc.c
:doc: Tile group
Simple KMS Helper Reference
===========================
Auxiliary Modeset Helpers
=========================
.. kernel-doc:: include/drm/drm_simple_kms_helper.h
:internal:
.. kernel-doc:: drivers/gpu/drm/drm_modeset_helper.c
:doc: aux kms helpers
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
.. kernel-doc:: drivers/gpu/drm/drm_modeset_helper.c
:export:
.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
:doc: overview
此差异已折叠。
......@@ -26,12 +26,12 @@ TTM, but has no video RAM management capabilities and is thus limited to
UMA devices.
The Translation Table Manager (TTM)
-----------------------------------
===================================
TTM design background and information belongs here.
TTM initialization
~~~~~~~~~~~~~~~~~~
------------------
**Warning**
......@@ -77,7 +77,7 @@ object, ttm_global_item_ref() is used to create an initial reference
count for the TTM, which will call your initialization function.
The Graphics Execution Manager (GEM)
------------------------------------
====================================
The GEM design approach has resulted in a memory manager that doesn't
provide full coverage of all (or even all common) use cases in its
......@@ -114,7 +114,7 @@ read & write, mapping, and domain ownership transfers are left to
driver-specific ioctls.
GEM Initialization
~~~~~~~~~~~~~~~~~~
------------------
Drivers that use GEM must set the DRIVER_GEM bit in the struct
:c:type:`struct drm_driver <drm_driver>` driver_features
......@@ -132,7 +132,7 @@ typically not managed by GEM, and must be initialized separately into
its own DRM MM object.
GEM Objects Creation
~~~~~~~~~~~~~~~~~~~~
--------------------
GEM splits creation of GEM objects and allocation of the memory that
backs them in two distinct operations.
......@@ -173,7 +173,7 @@ a call to :c:func:`drm_gem_private_object_init()` instead of
must be managed by drivers.
GEM Objects Lifetime
~~~~~~~~~~~~~~~~~~~~
--------------------
All GEM objects are reference-counted by the GEM core. References can be
acquired and release by :c:func:`calling
......@@ -196,7 +196,7 @@ resources created by the GEM core, which need to be released with
:c:func:`drm_gem_object_release()`.
GEM Objects Naming
~~~~~~~~~~~~~~~~~~
------------------
Communication between userspace and the kernel refers to GEM objects
using local handles, global names or, more recently, file descriptors.
......@@ -245,7 +245,7 @@ Furthermore PRIME also allows cross-device buffer sharing since it is
based on dma-bufs.
GEM Objects Mapping
~~~~~~~~~~~~~~~~~~~
-------------------
Because mapping operations are fairly heavyweight GEM favours
read/write-like access to buffers, implemented through driver-specific
......@@ -304,7 +304,7 @@ Drivers that want to map the GEM object upfront instead of handling page
faults can implement their own mmap file operation handler.
Memory Coherency
~~~~~~~~~~~~~~~~
----------------
When mapped to the device or used in a command buffer, backing pages for
an object are flushed to memory and marked write combined so as to be
......@@ -320,7 +320,7 @@ blocks the client and waits for rendering to complete before performing
any necessary flushing operations).
Command Execution
~~~~~~~~~~~~~~~~~
-----------------
Perhaps the most important GEM function for GPU devices is providing a
command execution interface to clients. Client programs construct
......@@ -348,8 +348,20 @@ GEM Function Reference
.. kernel-doc:: include/drm/drm_gem.h
:internal:
GEM CMA Helper Functions Reference
----------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
:doc: cma helpers
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
:export:
.. kernel-doc:: include/drm/drm_gem_cma_helper.h
:internal:
VMA Offset Manager
------------------
==================
.. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
:doc: vma offset manager
......@@ -361,14 +373,14 @@ VMA Offset Manager
:internal:
PRIME Buffer Sharing
--------------------
====================
PRIME is the cross device buffer sharing framework in drm, originally
created for the OPTIMUS range of multi-gpu platforms. To userspace PRIME
buffers are dma-buf based file descriptors.
Overview and Driver Interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------------------------
Similar to GEM global names, PRIME file descriptors are also used to
share buffer objects across processes. They offer additional security:
......@@ -406,7 +418,7 @@ struct drm_gem_object \*obj, int flags); struct drm_gem_object \*
support PRIME.
PRIME Helper Functions
~~~~~~~~~~~~~~~~~~~~~~
----------------------
.. kernel-doc:: drivers/gpu/drm/drm_prime.c
:doc: PRIME Helpers
......@@ -418,16 +430,16 @@ PRIME Function References
:export:
DRM MM Range Allocator
----------------------
======================
Overview
~~~~~~~~
--------
.. kernel-doc:: drivers/gpu/drm/drm_mm.c
:doc: Overview
LRU Scan/Eviction Support
~~~~~~~~~~~~~~~~~~~~~~~~~
-------------------------
.. kernel-doc:: drivers/gpu/drm/drm_mm.c
:doc: lru scan roaster
......@@ -440,15 +452,3 @@ DRM MM Range Allocator Function References
.. kernel-doc:: include/drm/drm_mm.h
:internal:
CMA Helper Functions Reference
------------------------------
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
:doc: cma helpers
.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
:export:
.. kernel-doc:: include/drm/drm_gem_cma_helper.h
:internal:
......@@ -33,6 +33,76 @@ Primary Nodes, DRM Master and Authentication
.. kernel-doc:: include/drm/drm_auth.h
:internal:
Open-Source Userspace Requirements
==================================
The DRM subsystem has stricter requirements than most other kernel subsystems on
what the userspace side for new uAPI needs to look like. This section here
explains what exactly those requirements are, and why they exist.
The short summary is that any addition of DRM uAPI requires corresponding
open-sourced userspace patches, and those patches must be reviewed and ready for
merging into a suitable and canonical upstream project.
GFX devices (both display and render/GPU side) are really complex bits of
hardware, with userspace and kernel by necessity having to work together really
closely. The interfaces, for rendering and modesetting, must be extremely wide
and flexible, and therefore it is almost always impossible to precisely define
them for every possible corner case. This in turn makes it really practically
infeasible to differentiate between behaviour that's required by userspace, and
which must not be changed to avoid regressions, and behaviour which is only an
accidental artifact of the current implementation.
Without access to the full source code of all userspace users that means it
becomes impossible to change the implementation details, since userspace could
depend upon the accidental behaviour of the current implementation in minute
details. And debugging such regressions without access to source code is pretty
much impossible. As a consequence this means:
- The Linux kernel's "no regression" policy holds in practice only for
open-source userspace of the DRM subsystem. DRM developers are perfectly fine
if closed-source blob drivers in userspace use the same uAPI as the open
drivers, but they must do so in the exact same way as the open drivers.
Creative (ab)use of the interfaces will, and in the past routinely has, lead
to breakage.
- Any new userspace interface must have an open-source implementation as
demonstration vehicle.
The other reason for requiring open-source userspace is uAPI review. Since the
kernel and userspace parts of a GFX stack must work together so closely, code
review can only assess whether a new interface achieves its goals by looking at
both sides. Making sure that the interface indeed covers the use-case fully
leads to a few additional requirements:
- The open-source userspace must not be a toy/test application, but the real
thing. Specifically it needs to handle all the usual error and corner cases.
These are often the places where new uAPI falls apart and hence essential to
assess the fitness of a proposed interface.
- The userspace side must be fully reviewed and tested to the standards of that
userspace project. For e.g. mesa this means piglit testcases and review on the
mailing list. This is again to ensure that the new interface actually gets the
job done.
- The userspace patches must be against the canonical upstream, not some vendor
fork. This is to make sure that no one cheats on the review and testing
requirements by doing a quick fork.
- The kernel patch can only be merged after all the above requirements are met,
but it **must** be merged **before** the userspace patches land. uAPI always flows
from the kernel, doing things the other way round risks divergence of the uAPI
definitions and header files.
These are fairly steep requirements, but have grown out from years of shared
pain and experience with uAPI added hastily, and almost always regretted about
just as fast. GFX devices change really fast, requiring a paradigm shift and
entire new set of uAPI interfaces every few years at least. Together with the
Linux kernel's guarantee to keep existing userspace running for 10+ years this
is already rather painful for the DRM subsystem, with multiple different uAPIs
for the same thing co-existing. If we add a few more complete mistakes into the
mix every year it would be entirely unmanageable.
Render nodes
============
......@@ -86,6 +156,43 @@ other hand, a driver requires shared state between clients which is
visible to user-space and accessible beyond open-file boundaries, they
cannot support render nodes.
Validating changes with IGT
===========================
There's a collection of tests that aims to cover the whole functionality of
DRM drivers and that can be used to check that changes to DRM drivers or the
core don't regress existing functionality. This test suite is called IGT and
its code can be found in https://cgit.freedesktop.org/drm/igt-gpu-tools/.
To build IGT, start by installing its build dependencies. In Debian-based
systems::
# apt-get build-dep intel-gpu-tools
And in Fedora-based systems::
# dnf builddep intel-gpu-tools
Then clone the repository::
$ git clone git://anongit.freedesktop.org/drm/igt-gpu-tools
Configure the build system and start the build::
$ cd igt-gpu-tools && ./autogen.sh && make -j6
Download the piglit dependency::
$ ./scripts/run-tests.sh -d
And run the tests::
$ ./scripts/run-tests.sh -t kms -t core -s
run-tests.sh is a wrapper around piglit that will execute the tests matching
the -t options. A report in HTML format will be available in
./results/html/index.html. Results can be compared with piglit.
VBlank event handling
=====================
......
......@@ -70,6 +70,9 @@ Frontbuffer Tracking
.. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.c
:doc: frontbuffer tracking
.. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.h
:internal:
.. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.c
:internal:
......
......@@ -12,6 +12,7 @@ Linux GPU Driver Developer's Guide
drm-uapi
i915
vga-switcheroo
vgaarbiter
.. only:: subproject
......
Owner Module/Drivers,Group,Property Name,Type,Property Values,Object attached,Description/Restrictions
DRM,Generic,“rotation”,BITMASK,"{ 0, ""rotate-0"" }, { 1, ""rotate-90"" }, { 2, ""rotate-180"" }, { 3, ""rotate-270"" }, { 4, ""reflect-x"" }, { 5, ""reflect-y"" }","CRTC, Plane",rotate-(degrees) rotates the image by the specified amount in degrees in counter clockwise direction. reflect-x and reflect-y reflects the image along the specified axis prior to rotation
,,“scaling mode”,ENUM,"{ ""None"", ""Full"", ""Center"", ""Full aspect"" }",Connector,"Supported by: amdgpu, gma500, i915, nouveau and radeon."
,Connector,“EDID”,BLOB | IMMUTABLE,0,Connector,Contains id of edid blob ptr object.
,,“DPMS”,ENUM,"{ “On”, “Standby”, “Suspend”, “Off” }",Connector,Contains DPMS operation mode value.
,,“PATH”,BLOB | IMMUTABLE,0,Connector,Contains topology path to a connector.
,,“TILE”,BLOB | IMMUTABLE,0,Connector,Contains tiling information for a connector.
,,“CRTC_ID”,OBJECT,DRM_MODE_OBJECT_CRTC,Connector,CRTC that connector is attached to (atomic)
,Plane,“type”,ENUM | IMMUTABLE,"{ ""Overlay"", ""Primary"", ""Cursor"" }",Plane,Plane type
,,“SRC_X”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source x coordinate in 16.16 fixed point (atomic)
,,“SRC_Y”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source y coordinate in 16.16 fixed point (atomic)
,,“SRC_W”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source width in 16.16 fixed point (atomic)
,,“SRC_H”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source height in 16.16 fixed point (atomic)
,,“CRTC_X”,SIGNED_RANGE,"Min=INT_MIN, Max=INT_MAX",Plane,Scanout CRTC (destination) x coordinate (atomic)
,,“CRTC_Y”,SIGNED_RANGE,"Min=INT_MIN, Max=INT_MAX",Plane,Scanout CRTC (destination) y coordinate (atomic)
,,“CRTC_W”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout CRTC (destination) width (atomic)
,,“CRTC_H”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout CRTC (destination) height (atomic)
,,“FB_ID”,OBJECT,DRM_MODE_OBJECT_FB,Plane,Scanout framebuffer (atomic)
,,“CRTC_ID”,OBJECT,DRM_MODE_OBJECT_CRTC,Plane,CRTC that plane is attached to (atomic)
,,“zpos”,RANGE,"Min=0, Max=UINT_MAX","Plane,Z-order of the plane.Planes with higher Z-order values are displayed on top, planes with identical Z-order values are display in an undefined order"
,DVI-I,“subconnector”,ENUM,"{ “Unknown”, “DVI-D”, “DVI-A” }",Connector,TBD
,,“select subconnector”,ENUM,"{ “Automatic”, “DVI-D”, “DVI-A” }",Connector,TBD
,TV,“subconnector”,ENUM,"{ ""Unknown"", ""Composite"", ""SVIDEO"", ""Component"", ""SCART"" }",Connector,TBD
......@@ -36,12 +23,6 @@ DRM,Generic,“rotation”,BITMASK,"{ 0, ""rotate-0"" }, { 1, ""rotate-90"" }, {
,Virtual GPU,“suggested X”,RANGE,"Min=0, Max=0xffffffff",Connector,property to suggest an X offset for a connector
,,“suggested Y”,RANGE,"Min=0, Max=0xffffffff",Connector,property to suggest an Y offset for a connector
,Optional,"""aspect ratio""",ENUM,"{ ""None"", ""4:3"", ""16:9"" }",Connector,TDB
,,“dirty”,ENUM | IMMUTABLE,"{ ""Off"", ""On"", ""Annotate"" }",Connector,TBD
,,“DEGAMMA_LUT”,BLOB,0,CRTC,DRM property to set the degamma lookup table (LUT) mapping pixel data from the framebuffer before it is given to the transformation matrix. The data is an interpreted as an array of struct drm_color_lut elements. Hardware might choose not to use the full precision of the LUT elements nor use all the elements of the LUT (for example the hardware might choose to interpolate between LUT[0] and LUT[4]).
,,“DEGAMMA_LUT_SIZE”,RANGE | IMMUTABLE,"Min=0, Max=UINT_MAX",CRTC,DRM property to gives the size of the lookup table to be set on the DEGAMMA_LUT property (the size depends on the underlying hardware).
,,“CTM”,BLOB,0,CRTC,DRM property to set the current transformation matrix (CTM) apply to pixel data after the lookup through the degamma LUT and before the lookup through the gamma LUT. The data is an interpreted as a struct drm_color_ctm.
,,“GAMMA_LUT”,BLOB,0,CRTC,DRM property to set the gamma lookup table (LUT) mapping pixel data after to the transformation matrix to data sent to the connector. The data is an interpreted as an array of struct drm_color_lut elements. Hardware might choose not to use the full precision of the LUT elements nor use all the elements of the LUT (for example the hardware might choose to interpolate between LUT[0] and LUT[4]).
,,“GAMMA_LUT_SIZE”,RANGE | IMMUTABLE,"Min=0, Max=UINT_MAX",CRTC,DRM property to gives the size of the lookup table to be set on the GAMMA_LUT property (the size depends on the underlying hardware).
i915,Generic,"""Broadcast RGB""",ENUM,"{ ""Automatic"", ""Full"", ""Limited 16:235"" }",Connector,"When this property is set to Limited 16:235 and CTM is set, the hardware will be programmed with the result of the multiplication of CTM by the limited range matrix to ensure the pixels normaly in the range 0..1.0 are remapped to the range 16/255..235/255."
,,“audio”,ENUM,"{ ""force-dvi"", ""off"", ""auto"", ""on"" }",Connector,TBD
,SDVO-TV,“mode”,ENUM,"{ ""NTSC_M"", ""NTSC_J"", ""NTSC_443"", ""PAL_B"" } etc.",Connector,TBD
......@@ -95,7 +76,6 @@ armada,CRTC,"""CSC_YUV""",ENUM,"{ ""Auto"" , ""CCIR601"", ""CCIR709"" }",CRTC,TB
,,"""contrast""",RANGE,"Min=0, Max=0x7fff",Plane,TBD
,,"""saturation""",RANGE,"Min=0, Max=0x7fff",Plane,TBD
exynos,CRTC,“mode”,ENUM,"{ ""normal"", ""blank"" }",CRTC,TBD
,Overlay,“zpos”,RANGE,"Min=0, Max=MAX_PLANE-1",Plane,TBD
i2c/ch7006_drv,Generic,“scale”,RANGE,"Min=0, Max=2",Connector,TBD
,TV,“mode”,ENUM,"{ ""PAL"", ""PAL-M"",""PAL-N""}, ”PAL-Nc"" , ""PAL-60"", ""NTSC-M"", ""NTSC-J"" }",Connector,TBD
nouveau,NV10 Overlay,"""colorkey""",RANGE,"Min=0, Max=0x01ffffff",Plane,TBD
......@@ -126,4 +106,3 @@ radeon,DVI-I,“coherent”,RANGE,"Min=0, Max=1",Connector,TBD
,FMT Dithering,“dither”,ENUM,"{ ""off"", ""on"" }",Connector,TBD
rcar-du,Generic,"""alpha""",RANGE,"Min=0, Max=255",Plane,TBD
,,"""colorkey""",RANGE,"Min=0, Max=0x01ffffff",Plane,TBD
,,"""zpos""",RANGE,"Min=1, Max=7",Plane,TBD
===========
VGA Arbiter
===========
......@@ -19,21 +19,8 @@ control bus resources. Therefore an arbitration scheme outside of the X server
is needed to control the sharing of these resources. This document introduces
the operation of the VGA arbiter implemented for the Linux kernel.
----------------------------------------------------------------------------
I. Details and Theory of Operation
I.1 vgaarb
I.2 libpciaccess
I.3 xf86VGAArbiter (X server implementation)
II. Credits
III.References
I. Details and Theory of Operation
==================================
I.1 vgaarb
----------
vgaarb kernel/userspace ABI
---------------------------
The vgaarb is a module of the Linux Kernel. When it is initially loaded, it
scans all PCI devices and adds the VGA ones inside the arbitration. The
......@@ -44,42 +31,52 @@ explicitly tell it by calling vga_set_legacy_decoding().
The kernel exports a char device interface (/dev/vga_arbiter) to the clients,
which has the following semantics:
open : open user instance of the arbiter. By default, it's attached to
the default VGA device of the system.
close : close user instance. Release locks made by the user
read : return a string indicating the status of the target like:
"<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)"
An IO state string is of the form {io,mem,io+mem,none}, mc and
ic are respectively mem and io lock counts (for debugging/
diagnostic only). "decodes" indicate what the card currently
decodes, "owns" indicates what is currently enabled on it, and
"locks" indicates what is locked by this card. If the card is
unplugged, we get "invalid" then for card_ID and an -ENODEV
error is returned for any command until a new card is targeted.
write : write a command to the arbiter. List of commands:
target <card_ID> : switch target to card <card_ID> (see below)
lock <io_state> : acquires locks on target ("none" is an invalid io_state)
trylock <io_state> : non-blocking acquire locks on target (returns EBUSY if
unsuccessful)
unlock <io_state> : release locks on target
unlock all : release all locks on target held by this user (not
implemented yet)
decodes <io_state> : set the legacy decoding attributes for the card
poll : event if something changes on any card (not just the
target)
card_ID is of the form "PCI:domain:bus:dev.fn". It can be set to "default"
to go back to the system default card (TODO: not implemented yet). Currently,
only PCI is supported as a prefix, but the userland API may support other bus
types in the future, even if the current kernel implementation doesn't.
open
Opens a user instance of the arbiter. By default, it's attached to the
default VGA device of the system.
close
Close a user instance. Release locks made by the user
read
Return a string indicating the status of the target like:
"<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)"
An IO state string is of the form {io,mem,io+mem,none}, mc and
ic are respectively mem and io lock counts (for debugging/
diagnostic only). "decodes" indicate what the card currently
decodes, "owns" indicates what is currently enabled on it, and
"locks" indicates what is locked by this card. If the card is
unplugged, we get "invalid" then for card_ID and an -ENODEV
error is returned for any command until a new card is targeted.
write
Write a command to the arbiter. List of commands:
target <card_ID>
switch target to card <card_ID> (see below)
lock <io_state>
acquires locks on target ("none" is an invalid io_state)
trylock <io_state>
non-blocking acquire locks on target (returns EBUSY if
unsuccessful)
unlock <io_state>
release locks on target
unlock all
release all locks on target held by this user (not implemented
yet)
decodes <io_state>
set the legacy decoding attributes for the card
poll
event if something changes on any card (not just the target)
card_ID is of the form "PCI:domain:bus:dev.fn". It can be set to "default"
to go back to the system default card (TODO: not implemented yet). Currently,
only PCI is supported as a prefix, but the userland API may support other bus
types in the future, even if the current kernel implementation doesn't.
Note about locks:
......@@ -97,29 +94,35 @@ in the arbiter.
There is also an in-kernel API of the arbiter in case DRM, vgacon, or other
drivers want to use it.
In-kernel interface
-------------------
.. kernel-doc:: include/linux/vgaarb.h
:internal:
I.2 libpciaccess
----------------
.. kernel-doc:: drivers/gpu/vga/vgaarb.c
:export:
libpciaccess
------------
To use the vga arbiter char device it was implemented an API inside the
libpciaccess library. One field was added to struct pci_device (each device
on the system):
on the system)::
/* the type of resource decoded by the device */
int vgaarb_rsrc;
Besides it, in pci_system were added:
Besides it, in pci_system were added::
int vgaarb_fd;
int vga_count;
struct pci_device *vga_target;
struct pci_device *vga_default_dev;
The vga_count is used to track how many cards are being arbitrated, so for
instance, if there is only one card, then it can completely escape arbitration.
These functions below acquire VGA resources for the given card and mark those
resources as locked. If the resources requested are "normal" (and not legacy)
resources, the arbiter will first check whether the card is doing legacy
......@@ -136,44 +139,44 @@ VGA memory and IO afaik). If the card already owns the resources, the function
succeeds. vga_arb_trylock() will return (-EBUSY) instead of blocking. Nested
calls are supported (a per-resource counter is maintained).
Set the target device of this client. ::
Set the target device of this client.
int pci_device_vgaarb_set_target (struct pci_device *dev);
For instance, in x86 if two devices on the same bus want to lock different
resources, both will succeed (lock). If devices are in different buses and
trying to lock different resources, only the first who tried succeeds.
trying to lock different resources, only the first who tried succeeds. ::
int pci_device_vgaarb_lock (void);
int pci_device_vgaarb_trylock (void);
Unlock resources of device.
Unlock resources of device. ::
int pci_device_vgaarb_unlock (void);
Indicates to the arbiter if the card decodes legacy VGA IOs, legacy VGA
Memory, both, or none. All cards default to both, the card driver (fbdev for
example) should tell the arbiter if it has disabled legacy decoding, so the
card can be left out of the arbitration process (and can be safe to take
interrupts at any time.
interrupts at any time. ::
int pci_device_vgaarb_decodes (int new_vgaarb_rsrc);
Connects to the arbiter device, allocates the struct
int pci_device_vgaarb_init (void);
Connects to the arbiter device, allocates the struct ::
Close the connection
void pci_device_vgaarb_fini (void);
int pci_device_vgaarb_init (void);
Close the connection ::
I.3 xf86VGAArbiter (X server implementation)
--------------------------------------------
void pci_device_vgaarb_fini (void);
(TODO)
xf86VGAArbiter (X server implementation)
----------------------------------------
X server basically wraps all the functions that touch VGA registers somehow.
II. Credits
===========
References
----------
Benjamin Herrenschmidt (IBM?) started this work when he discussed such design
with the Xorg community in 2005 [1, 2]. In the end of 2007, Paulo Zanoni and
......@@ -182,11 +185,7 @@ enhancing the kernel code to adapt as a kernel module and also did the
implementation of the user space side [3]. Now (2009) Tiago Vignatti and Dave
Airlie finally put this work in shape and queued to Jesse Barnes' PCI tree.
III. References
==============
[0] http://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347
[1] http://lists.freedesktop.org/archives/xorg/2005-March/006663.html
[2] http://lists.freedesktop.org/archives/xorg/2005-March/006745.html
[3] http://lists.freedesktop.org/archives/xorg/2007-October/029507.html
0) http://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347
1) http://lists.freedesktop.org/archives/xorg/2005-March/006663.html
2) http://lists.freedesktop.org/archives/xorg/2005-March/006745.html
3) http://lists.freedesktop.org/archives/xorg/2007-October/029507.html
......@@ -64,6 +64,20 @@ The sync_file fd now can be sent to userspace.
If the creation process fail, or the sync_file needs to be released by any
other reason fput(sync_file->file) should be used.
Receiving Sync Files from Userspace
-----------------------------------
When userspace needs to send an in-fence to the driver it passes file descriptor
of the Sync File to the kernel. The kernel can then retrieve the fences
from it.
Interface:
struct fence *sync_file_get_fence(int fd);
The returned reference is owned by the caller and must be disposed of
afterwards using fence_put(). In case of error, a NULL is returned instead.
References:
[1] struct sync_file in include/linux/sync_file.h
[2] All interfaces mentioned above defined in include/linux/sync_file.h
......@@ -4122,6 +4122,14 @@ S: Orphan / Obsolete
F: drivers/gpu/drm/i810/
F: include/uapi/drm/i810_drm.h
DRM DRIVERS FOR MEDIATEK
M: CK Hu <ck.hu@mediatek.com>
M: Philipp Zabel <p.zabel@pengutronix.de>
L: dri-devel@lists.freedesktop.org
S: Supported
F: drivers/gpu/drm/mediatek/
F: Documentation/devicetree/bindings/display/mediatek/
DRM DRIVER FOR MSM ADRENO GPU
M: Rob Clark <robdclark@gmail.com>
L: linux-arm-msm@vger.kernel.org
......
......@@ -9,6 +9,7 @@
#include "am33xx.dtsi"
#include "am335x-bone-common.dtsi"
#include <dt-bindings/display/tda998x.h>
/ {
model = "TI AM335x BeagleBone Black";
......@@ -64,6 +65,16 @@
AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */
>;
};
mcasp0_pins: mcasp0_pins {
pinctrl-single,pins = <
AM33XX_IOPAD(0x9ac, PIN_INPUT_PULLUP | MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */
AM33XX_IOPAD(0x99c, PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/
AM33XX_IOPAD(0x994, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mcasp0_fsx.mcasp0_fsx */
AM33XX_IOPAD(0x990, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mcasp0_aclkx.mcasp0_aclkx */
AM33XX_IOPAD(0x86c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a11.GPIO1_27 */
>;
};
};
&lcdc {
......@@ -76,16 +87,22 @@
};
&i2c0 {
tda19988 {
tda19988: tda19988 {
compatible = "nxp,tda998x";
reg = <0x70>;
pinctrl-names = "default", "off";
pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
port {
hdmi_0: endpoint@0 {
remote-endpoint = <&lcdc_0>;
#sound-dai-cells = <0>;
audio-ports = < TDA998x_I2S 0x03>;
ports {
port@0 {
hdmi_0: endpoint@0 {
remote-endpoint = <&lcdc_0>;
};
};
};
};
......@@ -94,3 +111,49 @@
&rtc {
system-power-controller;
};
&mcasp0 {
#sound-dai-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&mcasp0_pins>;
status = "okay";
op-mode = <0>; /* MCASP_IIS_MODE */
tdm-slots = <2>;
serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
0 0 1 0
>;
tx-num-evt = <32>;
rx-num-evt = <32>;
};
/ {
clk_mcasp0_fixed: clk_mcasp0_fixed {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24576000>;
};
clk_mcasp0: clk_mcasp0 {
#clock-cells = <0>;
compatible = "gpio-gate-clock";
clocks = <&clk_mcasp0_fixed>;
enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */
};
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "TI BeagleBone Black";
simple-audio-card,format = "i2s";
simple-audio-card,bitclock-master = <&dailink0_master>;
simple-audio-card,frame-master = <&dailink0_master>;
dailink0_master: simple-audio-card,cpu {
sound-dai = <&mcasp0>;
clocks = <&clk_mcasp0>;
};
simple-audio-card,codec {
sound-dai = <&tda19988>;
};
};
};
......@@ -317,16 +317,11 @@ static phys_addr_t __init i85x_stolen_base(int num, int slot, int func,
static phys_addr_t __init i865_stolen_base(int num, int slot, int func,
size_t stolen_size)
{
u16 toud;
u16 toud = 0;
/*
* FIXME is the graphics stolen memory region
* always at TOUD? Ie. is it always the last
* one to be allocated by the BIOS?
*/
toud = read_pci_config_16(0, 0, 0, I865_TOUD);
return (phys_addr_t)toud << 16;
return (phys_addr_t)(toud << 16) + i845_tseg_size();
}
static phys_addr_t __init gen3_stolen_base(int num, int slot, int func,
......@@ -512,8 +507,7 @@ static const struct pci_device_id intel_early_ids[] __initconst = {
INTEL_I915GM_IDS(&gen3_early_ops),
INTEL_I945G_IDS(&gen3_early_ops),
INTEL_I945GM_IDS(&gen3_early_ops),
INTEL_VLV_M_IDS(&gen6_early_ops),
INTEL_VLV_D_IDS(&gen6_early_ops),
INTEL_VLV_IDS(&gen6_early_ops),
INTEL_PINEVIEW_IDS(&gen3_early_ops),
INTEL_I965G_IDS(&gen3_early_ops),
INTEL_G33_IDS(&gen3_early_ops),
......@@ -526,10 +520,8 @@ static const struct pci_device_id intel_early_ids[] __initconst = {
INTEL_SNB_M_IDS(&gen6_early_ops),
INTEL_IVB_M_IDS(&gen6_early_ops),
INTEL_IVB_D_IDS(&gen6_early_ops),
INTEL_HSW_D_IDS(&gen6_early_ops),
INTEL_HSW_M_IDS(&gen6_early_ops),
INTEL_BDW_M_IDS(&gen8_early_ops),
INTEL_BDW_D_IDS(&gen8_early_ops),
INTEL_HSW_IDS(&gen6_early_ops),
INTEL_BDW_IDS(&gen8_early_ops),
INTEL_CHV_IDS(&chv_early_ops),
INTEL_SKL_IDS(&gen9_early_ops),
INTEL_BXT_IDS(&gen9_early_ops),
......
......@@ -845,6 +845,8 @@ void intel_gtt_insert_page(dma_addr_t addr,
unsigned int flags)
{
intel_private.driver->write_entry(addr, pg, flags);
if (intel_private.driver->chipset_flush)
intel_private.driver->chipset_flush();
}
EXPORT_SYMBOL(intel_gtt_insert_page);
......
......@@ -586,6 +586,22 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
}
EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
enum dma_data_direction direction)
{
bool write = (direction == DMA_BIDIRECTIONAL ||
direction == DMA_TO_DEVICE);
struct reservation_object *resv = dmabuf->resv;
long ret;
/* Wait on any implicit rendering fences */
ret = reservation_object_wait_timeout_rcu(resv, write, true,
MAX_SCHEDULE_TIMEOUT);
if (ret < 0)
return ret;
return 0;
}
/**
* dma_buf_begin_cpu_access - Must be called before accessing a dma_buf from the
......@@ -608,6 +624,13 @@ int dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
if (dmabuf->ops->begin_cpu_access)
ret = dmabuf->ops->begin_cpu_access(dmabuf, direction);
/* Ensure that all fences are waited upon - but we first allow
* the native handler the chance to do so more efficiently if it
* chooses. A double invocation here will be reasonably cheap no-op.
*/
if (ret == 0)
ret = __dma_buf_begin_cpu_access(dmabuf, direction);
return ret;
}
EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access);
......
......@@ -99,6 +99,7 @@ const struct fence_ops fence_array_ops = {
.wait = fence_default_wait,
.release = fence_array_release,
};
EXPORT_SYMBOL(fence_array_ops);
/**
* fence_array_create - Create a custom fence array
......@@ -106,14 +107,14 @@ const struct fence_ops fence_array_ops = {
* @fences: [in] array containing the fences
* @context: [in] fence context to use
* @seqno: [in] sequence number to use
* @signal_on_any [in] signal on any fence in the array
* @signal_on_any: [in] signal on any fence in the array
*
* Allocate a fence_array object and initialize the base fence with fence_init().
* In case of error it returns NULL.
*
* The caller should allocte the fences array with num_fences size
* The caller should allocate the fences array with num_fences size
* and fill it with the fences it wants to add to the object. Ownership of this
* array is take and fence_put() is used on each fence on release.
* array is taken and fence_put() is used on each fence on release.
*
* If @signal_on_any is true the fence array signals if any fence in the array
* signals, otherwise it signals when all fences in the array signal.
......
......@@ -205,7 +205,7 @@ reservation_object_add_shared_replace(struct reservation_object *obj,
* @fence: the shared fence to add
*
* Add a fence to a shared slot, obj->lock must be held, and
* reservation_object_reserve_shared_fence has been called.
* reservation_object_reserve_shared() has been called.
*/
void reservation_object_add_shared_fence(struct reservation_object *obj,
struct fence *fence)
......
......@@ -135,10 +135,16 @@ static void sync_print_sync_file(struct seq_file *s,
int i;
seq_printf(s, "[%p] %s: %s\n", sync_file, sync_file->name,
sync_status_str(atomic_read(&sync_file->status)));
sync_status_str(!fence_is_signaled(sync_file->fence)));
for (i = 0; i < sync_file->num_fences; ++i)
sync_print_fence(s, sync_file->cbs[i].fence, true);
if (fence_is_array(sync_file->fence)) {
struct fence_array *array = to_fence_array(sync_file->fence);
for (i = 0; i < array->num_fences; ++i)
sync_print_fence(s, array->fences[i], true);
} else {
sync_print_fence(s, sync_file->fence, true);
}
}
static int sync_debugfs_show(struct seq_file *s, void *unused)
......
......@@ -28,11 +28,11 @@
static const struct file_operations sync_file_fops;
static struct sync_file *sync_file_alloc(int size)
static struct sync_file *sync_file_alloc(void)
{
struct sync_file *sync_file;
sync_file = kzalloc(size, GFP_KERNEL);
sync_file = kzalloc(sizeof(*sync_file), GFP_KERNEL);
if (!sync_file)
return NULL;
......@@ -45,6 +45,8 @@ static struct sync_file *sync_file_alloc(int size)
init_waitqueue_head(&sync_file->wq);
INIT_LIST_HEAD(&sync_file->cb.node);
return sync_file;
err:
......@@ -54,14 +56,11 @@ static struct sync_file *sync_file_alloc(int size)
static void fence_check_cb_func(struct fence *f, struct fence_cb *cb)
{
struct sync_file_cb *check;
struct sync_file *sync_file;
check = container_of(cb, struct sync_file_cb, cb);
sync_file = check->sync_file;
sync_file = container_of(cb, struct sync_file, cb);
if (atomic_dec_and_test(&sync_file->status))
wake_up_all(&sync_file->wq);
wake_up_all(&sync_file->wq);
}
/**
......@@ -76,23 +75,17 @@ struct sync_file *sync_file_create(struct fence *fence)
{
struct sync_file *sync_file;
sync_file = sync_file_alloc(offsetof(struct sync_file, cbs[1]));
sync_file = sync_file_alloc();
if (!sync_file)
return NULL;
sync_file->num_fences = 1;
atomic_set(&sync_file->status, 1);
sync_file->fence = fence;
snprintf(sync_file->name, sizeof(sync_file->name), "%s-%s%llu-%d",
fence->ops->get_driver_name(fence),
fence->ops->get_timeline_name(fence), fence->context,
fence->seqno);
sync_file->cbs[0].fence = fence;
sync_file->cbs[0].sync_file = sync_file;
if (fence_add_callback(fence, &sync_file->cbs[0].cb,
fence_check_cb_func))
atomic_dec(&sync_file->status);
return sync_file;
}
EXPORT_SYMBOL(sync_file_create);
......@@ -121,14 +114,73 @@ static struct sync_file *sync_file_fdget(int fd)
return NULL;
}
static void sync_file_add_pt(struct sync_file *sync_file, int *i,
struct fence *fence)
/**
* sync_file_get_fence - get the fence related to the sync_file fd
* @fd: sync_file fd to get the fence from
*
* Ensures @fd references a valid sync_file and returns a fence that
* represents all fence in the sync_file. On error NULL is returned.
*/
struct fence *sync_file_get_fence(int fd)
{
struct sync_file *sync_file;
struct fence *fence;
sync_file = sync_file_fdget(fd);
if (!sync_file)
return NULL;
fence = fence_get(sync_file->fence);
fput(sync_file->file);
return fence;
}
EXPORT_SYMBOL(sync_file_get_fence);
static int sync_file_set_fence(struct sync_file *sync_file,
struct fence **fences, int num_fences)
{
struct fence_array *array;
/*
* The reference for the fences in the new sync_file and held
* in add_fence() during the merge procedure, so for num_fences == 1
* we already own a new reference to the fence. For num_fence > 1
* we own the reference of the fence_array creation.
*/
if (num_fences == 1) {
sync_file->fence = fences[0];
kfree(fences);
} else {
array = fence_array_create(num_fences, fences,
fence_context_alloc(1), 1, false);
if (!array)
return -ENOMEM;
sync_file->fence = &array->base;
}
return 0;
}
static struct fence **get_fences(struct sync_file *sync_file, int *num_fences)
{
if (fence_is_array(sync_file->fence)) {
struct fence_array *array = to_fence_array(sync_file->fence);
*num_fences = array->num_fences;
return array->fences;
}
*num_fences = 1;
return &sync_file->fence;
}
static void add_fence(struct fence **fences, int *i, struct fence *fence)
{
sync_file->cbs[*i].fence = fence;
sync_file->cbs[*i].sync_file = sync_file;
fences[*i] = fence;
if (!fence_add_callback(fence, &sync_file->cbs[*i].cb,
fence_check_cb_func)) {
if (!fence_is_signaled(fence)) {
fence_get(fence);
(*i)++;
}
......@@ -147,16 +199,24 @@ static void sync_file_add_pt(struct sync_file *sync_file, int *i,
static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
struct sync_file *b)
{
int num_fences = a->num_fences + b->num_fences;
struct sync_file *sync_file;
int i, i_a, i_b;
unsigned long size = offsetof(struct sync_file, cbs[num_fences]);
struct fence **fences, **nfences, **a_fences, **b_fences;
int i, i_a, i_b, num_fences, a_num_fences, b_num_fences;
sync_file = sync_file_alloc(size);
sync_file = sync_file_alloc();
if (!sync_file)
return NULL;
atomic_set(&sync_file->status, num_fences);
a_fences = get_fences(a, &a_num_fences);
b_fences = get_fences(b, &b_num_fences);
if (a_num_fences > INT_MAX - b_num_fences)
return NULL;
num_fences = a_num_fences + b_num_fences;
fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
if (!fences)
goto err;
/*
* Assume sync_file a and b are both ordered and have no
......@@ -165,55 +225,69 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
* If a sync_file can only be created with sync_file_merge
* and sync_file_create, this is a reasonable assumption.
*/
for (i = i_a = i_b = 0; i_a < a->num_fences && i_b < b->num_fences; ) {
struct fence *pt_a = a->cbs[i_a].fence;
struct fence *pt_b = b->cbs[i_b].fence;
for (i = i_a = i_b = 0; i_a < a_num_fences && i_b < b_num_fences; ) {
struct fence *pt_a = a_fences[i_a];
struct fence *pt_b = b_fences[i_b];
if (pt_a->context < pt_b->context) {
sync_file_add_pt(sync_file, &i, pt_a);
add_fence(fences, &i, pt_a);
i_a++;
} else if (pt_a->context > pt_b->context) {
sync_file_add_pt(sync_file, &i, pt_b);
add_fence(fences, &i, pt_b);
i_b++;
} else {
if (pt_a->seqno - pt_b->seqno <= INT_MAX)
sync_file_add_pt(sync_file, &i, pt_a);
add_fence(fences, &i, pt_a);
else
sync_file_add_pt(sync_file, &i, pt_b);
add_fence(fences, &i, pt_b);
i_a++;
i_b++;
}
}
for (; i_a < a->num_fences; i_a++)
sync_file_add_pt(sync_file, &i, a->cbs[i_a].fence);
for (; i_a < a_num_fences; i_a++)
add_fence(fences, &i, a_fences[i_a]);
for (; i_b < b_num_fences; i_b++)
add_fence(fences, &i, b_fences[i_b]);
for (; i_b < b->num_fences; i_b++)
sync_file_add_pt(sync_file, &i, b->cbs[i_b].fence);
if (i == 0)
fences[i++] = fence_get(a_fences[0]);
if (num_fences > i)
atomic_sub(num_fences - i, &sync_file->status);
sync_file->num_fences = i;
if (num_fences > i) {
nfences = krealloc(fences, i * sizeof(*fences),
GFP_KERNEL);
if (!nfences)
goto err;
fences = nfences;
}
if (sync_file_set_fence(sync_file, fences, i) < 0) {
kfree(fences);
goto err;
}
strlcpy(sync_file->name, name, sizeof(sync_file->name));
return sync_file;
err:
fput(sync_file->file);
return NULL;
}
static void sync_file_free(struct kref *kref)
{
struct sync_file *sync_file = container_of(kref, struct sync_file,
kref);
int i;
for (i = 0; i < sync_file->num_fences; ++i) {
fence_remove_callback(sync_file->cbs[i].fence,
&sync_file->cbs[i].cb);
fence_put(sync_file->cbs[i].fence);
}
if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
fence_remove_callback(sync_file->fence, &sync_file->cb);
fence_put(sync_file->fence);
kfree(sync_file);
}
......@@ -228,17 +302,17 @@ static int sync_file_release(struct inode *inode, struct file *file)
static unsigned int sync_file_poll(struct file *file, poll_table *wait)
{
struct sync_file *sync_file = file->private_data;
int status;
poll_wait(file, &sync_file->wq, wait);
status = atomic_read(&sync_file->status);
if (!poll_does_not_wait(wait) &&
!test_and_set_bit(POLL_ENABLED, &sync_file->fence->flags)) {
if (fence_add_callback(sync_file->fence, &sync_file->cb,
fence_check_cb_func) < 0)
wake_up_all(&sync_file->wq);
}
if (!status)
return POLLIN;
if (status < 0)
return POLLERR;
return 0;
return fence_is_signaled(sync_file->fence) ? POLLIN : 0;
}
static long sync_file_ioctl_merge(struct sync_file *sync_file,
......@@ -315,8 +389,9 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
{
struct sync_file_info info;
struct sync_fence_info *fence_info = NULL;
struct fence **fences;
__u32 size;
int ret, i;
int num_fences, ret, i;
if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
return -EFAULT;
......@@ -324,6 +399,8 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
if (info.flags || info.pad)
return -EINVAL;
fences = get_fences(sync_file, &num_fences);
/*
* Passing num_fences = 0 means that userspace doesn't want to
* retrieve any sync_fence_info. If num_fences = 0 we skip filling
......@@ -333,16 +410,16 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
if (!info.num_fences)
goto no_fences;
if (info.num_fences < sync_file->num_fences)
if (info.num_fences < num_fences)
return -EINVAL;
size = sync_file->num_fences * sizeof(*fence_info);
size = num_fences * sizeof(*fence_info);
fence_info = kzalloc(size, GFP_KERNEL);
if (!fence_info)
return -ENOMEM;
for (i = 0; i < sync_file->num_fences; ++i)
sync_fill_fence_info(sync_file->cbs[i].fence, &fence_info[i]);
for (i = 0; i < num_fences; i++)
sync_fill_fence_info(fences[i], &fence_info[i]);
if (copy_to_user(u64_to_user_ptr(info.sync_fence_info), fence_info,
size)) {
......@@ -352,11 +429,8 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
no_fences:
strlcpy(info.name, sync_file->name, sizeof(info.name));
info.status = atomic_read(&sync_file->status);
if (info.status >= 0)
info.status = !info.status;
info.num_fences = sync_file->num_fences;
info.status = fence_is_signaled(sync_file->fence);
info.num_fences = num_fences;
if (copy_to_user((void __user *)arg, &info, sizeof(info)))
ret = -EFAULT;
......
......@@ -108,33 +108,13 @@ config DRM_KMS_CMA_HELPER
source "drivers/gpu/drm/i2c/Kconfig"
config DRM_TDFX
tristate "3dfx Banshee/Voodoo3+"
depends on DRM && PCI
help
Choose this option if you have a 3dfx Banshee or Voodoo3 (or later),
graphics card. If M is selected, the module will be called tdfx.
source "drivers/gpu/drm/arm/Kconfig"
config DRM_R128
tristate "ATI Rage 128"
depends on DRM && PCI
select FW_LOADER
help
Choose this option if you have an ATI Rage 128 graphics card. If M
is selected, the module will be called r128. AGP support for
this card is strongly suggested (unless you have a PCI version).
config DRM_RADEON
tristate "ATI Radeon"
depends on DRM && PCI
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select FW_LOADER
select DRM_KMS_HELPER
select DRM_KMS_FB_HELPER
select DRM_TTM
select POWER_SUPPLY
select HWMON
......@@ -153,12 +133,8 @@ source "drivers/gpu/drm/radeon/Kconfig"
config DRM_AMDGPU
tristate "AMD GPU"
depends on DRM && PCI
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select FW_LOADER
select DRM_KMS_HELPER
select DRM_KMS_FB_HELPER
select DRM_TTM
select POWER_SUPPLY
select HWMON
......@@ -171,55 +147,11 @@ config DRM_AMDGPU
If M is selected, the module will be called amdgpu.
source "drivers/gpu/drm/amd/amdgpu/Kconfig"
source "drivers/gpu/drm/amd/powerplay/Kconfig"
source "drivers/gpu/drm/amd/acp/Kconfig"
source "drivers/gpu/drm/nouveau/Kconfig"
config DRM_I810
tristate "Intel I810"
# !PREEMPT because of missing ioctl locking
depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN)
help
Choose this option if you have an Intel I810 graphics card. If M is
selected, the module will be called i810. AGP support is required
for this driver to work.
source "drivers/gpu/drm/i915/Kconfig"
config DRM_MGA
tristate "Matrox g200/g400"
depends on DRM && PCI
select FW_LOADER
help
Choose this option if you have a Matrox G200, G400 or G450 graphics
card. If M is selected, the module will be called mga. AGP
support is required for this driver to work.
config DRM_SIS
tristate "SiS video cards"
depends on DRM && AGP
depends on FB_SIS || FB_SIS=n
help
Choose this option if you have a SiS 630 or compatible video
chipset. If M is selected the module will be called sis. AGP
support is required for this driver to work.
config DRM_VIA
tristate "Via unichrome video cards"
depends on DRM && PCI
help
Choose this option if you have a Via unichrome or compatible video
chipset. If M is selected the module will be called via.
config DRM_SAVAGE
tristate "Savage video cards"
depends on DRM && PCI
help
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
chipset. If M is selected the module will be called savage.
config DRM_VGEM
tristate "Virtual GEM provider"
depends on DRM
......@@ -290,3 +222,81 @@ source "drivers/gpu/drm/arc/Kconfig"
source "drivers/gpu/drm/hisilicon/Kconfig"
source "drivers/gpu/drm/mediatek/Kconfig"
# Keep legacy drivers last
menuconfig DRM_LEGACY
bool "Enable legacy drivers (DANGEROUS)"
depends on DRM
help
Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous
APIs to user-space, which can be used to circumvent access
restrictions and other security measures. For backwards compatibility
those drivers are still available, but their use is highly
inadvisable and might harm your system.
You are recommended to use the safe modeset-only drivers instead, and
perform 3D emulation in user-space.
Unless you have strong reasons to go rogue, say "N".
if DRM_LEGACY
config DRM_TDFX
tristate "3dfx Banshee/Voodoo3+"
depends on DRM && PCI
help
Choose this option if you have a 3dfx Banshee or Voodoo3 (or later),
graphics card. If M is selected, the module will be called tdfx.
config DRM_R128
tristate "ATI Rage 128"
depends on DRM && PCI
select FW_LOADER
help
Choose this option if you have an ATI Rage 128 graphics card. If M
is selected, the module will be called r128. AGP support for
this card is strongly suggested (unless you have a PCI version).
config DRM_I810
tristate "Intel I810"
# !PREEMPT because of missing ioctl locking
depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN)
help
Choose this option if you have an Intel I810 graphics card. If M is
selected, the module will be called i810. AGP support is required
for this driver to work.
config DRM_MGA
tristate "Matrox g200/g400"
depends on DRM && PCI
select FW_LOADER
help
Choose this option if you have a Matrox G200, G400 or G450 graphics
card. If M is selected, the module will be called mga. AGP
support is required for this driver to work.
config DRM_SIS
tristate "SiS video cards"
depends on DRM && AGP
depends on FB_SIS || FB_SIS=n
help
Choose this option if you have a SiS 630 or compatible video
chipset. If M is selected the module will be called sis. AGP
support is required for this driver to work.
config DRM_VIA
tristate "Via unichrome video cards"
depends on DRM && PCI
help
Choose this option if you have a Via unichrome or compatible video
chipset. If M is selected the module will be called via.
config DRM_SAVAGE
tristate "Savage video cards"
depends on DRM && PCI
help
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
chipset. If M is selected the module will be called savage.
endif # DRM_LEGACY
......@@ -12,7 +12,10 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
drm_info.o drm_debugfs.o drm_encoder_slave.o \
drm_trace_points.o drm_global.o drm_prime.o \
drm_rect.o drm_vma_manager.o drm_flip_work.o \
drm_modeset_lock.o drm_atomic.o drm_bridge.o
drm_modeset_lock.o drm_atomic.o drm_bridge.o \
drm_framebuffer.o drm_connector.o drm_blend.o \
drm_encoder.o drm_mode_object.o drm_property.o \
drm_plane.o drm_color_mgmt.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
......@@ -24,7 +27,7 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
drm_simple_kms_helper.o drm_blend.o
drm_simple_kms_helper.o drm_modeset_helper.o
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
......@@ -46,7 +49,7 @@ obj-$(CONFIG_DRM_RADEON)+= radeon/
obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/
obj-$(CONFIG_DRM_MGA) += mga/
obj-$(CONFIG_DRM_I810) += i810/
obj-$(CONFIG_DRM_I915) += i915/
obj-$(CONFIG_DRM_I915) += i915/
obj-$(CONFIG_DRM_MGAG200) += mgag200/
obj-$(CONFIG_DRM_VC4) += vc4/
obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
......
config DRM_AMDGPU_SI
bool "Enable amdgpu support for SI parts"
depends on DRM_AMDGPU
help
Choose this option if you want to enable experimental support
for SI asics.
config DRM_AMDGPU_CIK
bool "Enable amdgpu support for CIK parts"
depends on DRM_AMDGPU
......@@ -25,3 +32,4 @@ config DRM_AMDGPU_GART_DEBUGFS
Selecting this option creates a debugfs file to inspect the mapped
pages. Uses more memory for housekeeping, enable only for debugging.
source "drivers/gpu/drm/amd/acp/Kconfig"
......@@ -23,13 +23,16 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
amdgpu_pm.o atombios_dp.o amdgpu_afmt.o amdgpu_trace_points.o \
atombios_encoders.o amdgpu_sa.o atombios_i2c.o \
amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \
amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o
amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o \
amdgpu_gtt_mgr.o
# add asic specific block
amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o cik_ih.o kv_smc.o kv_dpm.o \
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 \
amdgpu_amdkfd_gfx_v7.o
amdgpu-$(CONFIG_DRM_AMDGPU_SI)+= si.o gmc_v6_0.o gfx_v6_0.o si_ih.o si_dma.o dce_v6_0.o si_dpm.o si_smc.o
amdgpu-y += \
vi.o
......@@ -50,15 +53,13 @@ amdgpu-y += \
amdgpu-y += \
amdgpu_dpm.o \
amdgpu_powerplay.o \
cz_smc.o cz_dpm.o \
tonga_smc.o tonga_dpm.o \
fiji_smc.o fiji_dpm.o \
iceland_smc.o iceland_dpm.o
cz_smc.o cz_dpm.o
# add DCE block
amdgpu-y += \
dce_v10_0.o \
dce_v11_0.o
dce_v11_0.o \
dce_virtual.o
# add GFX block
amdgpu-y += \
......@@ -110,14 +111,10 @@ amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o
amdgpu-$(CONFIG_ACPI) += amdgpu_acpi.o
amdgpu-$(CONFIG_MMU_NOTIFIER) += amdgpu_mn.o
ifneq ($(CONFIG_DRM_AMD_POWERPLAY),)
include $(FULL_AMD_PATH)/powerplay/Makefile
amdgpu-y += $(AMD_POWERPLAY_FILES)
endif
obj-$(CONFIG_DRM_AMDGPU)+= amdgpu.o
CFLAGS_amdgpu_trace_points.o := -I$(src)
......@@ -90,6 +90,7 @@
#define ENCODER_OBJECT_ID_INTERNAL_VCE 0x24
#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 0x25
#define ENCODER_OBJECT_ID_INTERNAL_AMCLK 0x27
#define ENCODER_OBJECT_ID_VIRTUAL 0x28
#define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO 0xFF
......@@ -119,6 +120,7 @@
#define CONNECTOR_OBJECT_ID_eDP 0x14
#define CONNECTOR_OBJECT_ID_MXM 0x15
#define CONNECTOR_OBJECT_ID_LVDS_eDP 0x16
#define CONNECTOR_OBJECT_ID_VIRTUAL 0x17
/* deleted */
......@@ -147,6 +149,7 @@
#define GRAPH_OBJECT_ENUM_ID5 0x05
#define GRAPH_OBJECT_ENUM_ID6 0x06
#define GRAPH_OBJECT_ENUM_ID7 0x07
#define GRAPH_OBJECT_ENUM_VIRTUAL 0x08
/****************************************************/
/* Graphics Object ID Bit definition */
......@@ -408,6 +411,10 @@
GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
ENCODER_OBJECT_ID_HDMI_ANX9805 << OBJECT_ID_SHIFT)
#define ENCODER_VIRTUAL_ENUM_VIRTUAL ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
GRAPH_OBJECT_ENUM_VIRTUAL << ENUM_ID_SHIFT |\
ENCODER_OBJECT_ID_VIRTUAL << OBJECT_ID_SHIFT)
/****************************************************/
/* Connector Object ID definition - Shared with BIOS */
/****************************************************/
......
此差异已折叠。
......@@ -25,6 +25,7 @@
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/power_supply.h>
#include <linux/pm_runtime.h>
#include <acpi/video.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
......@@ -333,6 +334,16 @@ int amdgpu_atif_handler(struct amdgpu_device *adev,
#endif
}
}
if (req.pending & ATIF_DGPU_DISPLAY_EVENT) {
if ((adev->flags & AMD_IS_PX) &&
amdgpu_atpx_dgpu_req_power_for_displays()) {
pm_runtime_get_sync(adev->ddev->dev);
/* Just fire off a uevent and let userspace tell us what to do */
drm_helper_hpd_irq_event(adev->ddev);
pm_runtime_mark_last_busy(adev->ddev->dev);
pm_runtime_put_autosuspend(adev->ddev->dev);
}
}
/* TODO: check other events */
/* We've handled the event, stop the notifier chain. The ACPI interface
......
......@@ -143,14 +143,6 @@ int amdgpu_amdkfd_resume(struct amdgpu_device *rdev)
return r;
}
u32 pool_to_domain(enum kgd_memory_pool p)
{
switch (p) {
case KGD_POOL_FRAMEBUFFER: return AMDGPU_GEM_DOMAIN_VRAM;
default: return AMDGPU_GEM_DOMAIN_GTT;
}
}
int alloc_gtt_mem(struct kgd_dev *kgd, size_t size,
void **mem_obj, uint64_t *gpu_addr,
void **cpu_ptr)
......
......@@ -103,11 +103,11 @@ static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address,
uint32_t pipe_id, uint32_t queue_id);
static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
unsigned int timeout, uint32_t pipe_id,
unsigned int utimeout, uint32_t pipe_id,
uint32_t queue_id);
static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd);
static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
unsigned int timeout);
unsigned int utimeout);
static int kgd_address_watch_disable(struct kgd_dev *kgd);
static int kgd_address_watch_execute(struct kgd_dev *kgd,
unsigned int watch_point_id,
......@@ -437,11 +437,12 @@ static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd)
}
static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
unsigned int timeout, uint32_t pipe_id,
unsigned int utimeout, uint32_t pipe_id,
uint32_t queue_id)
{
struct amdgpu_device *adev = get_amdgpu_device(kgd);
uint32_t temp;
int timeout = utimeout;
acquire_queue(kgd, pipe_id, queue_id);
WREG32(mmCP_HQD_PQ_DOORBELL_CONTROL, 0);
......@@ -452,9 +453,8 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
temp = RREG32(mmCP_HQD_ACTIVE);
if (temp & CP_HQD_ACTIVE__ACTIVE_MASK)
break;
if (timeout == 0) {
pr_err("kfd: cp queue preemption time out (%dms)\n",
temp);
if (timeout <= 0) {
pr_err("kfd: cp queue preemption time out.\n");
release_queue(kgd);
return -ETIME;
}
......@@ -467,12 +467,13 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
}
static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
unsigned int timeout)
unsigned int utimeout)
{
struct amdgpu_device *adev = get_amdgpu_device(kgd);
struct cik_sdma_rlc_registers *m;
uint32_t sdma_base_addr;
uint32_t temp;
int timeout = utimeout;
m = get_sdma_mqd(mqd);
sdma_base_addr = get_sdma_base_addr(m);
......@@ -485,7 +486,7 @@ static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
temp = RREG32(sdma_base_addr + mmSDMA0_RLC0_CONTEXT_STATUS);
if (temp & SDMA0_STATUS_REG__RB_CMD_IDLE__SHIFT)
break;
if (timeout == 0)
if (timeout <= 0)
return -ETIME;
msleep(20);
timeout -= 20;
......
......@@ -62,10 +62,10 @@ static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address,
uint32_t pipe_id, uint32_t queue_id);
static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd);
static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
unsigned int timeout, uint32_t pipe_id,
unsigned int utimeout, uint32_t pipe_id,
uint32_t queue_id);
static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
unsigned int timeout);
unsigned int utimeout);
static void write_vmid_invalidate_request(struct kgd_dev *kgd, uint8_t vmid);
static int kgd_address_watch_disable(struct kgd_dev *kgd);
static int kgd_address_watch_execute(struct kgd_dev *kgd,
......@@ -349,11 +349,12 @@ static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd)
}
static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
unsigned int timeout, uint32_t pipe_id,
unsigned int utimeout, uint32_t pipe_id,
uint32_t queue_id)
{
struct amdgpu_device *adev = get_amdgpu_device(kgd);
uint32_t temp;
int timeout = utimeout;
acquire_queue(kgd, pipe_id, queue_id);
......@@ -363,9 +364,8 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
temp = RREG32(mmCP_HQD_ACTIVE);
if (temp & CP_HQD_ACTIVE__ACTIVE_MASK)
break;
if (timeout == 0) {
pr_err("kfd: cp queue preemption time out (%dms)\n",
temp);
if (timeout <= 0) {
pr_err("kfd: cp queue preemption time out.\n");
release_queue(kgd);
return -ETIME;
}
......@@ -378,12 +378,13 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
}
static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
unsigned int timeout)
unsigned int utimeout)
{
struct amdgpu_device *adev = get_amdgpu_device(kgd);
struct cik_sdma_rlc_registers *m;
uint32_t sdma_base_addr;
uint32_t temp;
int timeout = utimeout;
m = get_sdma_mqd(mqd);
sdma_base_addr = get_sdma_base_addr(m);
......@@ -396,7 +397,7 @@ static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
temp = RREG32(sdma_base_addr + mmSDMA0_RLC0_CONTEXT_STATUS);
if (temp & SDMA0_STATUS_REG__RB_CMD_IDLE__SHIFT)
break;
if (timeout == 0)
if (timeout <= 0)
return -ETIME;
msleep(20);
timeout -= 20;
......
......@@ -259,6 +259,33 @@ static const int object_connector_convert[] = {
DRM_MODE_CONNECTOR_Unknown
};
bool amdgpu_atombios_has_dce_engine_info(struct amdgpu_device *adev)
{
struct amdgpu_mode_info *mode_info = &adev->mode_info;
struct atom_context *ctx = mode_info->atom_context;
int index = GetIndexIntoMasterTable(DATA, Object_Header);
u16 size, data_offset;
u8 frev, crev;
ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
ATOM_OBJECT_HEADER *obj_header;
if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset))
return false;
if (crev < 2)
return false;
obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);
path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)
(ctx->bios + data_offset +
le16_to_cpu(obj_header->usDisplayPathTableOffset));
if (path_obj->ucNumOfDispPath)
return true;
else
return false;
}
bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev)
{
struct amdgpu_mode_info *mode_info = &adev->mode_info;
......@@ -964,6 +991,48 @@ int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
return -EINVAL;
switch (crev) {
case 2:
case 3:
case 5:
/* r6xx, r7xx, evergreen, ni, si.
* TODO: add support for asic_type <= CHIP_RV770*/
if (clock_type == COMPUTE_ENGINE_PLL_PARAM) {
args.v3.ulClockParams = cpu_to_le32((clock_type << 24) | clock);
amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
dividers->post_div = args.v3.ucPostDiv;
dividers->enable_post_div = (args.v3.ucCntlFlag &
ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN) ? true : false;
dividers->enable_dithen = (args.v3.ucCntlFlag &
ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE) ? false : true;
dividers->whole_fb_div = le16_to_cpu(args.v3.ulFbDiv.usFbDiv);
dividers->frac_fb_div = le16_to_cpu(args.v3.ulFbDiv.usFbDivFrac);
dividers->ref_div = args.v3.ucRefDiv;
dividers->vco_mode = (args.v3.ucCntlFlag &
ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE) ? 1 : 0;
} else {
/* for SI we use ComputeMemoryClockParam for memory plls */
if (adev->asic_type >= CHIP_TAHITI)
return -EINVAL;
args.v5.ulClockParams = cpu_to_le32((clock_type << 24) | clock);
if (strobe_mode)
args.v5.ucInputFlag = ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN;
amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
dividers->post_div = args.v5.ucPostDiv;
dividers->enable_post_div = (args.v5.ucCntlFlag &
ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN) ? true : false;
dividers->enable_dithen = (args.v5.ucCntlFlag &
ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE) ? false : true;
dividers->whole_fb_div = le16_to_cpu(args.v5.ulFbDiv.usFbDiv);
dividers->frac_fb_div = le16_to_cpu(args.v5.ulFbDiv.usFbDivFrac);
dividers->ref_div = args.v5.ucRefDiv;
dividers->vco_mode = (args.v5.ucCntlFlag &
ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE) ? 1 : 0;
}
break;
case 4:
/* fusion */
args.v4.ulClock = cpu_to_le32(clock); /* 10 khz */
......@@ -1108,6 +1177,32 @@ void amdgpu_atombios_set_engine_dram_timings(struct amdgpu_device *adev,
amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
}
void amdgpu_atombios_get_default_voltages(struct amdgpu_device *adev,
u16 *vddc, u16 *vddci, u16 *mvdd)
{
struct amdgpu_mode_info *mode_info = &adev->mode_info;
int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
u8 frev, crev;
u16 data_offset;
union firmware_info *firmware_info;
*vddc = 0;
*vddci = 0;
*mvdd = 0;
if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset)) {
firmware_info =
(union firmware_info *)(mode_info->atom_context->bios +
data_offset);
*vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage);
if ((frev == 2) && (crev >= 2)) {
*vddci = le16_to_cpu(firmware_info->info_22.usBootUpVDDCIVoltage);
*mvdd = le16_to_cpu(firmware_info->info_22.usBootUpMVDDCVoltage);
}
}
}
union set_voltage {
struct _SET_VOLTAGE_PS_ALLOCATION alloc;
struct _SET_VOLTAGE_PARAMETERS v1;
......@@ -1115,6 +1210,52 @@ union set_voltage {
struct _SET_VOLTAGE_PARAMETERS_V1_3 v3;
};
int amdgpu_atombios_get_max_vddc(struct amdgpu_device *adev, u8 voltage_type,
u16 voltage_id, u16 *voltage)
{
union set_voltage args;
int index = GetIndexIntoMasterTable(COMMAND, SetVoltage);
u8 frev, crev;
if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev))
return -EINVAL;
switch (crev) {
case 1:
return -EINVAL;
case 2:
args.v2.ucVoltageType = SET_VOLTAGE_GET_MAX_VOLTAGE;
args.v2.ucVoltageMode = 0;
args.v2.usVoltageLevel = 0;
amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
*voltage = le16_to_cpu(args.v2.usVoltageLevel);
break;
case 3:
args.v3.ucVoltageType = voltage_type;
args.v3.ucVoltageMode = ATOM_GET_VOLTAGE_LEVEL;
args.v3.usVoltageLevel = cpu_to_le16(voltage_id);
amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
*voltage = le16_to_cpu(args.v3.usVoltageLevel);
break;
default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
return -EINVAL;
}
return 0;
}
int amdgpu_atombios_get_leakage_vddc_based_on_leakage_idx(struct amdgpu_device *adev,
u16 *voltage,
u16 leakage_idx)
{
return amdgpu_atombios_get_max_vddc(adev, VOLTAGE_TYPE_VDDC, leakage_idx, voltage);
}
void amdgpu_atombios_set_voltage(struct amdgpu_device *adev,
u16 voltage_level,
u8 voltage_type)
......@@ -1335,6 +1476,50 @@ static ATOM_VOLTAGE_OBJECT_V3 *amdgpu_atombios_lookup_voltage_object_v3(ATOM_VOL
return NULL;
}
int amdgpu_atombios_get_svi2_info(struct amdgpu_device *adev,
u8 voltage_type,
u8 *svd_gpio_id, u8 *svc_gpio_id)
{
int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
u8 frev, crev;
u16 data_offset, size;
union voltage_object_info *voltage_info;
union voltage_object *voltage_object = NULL;
if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) {
voltage_info = (union voltage_object_info *)
(adev->mode_info.atom_context->bios + data_offset);
switch (frev) {
case 3:
switch (crev) {
case 1:
voltage_object = (union voltage_object *)
amdgpu_atombios_lookup_voltage_object_v3(&voltage_info->v3,
voltage_type,
VOLTAGE_OBJ_SVID2);
if (voltage_object) {
*svd_gpio_id = voltage_object->v3.asSVID2Obj.ucSVDGpioId;
*svc_gpio_id = voltage_object->v3.asSVID2Obj.ucSVCGpioId;
} else {
return -EINVAL;
}
break;
default:
DRM_ERROR("unknown voltage object table\n");
return -EINVAL;
}
break;
default:
DRM_ERROR("unknown voltage object table\n");
return -EINVAL;
}
}
return 0;
}
bool
amdgpu_atombios_is_voltage_gpio(struct amdgpu_device *adev,
u8 voltage_type, u8 voltage_mode)
......
......@@ -140,6 +140,8 @@ struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *
uint8_t id);
void amdgpu_atombios_i2c_init(struct amdgpu_device *adev);
bool amdgpu_atombios_has_dce_engine_info(struct amdgpu_device *adev);
bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev);
int amdgpu_atombios_get_clock_info(struct amdgpu_device *adev);
......@@ -206,5 +208,19 @@ void amdgpu_atombios_scratch_regs_save(struct amdgpu_device *adev);
void amdgpu_atombios_scratch_regs_restore(struct amdgpu_device *adev);
void amdgpu_atombios_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le);
int amdgpu_atombios_get_max_vddc(struct amdgpu_device *adev, u8 voltage_type,
u16 voltage_id, u16 *voltage);
int amdgpu_atombios_get_leakage_vddc_based_on_leakage_idx(struct amdgpu_device *adev,
u16 *voltage,
u16 leakage_idx);
void amdgpu_atombios_get_default_voltages(struct amdgpu_device *adev,
u16 *vddc, u16 *vddci, u16 *mvdd);
int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
u8 clock_type,
u32 clock,
bool strobe_mode,
struct atom_clock_dividers *dividers);
int amdgpu_atombios_get_svi2_info(struct amdgpu_device *adev,
u8 voltage_type,
u8 *svd_gpio_id, u8 *svc_gpio_id);
#endif
......@@ -29,6 +29,7 @@ struct amdgpu_atpx {
acpi_handle handle;
struct amdgpu_atpx_functions functions;
bool is_hybrid;
bool dgpu_req_power_for_displays;
};
static struct amdgpu_atpx_priv {
......@@ -73,6 +74,10 @@ bool amdgpu_is_atpx_hybrid(void) {
return amdgpu_atpx_priv.atpx.is_hybrid;
}
bool amdgpu_atpx_dgpu_req_power_for_displays(void) {
return amdgpu_atpx_priv.atpx.dgpu_req_power_for_displays;
}
/**
* amdgpu_atpx_call - call an ATPX method
*
......@@ -204,6 +209,10 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
atpx->is_hybrid = true;
}
atpx->dgpu_req_power_for_displays = false;
if (valid_bits & ATPX_DGPU_REQ_POWER_FOR_DISPLAYS)
atpx->dgpu_req_power_for_displays = true;
return 0;
}
......
......@@ -39,7 +39,8 @@ static int amdgpu_benchmark_do_move(struct amdgpu_device *adev, unsigned size,
start_jiffies = jiffies;
for (i = 0; i < n; i++) {
struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
r = amdgpu_copy_buffer(ring, saddr, daddr, size, NULL, &fence);
r = amdgpu_copy_buffer(ring, saddr, daddr, size, NULL, &fence,
false);
if (r)
goto exit_do_move;
r = fence_wait(fence, false);
......
......@@ -616,7 +616,7 @@ static int amdgpu_cgs_irq_put(struct cgs_device *cgs_device, unsigned src_id, un
return amdgpu_irq_put(adev, adev->irq.sources[src_id], type);
}
int amdgpu_cgs_set_clockgating_state(struct cgs_device *cgs_device,
static int amdgpu_cgs_set_clockgating_state(struct cgs_device *cgs_device,
enum amd_ip_block_type block_type,
enum amd_clockgating_state state)
{
......@@ -637,7 +637,7 @@ int amdgpu_cgs_set_clockgating_state(struct cgs_device *cgs_device,
return r;
}
int amdgpu_cgs_set_powergating_state(struct cgs_device *cgs_device,
static int amdgpu_cgs_set_powergating_state(struct cgs_device *cgs_device,
enum amd_ip_block_type block_type,
enum amd_powergating_state state)
{
......@@ -711,6 +711,47 @@ static int amdgpu_cgs_rel_firmware(struct cgs_device *cgs_device, enum cgs_ucode
return -EINVAL;
}
static uint16_t amdgpu_get_firmware_version(struct cgs_device *cgs_device,
enum cgs_ucode_id type)
{
CGS_FUNC_ADEV;
uint16_t fw_version;
switch (type) {
case CGS_UCODE_ID_SDMA0:
fw_version = adev->sdma.instance[0].fw_version;
break;
case CGS_UCODE_ID_SDMA1:
fw_version = adev->sdma.instance[1].fw_version;
break;
case CGS_UCODE_ID_CP_CE:
fw_version = adev->gfx.ce_fw_version;
break;
case CGS_UCODE_ID_CP_PFP:
fw_version = adev->gfx.pfp_fw_version;
break;
case CGS_UCODE_ID_CP_ME:
fw_version = adev->gfx.me_fw_version;
break;
case CGS_UCODE_ID_CP_MEC:
fw_version = adev->gfx.mec_fw_version;
break;
case CGS_UCODE_ID_CP_MEC_JT1:
fw_version = adev->gfx.mec_fw_version;
break;
case CGS_UCODE_ID_CP_MEC_JT2:
fw_version = adev->gfx.mec_fw_version;
break;
case CGS_UCODE_ID_RLC_G:
fw_version = adev->gfx.rlc_fw_version;
break;
default:
DRM_ERROR("firmware type %d do not have version\n", type);
fw_version = 0;
}
return fw_version;
}
static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
enum cgs_ucode_id type,
struct cgs_firmware_info *info)
......@@ -741,6 +782,7 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
info->mc_addr = gpu_addr;
info->image_size = data_size;
info->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
info->fw_version = amdgpu_get_firmware_version(cgs_device, type);
info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);
} else {
char fw_name[30] = {0};
......@@ -848,6 +890,12 @@ static int amdgpu_cgs_query_system_info(struct cgs_device *cgs_device,
case CGS_SYSTEM_INFO_GFX_SE_INFO:
sys_info->value = adev->gfx.config.max_shader_engines;
break;
case CGS_SYSTEM_INFO_PCIE_SUB_SYS_ID:
sys_info->value = adev->pdev->subsystem_device;
break;
case CGS_SYSTEM_INFO_PCIE_SUB_SYS_VENDOR_ID:
sys_info->value = adev->pdev->subsystem_vendor;
break;
default:
return -ENODEV;
}
......
......@@ -168,12 +168,12 @@ int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector)
}
/* Any defined maximum tmds clock limit we must not exceed? */
if (connector->max_tmds_clock > 0) {
if (connector->display_info.max_tmds_clock > 0) {
/* mode_clock is clock in kHz for mode to be modeset on this connector */
mode_clock = amdgpu_connector->pixelclock_for_modeset;
/* Maximum allowable input clock in kHz */
max_tmds_clock = connector->max_tmds_clock * 1000;
max_tmds_clock = connector->display_info.max_tmds_clock;
DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n",
connector->name, mode_clock, max_tmds_clock);
......@@ -769,8 +769,10 @@ static void amdgpu_connector_destroy(struct drm_connector *connector)
{
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
if (amdgpu_connector->ddc_bus->has_aux)
if (amdgpu_connector->ddc_bus->has_aux) {
drm_dp_aux_unregister(&amdgpu_connector->ddc_bus->aux);
amdgpu_connector->ddc_bus->has_aux = false;
}
amdgpu_connector_free_edid(connector);
kfree(amdgpu_connector->con_priv);
drm_connector_unregister(connector);
......@@ -1504,6 +1506,88 @@ static const struct drm_connector_funcs amdgpu_connector_edp_funcs = {
.force = amdgpu_connector_dvi_force,
};
static struct drm_encoder *
amdgpu_connector_virtual_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
struct drm_encoder *encoder;
int i;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] == 0)
break;
encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
if (!encoder)
continue;
if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
return encoder;
}
/* pick the first one */
if (enc_id)
return drm_encoder_find(connector->dev, enc_id);
return NULL;
}
static int amdgpu_connector_virtual_get_modes(struct drm_connector *connector)
{
struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector);
if (encoder) {
amdgpu_connector_add_common_modes(encoder, connector);
}
return 0;
}
static int amdgpu_connector_virtual_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static int
amdgpu_connector_virtual_dpms(struct drm_connector *connector, int mode)
{
return 0;
}
static enum drm_connector_status
amdgpu_connector_virtual_detect(struct drm_connector *connector, bool force)
{
return connector_status_connected;
}
static int
amdgpu_connector_virtual_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
return 0;
}
static void amdgpu_connector_virtual_force(struct drm_connector *connector)
{
return;
}
static const struct drm_connector_helper_funcs amdgpu_connector_virtual_helper_funcs = {
.get_modes = amdgpu_connector_virtual_get_modes,
.mode_valid = amdgpu_connector_virtual_mode_valid,
.best_encoder = amdgpu_connector_virtual_encoder,
};
static const struct drm_connector_funcs amdgpu_connector_virtual_funcs = {
.dpms = amdgpu_connector_virtual_dpms,
.detect = amdgpu_connector_virtual_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = amdgpu_connector_virtual_set_property,
.destroy = amdgpu_connector_destroy,
.force = amdgpu_connector_virtual_force,
};
void
amdgpu_connector_add(struct amdgpu_device *adev,
uint32_t connector_id,
......@@ -1888,6 +1972,17 @@ amdgpu_connector_add(struct amdgpu_device *adev,
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
break;
case DRM_MODE_CONNECTOR_VIRTUAL:
amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL);
if (!amdgpu_dig_connector)
goto failed;
amdgpu_connector->con_priv = amdgpu_dig_connector;
drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_virtual_funcs, connector_type);
drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_virtual_helper_funcs);
subpixel_order = SubPixelHorizontalRGB;
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
break;
}
}
......
......@@ -91,6 +91,7 @@ static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,
uint32_t *offset)
{
struct drm_gem_object *gobj;
unsigned long size;
gobj = drm_gem_object_lookup(p->filp, data->handle);
if (gobj == NULL)
......@@ -101,6 +102,11 @@ static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,
p->uf_entry.tv.bo = &p->uf_entry.robj->tbo;
p->uf_entry.tv.shared = true;
p->uf_entry.user_pages = NULL;
size = amdgpu_bo_size(p->uf_entry.robj);
if (size != PAGE_SIZE || (data->offset + 8) > size)
return -EINVAL;
*offset = data->offset;
drm_gem_object_unreference_unlocked(gobj);
......@@ -235,70 +241,212 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
return ret;
}
/* Returns how many bytes TTM can move per IB.
/* Convert microseconds to bytes. */
static u64 us_to_bytes(struct amdgpu_device *adev, s64 us)
{
if (us <= 0 || !adev->mm_stats.log2_max_MBps)
return 0;
/* Since accum_us is incremented by a million per second, just
* multiply it by the number of MB/s to get the number of bytes.
*/
return us << adev->mm_stats.log2_max_MBps;
}
static s64 bytes_to_us(struct amdgpu_device *adev, u64 bytes)
{
if (!adev->mm_stats.log2_max_MBps)
return 0;
return bytes >> adev->mm_stats.log2_max_MBps;
}
/* Returns how many bytes TTM can move right now. If no bytes can be moved,
* it returns 0. If it returns non-zero, it's OK to move at least one buffer,
* which means it can go over the threshold once. If that happens, the driver
* will be in debt and no other buffer migrations can be done until that debt
* is repaid.
*
* This approach allows moving a buffer of any size (it's important to allow
* that).
*
* The currency is simply time in microseconds and it increases as the clock
* ticks. The accumulated microseconds (us) are converted to bytes and
* returned.
*/
static u64 amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev)
{
u64 real_vram_size = adev->mc.real_vram_size;
u64 vram_usage = atomic64_read(&adev->vram_usage);
s64 time_us, increment_us;
u64 max_bytes;
u64 free_vram, total_vram, used_vram;
/* This function is based on the current VRAM usage.
/* Allow a maximum of 200 accumulated ms. This is basically per-IB
* throttling.
*
* - If all of VRAM is free, allow relocating the number of bytes that
* is equal to 1/4 of the size of VRAM for this IB.
* It means that in order to get full max MBps, at least 5 IBs per
* second must be submitted and not more than 200ms apart from each
* other.
*/
const s64 us_upper_bound = 200000;
* - If more than one half of VRAM is occupied, only allow relocating
* 1 MB of data for this IB.
*
* - From 0 to one half of used VRAM, the threshold decreases
* linearly.
* __________________
* 1/4 of -|\ |
* VRAM | \ |
* | \ |
* | \ |
* | \ |
* | \ |
* | \ |
* | \________|1 MB
* |----------------|
* VRAM 0 % 100 %
* used used
*
* Note: It's a threshold, not a limit. The threshold must be crossed
* for buffer relocations to stop, so any buffer of an arbitrary size
* can be moved as long as the threshold isn't crossed before
* the relocation takes place. We don't want to disable buffer
* relocations completely.
if (!adev->mm_stats.log2_max_MBps)
return 0;
total_vram = adev->mc.real_vram_size - adev->vram_pin_size;
used_vram = atomic64_read(&adev->vram_usage);
free_vram = used_vram >= total_vram ? 0 : total_vram - used_vram;
spin_lock(&adev->mm_stats.lock);
/* Increase the amount of accumulated us. */
time_us = ktime_to_us(ktime_get());
increment_us = time_us - adev->mm_stats.last_update_us;
adev->mm_stats.last_update_us = time_us;
adev->mm_stats.accum_us = min(adev->mm_stats.accum_us + increment_us,
us_upper_bound);
/* This prevents the short period of low performance when the VRAM
* usage is low and the driver is in debt or doesn't have enough
* accumulated us to fill VRAM quickly.
*
* The idea is that buffers should be placed in VRAM at creation time
* and TTM should only do a minimum number of relocations during
* command submission. In practice, you need to submit at least
* a dozen IBs to move all buffers to VRAM if they are in GTT.
* The situation can occur in these cases:
* - a lot of VRAM is freed by userspace
* - the presence of a big buffer causes a lot of evictions
* (solution: split buffers into smaller ones)
*
* Also, things can get pretty crazy under memory pressure and actual
* VRAM usage can change a lot, so playing safe even at 50% does
* consistently increase performance.
* If 128 MB or 1/8th of VRAM is free, start filling it now by setting
* accum_us to a positive number.
*/
if (free_vram >= 128 * 1024 * 1024 || free_vram >= total_vram / 8) {
s64 min_us;
/* Be more aggresive on dGPUs. Try to fill a portion of free
* VRAM now.
*/
if (!(adev->flags & AMD_IS_APU))
min_us = bytes_to_us(adev, free_vram / 4);
else
min_us = 0; /* Reset accum_us on APUs. */
adev->mm_stats.accum_us = max(min_us, adev->mm_stats.accum_us);
}
/* This returns 0 if the driver is in debt to disallow (optional)
* buffer moves.
*/
max_bytes = us_to_bytes(adev, adev->mm_stats.accum_us);
spin_unlock(&adev->mm_stats.lock);
return max_bytes;
}
/* Report how many bytes have really been moved for the last command
* submission. This can result in a debt that can stop buffer migrations
* temporarily.
*/
static void amdgpu_cs_report_moved_bytes(struct amdgpu_device *adev,
u64 num_bytes)
{
spin_lock(&adev->mm_stats.lock);
adev->mm_stats.accum_us -= bytes_to_us(adev, num_bytes);
spin_unlock(&adev->mm_stats.lock);
}
static int amdgpu_cs_bo_validate(struct amdgpu_cs_parser *p,
struct amdgpu_bo *bo)
{
u64 initial_bytes_moved;
uint32_t domain;
int r;
if (bo->pin_count)
return 0;
/* Don't move this buffer if we have depleted our allowance
* to move it. Don't move anything if the threshold is zero.
*/
if (p->bytes_moved < p->bytes_moved_threshold)
domain = bo->prefered_domains;
else
domain = bo->allowed_domains;
retry:
amdgpu_ttm_placement_from_domain(bo, domain);
initial_bytes_moved = atomic64_read(&bo->adev->num_bytes_moved);
r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
p->bytes_moved += atomic64_read(&bo->adev->num_bytes_moved) -
initial_bytes_moved;
if (unlikely(r == -ENOMEM) && domain != bo->allowed_domains) {
domain = bo->allowed_domains;
goto retry;
}
u64 half_vram = real_vram_size >> 1;
u64 half_free_vram = vram_usage >= half_vram ? 0 : half_vram - vram_usage;
u64 bytes_moved_threshold = half_free_vram >> 1;
return max(bytes_moved_threshold, 1024*1024ull);
return r;
}
int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p,
/* Last resort, try to evict something from the current working set */
static bool amdgpu_cs_try_evict(struct amdgpu_cs_parser *p,
struct amdgpu_bo_list_entry *lobj)
{
uint32_t domain = lobj->robj->allowed_domains;
int r;
if (!p->evictable)
return false;
for (;&p->evictable->tv.head != &p->validated;
p->evictable = list_prev_entry(p->evictable, tv.head)) {
struct amdgpu_bo_list_entry *candidate = p->evictable;
struct amdgpu_bo *bo = candidate->robj;
u64 initial_bytes_moved;
uint32_t other;
/* If we reached our current BO we can forget it */
if (candidate == lobj)
break;
other = amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type);
/* Check if this BO is in one of the domains we need space for */
if (!(other & domain))
continue;
/* Check if we can move this BO somewhere else */
other = bo->allowed_domains & ~domain;
if (!other)
continue;
/* Good we can try to move this BO somewhere else */
amdgpu_ttm_placement_from_domain(bo, other);
initial_bytes_moved = atomic64_read(&bo->adev->num_bytes_moved);
r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
p->bytes_moved += atomic64_read(&bo->adev->num_bytes_moved) -
initial_bytes_moved;
if (unlikely(r))
break;
p->evictable = list_prev_entry(p->evictable, tv.head);
list_move(&candidate->tv.head, &p->validated);
return true;
}
return false;
}
static int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p,
struct list_head *validated)
{
struct amdgpu_bo_list_entry *lobj;
u64 initial_bytes_moved;
int r;
list_for_each_entry(lobj, validated, tv.head) {
struct amdgpu_bo *bo = lobj->robj;
bool binding_userptr = false;
struct mm_struct *usermm;
uint32_t domain;
usermm = amdgpu_ttm_tt_get_usermm(bo->tbo.ttm);
if (usermm && usermm != current->mm)
......@@ -313,35 +461,19 @@ int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p,
binding_userptr = true;
}
if (bo->pin_count)
continue;
/* Avoid moving this one if we have moved too many buffers
* for this IB already.
*
* Note that this allows moving at least one buffer of
* any size, because it doesn't take the current "bo"
* into account. We don't want to disallow buffer moves
* completely.
*/
if (p->bytes_moved <= p->bytes_moved_threshold)
domain = bo->prefered_domains;
else
domain = bo->allowed_domains;
retry:
amdgpu_ttm_placement_from_domain(bo, domain);
initial_bytes_moved = atomic64_read(&bo->adev->num_bytes_moved);
r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
p->bytes_moved += atomic64_read(&bo->adev->num_bytes_moved) -
initial_bytes_moved;
if (p->evictable == lobj)
p->evictable = NULL;
if (unlikely(r)) {
if (r != -ERESTARTSYS && domain != bo->allowed_domains) {
domain = bo->allowed_domains;
goto retry;
}
do {
r = amdgpu_cs_bo_validate(p, bo);
} while (r == -ENOMEM && amdgpu_cs_try_evict(p, lobj));
if (r)
return r;
if (bo->shadow) {
r = amdgpu_cs_bo_validate(p, bo);
if (r)
return r;
}
if (binding_userptr) {
......@@ -386,8 +518,10 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true,
&duplicates);
if (unlikely(r != 0))
if (unlikely(r != 0)) {
DRM_ERROR("ttm_eu_reserve_buffers failed.\n");
goto error_free_pages;
}
/* Without a BO list we don't have userptr BOs */
if (!p->bo_list)
......@@ -427,9 +561,10 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
/* Unreserve everything again. */
ttm_eu_backoff_reservation(&p->ticket, &p->validated);
/* We tried to often, just abort */
/* We tried too many times, just abort */
if (!--tries) {
r = -EDEADLK;
DRM_ERROR("deadlock in %s\n", __func__);
goto error_free_pages;
}
......@@ -441,11 +576,13 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
sizeof(struct page*));
if (!e->user_pages) {
r = -ENOMEM;
DRM_ERROR("calloc failure in %s\n", __func__);
goto error_free_pages;
}
r = amdgpu_ttm_tt_get_user_pages(ttm, e->user_pages);
if (r) {
DRM_ERROR("amdgpu_ttm_tt_get_user_pages failed.\n");
drm_free_large(e->user_pages);
e->user_pages = NULL;
goto error_free_pages;
......@@ -460,14 +597,23 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
p->bytes_moved_threshold = amdgpu_cs_get_threshold_for_moves(p->adev);
p->bytes_moved = 0;
p->evictable = list_last_entry(&p->validated,
struct amdgpu_bo_list_entry,
tv.head);
r = amdgpu_cs_list_validate(p, &duplicates);
if (r)
if (r) {
DRM_ERROR("amdgpu_cs_list_validate(duplicates) failed.\n");
goto error_validate;
}
r = amdgpu_cs_list_validate(p, &p->validated);
if (r)
if (r) {
DRM_ERROR("amdgpu_cs_list_validate(validated) failed.\n");
goto error_validate;
}
amdgpu_cs_report_moved_bytes(p->adev, p->bytes_moved);
fpriv->vm.last_eviction_counter =
atomic64_read(&p->adev->num_evictions);
......@@ -499,8 +645,12 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
}
}
if (p->uf_entry.robj)
p->job->uf_addr += amdgpu_bo_gpu_offset(p->uf_entry.robj);
if (!r && p->uf_entry.robj) {
struct amdgpu_bo *uf = p->uf_entry.robj;
r = amdgpu_ttm_bind(&uf->tbo, &uf->tbo.mem);
p->job->uf_addr += amdgpu_bo_gpu_offset(uf);
}
error_validate:
if (r) {
......@@ -617,7 +767,7 @@ static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p,
if (bo_va == NULL)
continue;
r = amdgpu_vm_bo_update(adev, bo_va, &bo->tbo.mem);
r = amdgpu_vm_bo_update(adev, bo_va, false);
if (r)
return r;
......@@ -710,6 +860,14 @@ static int amdgpu_cs_ib_fill(struct amdgpu_device *adev,
if (r)
return r;
if (ib->flags & AMDGPU_IB_FLAG_PREAMBLE) {
parser->job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT;
if (!parser->ctx->preamble_presented) {
parser->job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT_FIRST;
parser->ctx->preamble_presented = true;
}
}
if (parser->job->ring && parser->job->ring != ring)
return -EINVAL;
......@@ -849,7 +1007,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
}
job->owner = p->filp;
job->ctx = entity->fence_context;
job->fence_ctx = entity->fence_context;
p->fence = fence_get(&job->base.s_fence->finished);
cs->out.handle = amdgpu_ctx_add_fence(p->ctx, ring, p->fence);
job->uf_sequence = cs->out.handle;
......@@ -1015,3 +1173,29 @@ amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser,
return NULL;
}
/**
* amdgpu_cs_sysvm_access_required - make BOs accessible by the system VM
*
* @parser: command submission parser context
*
* Helper for UVD/VCE VM emulation, make sure BOs are accessible by the system VM.
*/
int amdgpu_cs_sysvm_access_required(struct amdgpu_cs_parser *parser)
{
unsigned i;
int r;
if (!parser->bo_list)
return 0;
for (i = 0; i < parser->bo_list->num_entries; i++) {
struct amdgpu_bo *bo = parser->bo_list->array[i].robj;
r = amdgpu_ttm_bind(&bo->tbo, &bo->tbo.mem);
if (unlikely(r))
return r;
}
return 0;
}
......@@ -60,6 +60,7 @@ static int amdgpu_ctx_init(struct amdgpu_device *adev, struct amdgpu_ctx *ctx)
amd_sched_entity_fini(&adev->rings[j]->sched,
&ctx->rings[j].entity);
kfree(ctx->fences);
ctx->fences = NULL;
return r;
}
return 0;
......@@ -77,6 +78,7 @@ static void amdgpu_ctx_fini(struct amdgpu_ctx *ctx)
for (j = 0; j < amdgpu_sched_jobs; ++j)
fence_put(ctx->rings[i].fences[j]);
kfree(ctx->fences);
ctx->fences = NULL;
for (i = 0; i < adev->num_rings; i++)
amd_sched_entity_fini(&adev->rings[i]->sched,
......
......@@ -454,6 +454,7 @@ void amdgpu_fence_driver_fini(struct amdgpu_device *adev)
for (j = 0; j <= ring->fence_drv.num_fences_mask; ++j)
fence_put(ring->fence_drv.fences[j]);
kfree(ring->fence_drv.fences);
ring->fence_drv.fences = NULL;
ring->fence_drv.initialized = false;
}
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册