mirror of
https://github.com/t2linux/kernel.git
synced 2026-04-30 13:48:59 -07:00
Merge tag 'drm-misc-next-2020-07-22' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v5.9: UAPI Changes: Cross-subsystem Changes: - Convert panel-dsi-cm and ingenic bindings to YAML. - Add lockdep annotations for dma-fence. \o/ - Describe why indefinite fences are a bad idea - Update binding for rocktech jh057n00900. Core Changes: - Add vblank workers. - Use spin_(un)lock_irq instead of the irqsave/restore variants in crtc code. - Add managed vram helpers. - Convert more logging to drm functions. - Replace more http links with https in core and drivers. - Cleanup to ttm iomem functions and implementation. - Remove TTM CMA memtype as it doesn't work correctly. - Remove TTM_MEMTYPE_FLAG_MAPPABLE for many drivers that have no unmappable memory resources. Driver Changes: - Add CRC support to nouveau, using the new vblank workers. - Dithering and atomic state fix for nouveau. - Fixes for Frida FRD350H54004 panel. - Add support for OSD mode (sprite planes), IPU (scaling) and multiple panels/bridges to ingenic. - Use managed vram helpers in ast. - Assorted small fixes to ingenic, i810, mxsfb. - Remove optional unused ttm dummy functions. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/d6bf269e-ccb2-8a7b-fdae-226e9e3f8274@linux.intel.com
This commit is contained in:
@@ -165,6 +165,7 @@ examples:
|
||||
- |
|
||||
|
||||
#include <dt-bindings/clock/imx8mq-clock.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/reset/imx8mq-reset.h>
|
||||
|
||||
@@ -191,12 +192,12 @@ examples:
|
||||
phy-names = "dphy";
|
||||
|
||||
panel@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "rocktech,jh057n00900";
|
||||
reg = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
vcc-supply = <®_2v8_p>;
|
||||
iovcc-supply = <®_1v8_p>;
|
||||
reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>;
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&mipi_dsi_out>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/ingenic,ipu.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ingenic SoCs Image Processing Unit (IPU) devicetree bindings
|
||||
|
||||
maintainers:
|
||||
- Paul Cercueil <paul@crapouillou.net>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- ingenic,jz4725b-ipu
|
||||
- ingenic,jz4760-ipu
|
||||
- items:
|
||||
- const: ingenic,jz4770-ipu
|
||||
- const: ingenic,jz4760-ipu
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: ipu
|
||||
|
||||
patternProperties:
|
||||
"^ports?$":
|
||||
description: OF graph bindings (specified in bindings/graph.txt).
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/jz4770-cgu.h>
|
||||
ipu@13080000 {
|
||||
compatible = "ingenic,jz4770-ipu", "ingenic,jz4760-ipu";
|
||||
reg = <0x13080000 0x800>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <29>;
|
||||
|
||||
clocks = <&cgu JZ4770_CLK_IPU>;
|
||||
clock-names = "ipu";
|
||||
|
||||
port {
|
||||
ipu_ep: endpoint {
|
||||
remote-endpoint = <&lcdc_ep>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
Ingenic JZ47xx LCD driver
|
||||
|
||||
Required properties:
|
||||
- compatible: one of:
|
||||
* ingenic,jz4740-lcd
|
||||
* ingenic,jz4725b-lcd
|
||||
* ingenic,jz4770-lcd
|
||||
- reg: LCD registers location and length
|
||||
- clocks: LCD pixclock and device clock specifiers.
|
||||
The device clock is only required on the JZ4740.
|
||||
- clock-names: "lcd_pclk" and "lcd"
|
||||
- interrupts: Specifies the interrupt line the LCD controller is connected to.
|
||||
|
||||
Example:
|
||||
|
||||
panel {
|
||||
compatible = "sharp,ls020b1dd01d";
|
||||
|
||||
backlight = <&backlight>;
|
||||
power-supply = <&vcc>;
|
||||
|
||||
port {
|
||||
panel_input: endpoint {
|
||||
remote-endpoint = <&panel_output>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
lcd: lcd-controller@13050000 {
|
||||
compatible = "ingenic,jz4725b-lcd";
|
||||
reg = <0x13050000 0x1000>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <31>;
|
||||
|
||||
clocks = <&cgu JZ4725B_CLK_LCD>;
|
||||
clock-names = "lcd";
|
||||
|
||||
port {
|
||||
panel_output: endpoint {
|
||||
remote-endpoint = <&panel_input>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/ingenic,lcd.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ingenic SoCs LCD controller devicetree bindings
|
||||
|
||||
maintainers:
|
||||
- Paul Cercueil <paul@crapouillou.net>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^lcd-controller@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- ingenic,jz4740-lcd
|
||||
- ingenic,jz4725b-lcd
|
||||
- ingenic,jz4770-lcd
|
||||
- ingenic,jz4780-lcd
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Pixel clock
|
||||
- description: Module clock
|
||||
minItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: lcd_pclk
|
||||
- const: lcd
|
||||
minItems: 1
|
||||
|
||||
port:
|
||||
description: OF graph bindings (specified in bindings/graph.txt).
|
||||
|
||||
ports:
|
||||
description: OF graph bindings (specified in bindings/graph.txt).
|
||||
type: object
|
||||
properties:
|
||||
port@0:
|
||||
type: object
|
||||
description: DPI output, to interface with TFT panels.
|
||||
|
||||
port@8:
|
||||
type: object
|
||||
description: Link to the Image Processing Unit (IPU).
|
||||
(See ingenic,ipu.yaml).
|
||||
|
||||
required:
|
||||
- port@0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ingenic,jz4740-lcd
|
||||
- ingenic,jz4780-lcd
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 2
|
||||
clock-names:
|
||||
minItems: 2
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/jz4740-cgu.h>
|
||||
lcd-controller@13050000 {
|
||||
compatible = "ingenic,jz4740-lcd";
|
||||
reg = <0x13050000 0x1000>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <30>;
|
||||
|
||||
clocks = <&cgu JZ4740_CLK_LCD_PCLK>, <&cgu JZ4740_CLK_LCD>;
|
||||
clock-names = "lcd_pclk", "lcd";
|
||||
|
||||
port {
|
||||
endpoint {
|
||||
remote-endpoint = <&panel_input>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/clock/jz4725b-cgu.h>
|
||||
lcd-controller@13050000 {
|
||||
compatible = "ingenic,jz4725b-lcd";
|
||||
reg = <0x13050000 0x1000>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <31>;
|
||||
|
||||
clocks = <&cgu JZ4725B_CLK_LCD>;
|
||||
clock-names = "lcd_pclk";
|
||||
|
||||
port {
|
||||
endpoint {
|
||||
remote-endpoint = <&panel_input>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
Generic MIPI DSI Command Mode Panel
|
||||
===================================
|
||||
|
||||
Required properties:
|
||||
- compatible: "panel-dsi-cm"
|
||||
|
||||
Optional properties:
|
||||
- label: a symbolic name for the panel
|
||||
- reset-gpios: panel reset gpio
|
||||
- te-gpios: panel TE gpio
|
||||
|
||||
Required nodes:
|
||||
- Video port for DSI input
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
lcd0: display {
|
||||
compatible = "tpo,taal", "panel-dsi-cm";
|
||||
label = "lcd0";
|
||||
|
||||
reset-gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
port {
|
||||
lcd0_in: endpoint {
|
||||
remote-endpoint = <&dsi1_out_ep>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/panel-dsi-cm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: DSI command mode panels
|
||||
|
||||
maintainers:
|
||||
- Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||||
- Sebastian Reichel <sre@kernel.org>
|
||||
|
||||
description: |
|
||||
This binding file is a collection of the DSI panels that
|
||||
are usually driven in command mode. If no backlight is
|
||||
referenced via the optional backlight property, the DSI
|
||||
panel is assumed to have native backlight support.
|
||||
The panel may use an OF graph binding for the association
|
||||
to the display, or it may be a direct child node of the
|
||||
display.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- motorola,droid4-panel # Panel from Motorola Droid4 phone
|
||||
- nokia,himalaya # Panel from Nokia N950 phone
|
||||
- tpo,taal # Panel from OMAP4 SDP board
|
||||
- const: panel-dsi-cm # Generic DSI command mode panel compatible fallback
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: DSI virtual channel
|
||||
|
||||
vddi-supply:
|
||||
description:
|
||||
Display panels require power to be supplied. While several panels need
|
||||
more than one power supply with panel-specific constraints governing the
|
||||
order and timings of the power supplies, in many cases a single power
|
||||
supply is sufficient, either because the panel has a single power rail, or
|
||||
because all its power rails can be driven by the same supply. In that case
|
||||
the vddi-supply property specifies the supply powering the panel as a
|
||||
phandle to a regulator.
|
||||
|
||||
vpnl-supply:
|
||||
description:
|
||||
When the display panel needs a second power supply, this property can be
|
||||
used in addition to vddi-supply. Both supplies will be enabled at the
|
||||
same time before the panel is being accessed.
|
||||
|
||||
width-mm: true
|
||||
height-mm: true
|
||||
label: true
|
||||
rotation: true
|
||||
panel-timing: true
|
||||
port: true
|
||||
reset-gpios: true
|
||||
te-gpios: true
|
||||
backlight: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi-controller {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "tpo,taal", "panel-dsi-cm";
|
||||
reg = <0>;
|
||||
reset-gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -24,6 +24,7 @@ properties:
|
||||
# Xingbangda XBD599 5.99" 720x1440 TFT LCD panel
|
||||
- xingbangda,xbd599
|
||||
|
||||
port: true
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: DSI virtual channel
|
||||
|
||||
@@ -133,6 +133,18 @@ DMA Fences
|
||||
.. kernel-doc:: drivers/dma-buf/dma-fence.c
|
||||
:doc: DMA fences overview
|
||||
|
||||
DMA Fence Cross-Driver Contract
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. kernel-doc:: drivers/dma-buf/dma-fence.c
|
||||
:doc: fence cross-driver contract
|
||||
|
||||
DMA Fence Signalling Annotations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. kernel-doc:: drivers/dma-buf/dma-fence.c
|
||||
:doc: fence signalling annotation
|
||||
|
||||
DMA Fences Functions Reference
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -166,3 +178,73 @@ DMA Fence uABI/Sync File
|
||||
.. kernel-doc:: include/linux/sync_file.h
|
||||
:internal:
|
||||
|
||||
Indefinite DMA Fences
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
At various times &dma_fence with an indefinite time until dma_fence_wait()
|
||||
finishes have been proposed. Examples include:
|
||||
|
||||
* Future fences, used in HWC1 to signal when a buffer isn't used by the display
|
||||
any longer, and created with the screen update that makes the buffer visible.
|
||||
The time this fence completes is entirely under userspace's control.
|
||||
|
||||
* Proxy fences, proposed to handle &drm_syncobj for which the fence has not yet
|
||||
been set. Used to asynchronously delay command submission.
|
||||
|
||||
* Userspace fences or gpu futexes, fine-grained locking within a command buffer
|
||||
that userspace uses for synchronization across engines or with the CPU, which
|
||||
are then imported as a DMA fence for integration into existing winsys
|
||||
protocols.
|
||||
|
||||
* Long-running compute command buffers, while still using traditional end of
|
||||
batch DMA fences for memory management instead of context preemption DMA
|
||||
fences which get reattached when the compute job is rescheduled.
|
||||
|
||||
Common to all these schemes is that userspace controls the dependencies of these
|
||||
fences and controls when they fire. Mixing indefinite fences with normal
|
||||
in-kernel DMA fences does not work, even when a fallback timeout is included to
|
||||
protect against malicious userspace:
|
||||
|
||||
* Only the kernel knows about all DMA fence dependencies, userspace is not aware
|
||||
of dependencies injected due to memory management or scheduler decisions.
|
||||
|
||||
* Only userspace knows about all dependencies in indefinite fences and when
|
||||
exactly they will complete, the kernel has no visibility.
|
||||
|
||||
Furthermore the kernel has to be able to hold up userspace command submission
|
||||
for memory management needs, which means we must support indefinite fences being
|
||||
dependent upon DMA fences. If the kernel also support indefinite fences in the
|
||||
kernel like a DMA fence, like any of the above proposal would, there is the
|
||||
potential for deadlocks.
|
||||
|
||||
.. kernel-render:: DOT
|
||||
:alt: Indefinite Fencing Dependency Cycle
|
||||
:caption: Indefinite Fencing Dependency Cycle
|
||||
|
||||
digraph "Fencing Cycle" {
|
||||
node [shape=box bgcolor=grey style=filled]
|
||||
kernel [label="Kernel DMA Fences"]
|
||||
userspace [label="userspace controlled fences"]
|
||||
kernel -> userspace [label="memory management"]
|
||||
userspace -> kernel [label="Future fence, fence proxy, ..."]
|
||||
|
||||
{ rank=same; kernel userspace }
|
||||
}
|
||||
|
||||
This means that the kernel might accidentally create deadlocks
|
||||
through memory management dependencies which userspace is unaware of, which
|
||||
randomly hangs workloads until the timeout kicks in. Workloads, which from
|
||||
userspace's perspective, do not contain a deadlock. In such a mixed fencing
|
||||
architecture there is no single entity with knowledge of all dependencies.
|
||||
Thefore preventing such deadlocks from within the kernel is not possible.
|
||||
|
||||
The only solution to avoid dependencies loops is by not allowing indefinite
|
||||
fences in the kernel. This means:
|
||||
|
||||
* No future fences, proxy fences or userspace fences imported as DMA fences,
|
||||
with or without a timeout.
|
||||
|
||||
* No DMA fences that signal end of batchbuffer for command submission where
|
||||
userspace is allowed to use userspace fencing or long running compute
|
||||
workloads. This also means no implicit fencing for shared buffers in these
|
||||
cases.
|
||||
|
||||
@@ -127,7 +127,7 @@ At least on the EP9315 there is a silicon bug which causes bit 27 of
|
||||
the VIDSCRNPAGE (framebuffer physical offset) to be tied low. There is
|
||||
an unofficial errata for this bug at::
|
||||
|
||||
http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2
|
||||
https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2
|
||||
|
||||
By default the EP93xx framebuffer driver checks if the allocated physical
|
||||
address has bit 27 set. If it does, then the memory is freed and an
|
||||
|
||||
@@ -543,3 +543,18 @@ Vertical Blanking and Interrupt Handling Functions Reference
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vblank.c
|
||||
:export:
|
||||
|
||||
Vertical Blank Work
|
||||
===================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vblank_work.c
|
||||
:doc: vblank works
|
||||
|
||||
Vertical Blank Work Functions Reference
|
||||
---------------------------------------
|
||||
|
||||
.. kernel-doc:: include/drm/drm_vblank_work.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vblank_work.c
|
||||
:export:
|
||||
|
||||
@@ -185,7 +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.
|
||||
|
||||
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) https://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347
|
||||
1) https://lists.freedesktop.org/archives/xorg/2005-March/006663.html
|
||||
2) https://lists.freedesktop.org/archives/xorg/2005-March/006745.html
|
||||
3) https://lists.freedesktop.org/archives/xorg/2007-October/029507.html
|
||||
|
||||
@@ -64,6 +64,52 @@ static atomic64_t dma_fence_context_counter = ATOMIC64_INIT(1);
|
||||
* &dma_buf.resv pointer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: fence cross-driver contract
|
||||
*
|
||||
* Since &dma_fence provide a cross driver contract, all drivers must follow the
|
||||
* same rules:
|
||||
*
|
||||
* * Fences must complete in a reasonable time. Fences which represent kernels
|
||||
* and shaders submitted by userspace, which could run forever, must be backed
|
||||
* up by timeout and gpu hang recovery code. Minimally that code must prevent
|
||||
* further command submission and force complete all in-flight fences, e.g.
|
||||
* when the driver or hardware do not support gpu reset, or if the gpu reset
|
||||
* failed for some reason. Ideally the driver supports gpu recovery which only
|
||||
* affects the offending userspace context, and no other userspace
|
||||
* submissions.
|
||||
*
|
||||
* * Drivers may have different ideas of what completion within a reasonable
|
||||
* time means. Some hang recovery code uses a fixed timeout, others a mix
|
||||
* between observing forward progress and increasingly strict timeouts.
|
||||
* Drivers should not try to second guess timeout handling of fences from
|
||||
* other drivers.
|
||||
*
|
||||
* * To ensure there's no deadlocks of dma_fence_wait() against other locks
|
||||
* drivers should annotate all code required to reach dma_fence_signal(),
|
||||
* which completes the fences, with dma_fence_begin_signalling() and
|
||||
* dma_fence_end_signalling().
|
||||
*
|
||||
* * Drivers are allowed to call dma_fence_wait() while holding dma_resv_lock().
|
||||
* This means any code required for fence completion cannot acquire a
|
||||
* &dma_resv lock. Note that this also pulls in the entire established
|
||||
* locking hierarchy around dma_resv_lock() and dma_resv_unlock().
|
||||
*
|
||||
* * Drivers are allowed to call dma_fence_wait() from their &shrinker
|
||||
* callbacks. This means any code required for fence completion cannot
|
||||
* allocate memory with GFP_KERNEL.
|
||||
*
|
||||
* * Drivers are allowed to call dma_fence_wait() from their &mmu_notifier
|
||||
* respectively &mmu_interval_notifier callbacks. This means any code required
|
||||
* for fence completeion cannot allocate memory with GFP_NOFS or GFP_NOIO.
|
||||
* Only GFP_ATOMIC is permissible, which might fail.
|
||||
*
|
||||
* Note that only GPU drivers have a reasonable excuse for both requiring
|
||||
* &mmu_interval_notifier and &shrinker callbacks at the same time as having to
|
||||
* track asynchronous compute work using &dma_fence. No driver outside of
|
||||
* drivers/gpu should ever call dma_fence_wait() in such contexts.
|
||||
*/
|
||||
|
||||
static const char *dma_fence_stub_get_name(struct dma_fence *fence)
|
||||
{
|
||||
return "stub";
|
||||
@@ -110,6 +156,160 @@ u64 dma_fence_context_alloc(unsigned num)
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_context_alloc);
|
||||
|
||||
/**
|
||||
* DOC: fence signalling annotation
|
||||
*
|
||||
* Proving correctness of all the kernel code around &dma_fence through code
|
||||
* review and testing is tricky for a few reasons:
|
||||
*
|
||||
* * It is a cross-driver contract, and therefore all drivers must follow the
|
||||
* same rules for lock nesting order, calling contexts for various functions
|
||||
* and anything else significant for in-kernel interfaces. But it is also
|
||||
* impossible to test all drivers in a single machine, hence brute-force N vs.
|
||||
* N testing of all combinations is impossible. Even just limiting to the
|
||||
* possible combinations is infeasible.
|
||||
*
|
||||
* * There is an enormous amount of driver code involved. For render drivers
|
||||
* there's the tail of command submission, after fences are published,
|
||||
* scheduler code, interrupt and workers to process job completion,
|
||||
* and timeout, gpu reset and gpu hang recovery code. Plus for integration
|
||||
* with core mm with have &mmu_notifier, respectively &mmu_interval_notifier,
|
||||
* and &shrinker. For modesetting drivers there's the commit tail functions
|
||||
* between when fences for an atomic modeset are published, and when the
|
||||
* corresponding vblank completes, including any interrupt processing and
|
||||
* related workers. Auditing all that code, across all drivers, is not
|
||||
* feasible.
|
||||
*
|
||||
* * Due to how many other subsystems are involved and the locking hierarchies
|
||||
* this pulls in there is extremely thin wiggle-room for driver-specific
|
||||
* differences. &dma_fence interacts with almost all of the core memory
|
||||
* handling through page fault handlers via &dma_resv, dma_resv_lock() and
|
||||
* dma_resv_unlock(). On the other side it also interacts through all
|
||||
* allocation sites through &mmu_notifier and &shrinker.
|
||||
*
|
||||
* Furthermore lockdep does not handle cross-release dependencies, which means
|
||||
* any deadlocks between dma_fence_wait() and dma_fence_signal() can't be caught
|
||||
* at runtime with some quick testing. The simplest example is one thread
|
||||
* waiting on a &dma_fence while holding a lock::
|
||||
*
|
||||
* lock(A);
|
||||
* dma_fence_wait(B);
|
||||
* unlock(A);
|
||||
*
|
||||
* while the other thread is stuck trying to acquire the same lock, which
|
||||
* prevents it from signalling the fence the previous thread is stuck waiting
|
||||
* on::
|
||||
*
|
||||
* lock(A);
|
||||
* unlock(A);
|
||||
* dma_fence_signal(B);
|
||||
*
|
||||
* By manually annotating all code relevant to signalling a &dma_fence we can
|
||||
* teach lockdep about these dependencies, which also helps with the validation
|
||||
* headache since now lockdep can check all the rules for us::
|
||||
*
|
||||
* cookie = dma_fence_begin_signalling();
|
||||
* lock(A);
|
||||
* unlock(A);
|
||||
* dma_fence_signal(B);
|
||||
* dma_fence_end_signalling(cookie);
|
||||
*
|
||||
* For using dma_fence_begin_signalling() and dma_fence_end_signalling() to
|
||||
* annotate critical sections the following rules need to be observed:
|
||||
*
|
||||
* * All code necessary to complete a &dma_fence must be annotated, from the
|
||||
* point where a fence is accessible to other threads, to the point where
|
||||
* dma_fence_signal() is called. Un-annotated code can contain deadlock issues,
|
||||
* and due to the very strict rules and many corner cases it is infeasible to
|
||||
* catch these just with review or normal stress testing.
|
||||
*
|
||||
* * &struct dma_resv deserves a special note, since the readers are only
|
||||
* protected by rcu. This means the signalling critical section starts as soon
|
||||
* as the new fences are installed, even before dma_resv_unlock() is called.
|
||||
*
|
||||
* * The only exception are fast paths and opportunistic signalling code, which
|
||||
* calls dma_fence_signal() purely as an optimization, but is not required to
|
||||
* guarantee completion of a &dma_fence. The usual example is a wait IOCTL
|
||||
* which calls dma_fence_signal(), while the mandatory completion path goes
|
||||
* through a hardware interrupt and possible job completion worker.
|
||||
*
|
||||
* * To aid composability of code, the annotations can be freely nested, as long
|
||||
* as the overall locking hierarchy is consistent. The annotations also work
|
||||
* both in interrupt and process context. Due to implementation details this
|
||||
* requires that callers pass an opaque cookie from
|
||||
* dma_fence_begin_signalling() to dma_fence_end_signalling().
|
||||
*
|
||||
* * Validation against the cross driver contract is implemented by priming
|
||||
* lockdep with the relevant hierarchy at boot-up. This means even just
|
||||
* testing with a single device is enough to validate a driver, at least as
|
||||
* far as deadlocks with dma_fence_wait() against dma_fence_signal() are
|
||||
* concerned.
|
||||
*/
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
struct lockdep_map dma_fence_lockdep_map = {
|
||||
.name = "dma_fence_map"
|
||||
};
|
||||
|
||||
/**
|
||||
* dma_fence_begin_signalling - begin a critical DMA fence signalling section
|
||||
*
|
||||
* Drivers should use this to annotate the beginning of any code section
|
||||
* required to eventually complete &dma_fence by calling dma_fence_signal().
|
||||
*
|
||||
* The end of these critical sections are annotated with
|
||||
* dma_fence_end_signalling().
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* Opaque cookie needed by the implementation, which needs to be passed to
|
||||
* dma_fence_end_signalling().
|
||||
*/
|
||||
bool dma_fence_begin_signalling(void)
|
||||
{
|
||||
/* explicitly nesting ... */
|
||||
if (lock_is_held_type(&dma_fence_lockdep_map, 1))
|
||||
return true;
|
||||
|
||||
/* rely on might_sleep check for soft/hardirq locks */
|
||||
if (in_atomic())
|
||||
return true;
|
||||
|
||||
/* ... and non-recursive readlock */
|
||||
lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _RET_IP_);
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_begin_signalling);
|
||||
|
||||
/**
|
||||
* dma_fence_end_signalling - end a critical DMA fence signalling section
|
||||
*
|
||||
* Closes a critical section annotation opened by dma_fence_begin_signalling().
|
||||
*/
|
||||
void dma_fence_end_signalling(bool cookie)
|
||||
{
|
||||
if (cookie)
|
||||
return;
|
||||
|
||||
lock_release(&dma_fence_lockdep_map, _RET_IP_);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_end_signalling);
|
||||
|
||||
void __dma_fence_might_wait(void)
|
||||
{
|
||||
bool tmp;
|
||||
|
||||
tmp = lock_is_held_type(&dma_fence_lockdep_map, 1);
|
||||
if (tmp)
|
||||
lock_release(&dma_fence_lockdep_map, _THIS_IP_);
|
||||
lock_map_acquire(&dma_fence_lockdep_map);
|
||||
lock_map_release(&dma_fence_lockdep_map);
|
||||
if (tmp)
|
||||
lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _THIS_IP_);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* dma_fence_signal_locked - signal completion of a fence
|
||||
* @fence: the fence to signal
|
||||
@@ -170,14 +370,19 @@ int dma_fence_signal(struct dma_fence *fence)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
bool tmp;
|
||||
|
||||
if (!fence)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = dma_fence_begin_signalling();
|
||||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
ret = dma_fence_signal_locked(fence);
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
|
||||
dma_fence_end_signalling(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_fence_signal);
|
||||
@@ -210,6 +415,8 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
|
||||
|
||||
might_sleep();
|
||||
|
||||
__dma_fence_might_wait();
|
||||
|
||||
trace_dma_fence_wait_start(fence);
|
||||
if (fence->ops->wait)
|
||||
ret = fence->ops->wait(fence, intr, timeout);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/mmu_notifier.h>
|
||||
|
||||
/**
|
||||
* DOC: Reservation Object Overview
|
||||
@@ -116,6 +117,13 @@ static int __init dma_resv_lockdep(void)
|
||||
if (ret == -EDEADLK)
|
||||
dma_resv_lock_slow(&obj, &ctx);
|
||||
fs_reclaim_acquire(GFP_KERNEL);
|
||||
#ifdef CONFIG_MMU_NOTIFIER
|
||||
lock_map_acquire(&__mmu_notifier_invalidate_range_start_map);
|
||||
__dma_fence_might_wait();
|
||||
lock_map_release(&__mmu_notifier_invalidate_range_start_map);
|
||||
#else
|
||||
__dma_fence_might_wait();
|
||||
#endif
|
||||
fs_reclaim_release(GFP_KERNEL);
|
||||
ww_mutex_unlock(&obj.lock);
|
||||
ww_acquire_fini(&ctx);
|
||||
|
||||
@@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_cache.o \
|
||||
drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \
|
||||
drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \
|
||||
drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \
|
||||
drm_managed.o
|
||||
drm_managed.o drm_vblank_work.o
|
||||
|
||||
drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o
|
||||
drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
|
||||
|
||||
@@ -94,7 +94,7 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
||||
man->func = &amdgpu_gtt_mgr_func;
|
||||
man->available_caching = TTM_PL_MASK_CACHING;
|
||||
man->default_caching = TTM_PL_FLAG_CACHED;
|
||||
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA;
|
||||
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
|
||||
break;
|
||||
case TTM_PL_VRAM:
|
||||
/* "On-card" video ram */
|
||||
@@ -109,7 +109,7 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
|
||||
case AMDGPU_PL_OA:
|
||||
/* On-chip GDS memory*/
|
||||
man->func = &ttm_bo_manager_func;
|
||||
man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_CMA;
|
||||
man->flags = TTM_MEMTYPE_FLAG_FIXED;
|
||||
man->available_caching = TTM_PL_FLAG_UNCACHED;
|
||||
man->default_caching = TTM_PL_FLAG_UNCACHED;
|
||||
break;
|
||||
@@ -837,10 +837,6 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void amdgpu_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
|
||||
{
|
||||
}
|
||||
|
||||
static unsigned long amdgpu_ttm_io_mem_pfn(struct ttm_buffer_object *bo,
|
||||
unsigned long page_offset)
|
||||
{
|
||||
@@ -1755,7 +1751,6 @@ static struct ttm_bo_driver amdgpu_bo_driver = {
|
||||
.release_notify = &amdgpu_bo_release_notify,
|
||||
.fault_reserve_notify = &amdgpu_bo_fault_reserve_notify,
|
||||
.io_mem_reserve = &amdgpu_ttm_io_mem_reserve,
|
||||
.io_mem_free = &amdgpu_ttm_io_mem_free,
|
||||
.io_mem_pfn = amdgpu_ttm_io_mem_pfn,
|
||||
.access_memory = &amdgpu_ttm_access_memory,
|
||||
.del_from_lru_notify = &amdgpu_vm_del_from_lru_notify
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Makefile for the drm device driver. This driver provides support for the
|
||||
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
|
||||
|
||||
ast-y := ast_cursor.o ast_drv.o ast_main.o ast_mode.o ast_ttm.o ast_post.o \
|
||||
ast-y := ast_cursor.o ast_drv.o ast_main.o ast_mm.o ast_mode.o ast_post.o \
|
||||
ast_dp501.o
|
||||
|
||||
obj-$(CONFIG_DRM_AST) := ast.o
|
||||
|
||||
@@ -110,7 +110,6 @@ struct ast_private {
|
||||
uint32_t dram_bus_width;
|
||||
uint32_t dram_type;
|
||||
uint32_t mclk;
|
||||
uint32_t vram_size;
|
||||
|
||||
int fb_mtrr;
|
||||
|
||||
@@ -292,7 +291,6 @@ int ast_mode_config_init(struct ast_private *ast);
|
||||
#define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1)
|
||||
|
||||
int ast_mm_init(struct ast_private *ast);
|
||||
void ast_mm_fini(struct ast_private *ast);
|
||||
|
||||
/* ast post */
|
||||
void ast_enable_vga(struct drm_device *dev);
|
||||
|
||||
@@ -378,38 +378,6 @@ static int ast_get_dram_info(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 ast_get_vram_info(struct drm_device *dev)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
u8 jreg;
|
||||
u32 vram_size;
|
||||
ast_open_key(ast);
|
||||
|
||||
vram_size = AST_VIDMEM_DEFAULT_SIZE;
|
||||
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff);
|
||||
switch (jreg & 3) {
|
||||
case 0: vram_size = AST_VIDMEM_SIZE_8M; break;
|
||||
case 1: vram_size = AST_VIDMEM_SIZE_16M; break;
|
||||
case 2: vram_size = AST_VIDMEM_SIZE_32M; break;
|
||||
case 3: vram_size = AST_VIDMEM_SIZE_64M; break;
|
||||
}
|
||||
|
||||
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff);
|
||||
switch (jreg & 0x03) {
|
||||
case 1:
|
||||
vram_size -= 0x100000;
|
||||
break;
|
||||
case 2:
|
||||
vram_size -= 0x200000;
|
||||
break;
|
||||
case 3:
|
||||
vram_size -= 0x400000;
|
||||
break;
|
||||
}
|
||||
|
||||
return vram_size;
|
||||
}
|
||||
|
||||
int ast_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
struct ast_private *ast;
|
||||
@@ -450,16 +418,14 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
ast_detect_chip(dev, &need_post);
|
||||
|
||||
if (need_post)
|
||||
ast_post_gpu(dev);
|
||||
|
||||
ret = ast_get_dram_info(dev);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
ast->vram_size = ast_get_vram_info(dev);
|
||||
drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d size=%08x\n",
|
||||
ast->mclk, ast->dram_type,
|
||||
ast->dram_bus_width, ast->vram_size);
|
||||
drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n",
|
||||
ast->mclk, ast->dram_type, ast->dram_bus_width);
|
||||
|
||||
if (need_post)
|
||||
ast_post_gpu(dev);
|
||||
|
||||
ret = ast_mm_init(ast);
|
||||
if (ret)
|
||||
@@ -486,6 +452,5 @@ void ast_driver_unload(struct drm_device *dev)
|
||||
ast_release_firmware(dev);
|
||||
kfree(ast->dp501_fw_addr);
|
||||
|
||||
ast_mm_fini(ast);
|
||||
kfree(ast);
|
||||
}
|
||||
|
||||
@@ -28,22 +28,72 @@
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_gem_vram_helper.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "ast_drv.h"
|
||||
|
||||
static u32 ast_get_vram_size(struct ast_private *ast)
|
||||
{
|
||||
u8 jreg;
|
||||
u32 vram_size;
|
||||
|
||||
ast_open_key(ast);
|
||||
|
||||
vram_size = AST_VIDMEM_DEFAULT_SIZE;
|
||||
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff);
|
||||
switch (jreg & 3) {
|
||||
case 0:
|
||||
vram_size = AST_VIDMEM_SIZE_8M;
|
||||
break;
|
||||
case 1:
|
||||
vram_size = AST_VIDMEM_SIZE_16M;
|
||||
break;
|
||||
case 2:
|
||||
vram_size = AST_VIDMEM_SIZE_32M;
|
||||
break;
|
||||
case 3:
|
||||
vram_size = AST_VIDMEM_SIZE_64M;
|
||||
break;
|
||||
}
|
||||
|
||||
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff);
|
||||
switch (jreg & 0x03) {
|
||||
case 1:
|
||||
vram_size -= 0x100000;
|
||||
break;
|
||||
case 2:
|
||||
vram_size -= 0x200000;
|
||||
break;
|
||||
case 3:
|
||||
vram_size -= 0x400000;
|
||||
break;
|
||||
}
|
||||
|
||||
return vram_size;
|
||||
}
|
||||
|
||||
static void ast_mm_release(struct drm_device *dev, void *ptr)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
|
||||
arch_phys_wc_del(ast->fb_mtrr);
|
||||
arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0),
|
||||
pci_resource_len(dev->pdev, 0));
|
||||
}
|
||||
|
||||
int ast_mm_init(struct ast_private *ast)
|
||||
{
|
||||
struct drm_vram_mm *vmm;
|
||||
u32 vram_size;
|
||||
int ret;
|
||||
struct drm_device *dev = ast->dev;
|
||||
|
||||
vmm = drm_vram_helper_alloc_mm(
|
||||
dev, pci_resource_start(dev->pdev, 0),
|
||||
ast->vram_size);
|
||||
if (IS_ERR(vmm)) {
|
||||
ret = PTR_ERR(vmm);
|
||||
vram_size = ast_get_vram_size(ast);
|
||||
|
||||
ret = drmm_vram_helper_init(dev, pci_resource_start(dev->pdev, 0),
|
||||
vram_size);
|
||||
if (ret) {
|
||||
drm_err(dev, "Error initializing VRAM MM; %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -53,16 +103,5 @@ int ast_mm_init(struct ast_private *ast)
|
||||
ast->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0),
|
||||
pci_resource_len(dev->pdev, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ast_mm_fini(struct ast_private *ast)
|
||||
{
|
||||
struct drm_device *dev = ast->dev;
|
||||
|
||||
drm_vram_helper_release_mm(dev);
|
||||
|
||||
arch_phys_wc_del(ast->fb_mtrr);
|
||||
arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0),
|
||||
pci_resource_len(dev->pdev, 0));
|
||||
return drmm_add_action_or_reset(dev, ast_mm_release, NULL);
|
||||
}
|
||||
@@ -1844,9 +1844,7 @@ static void connector_bad_edid(struct drm_connector *connector,
|
||||
if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
|
||||
return;
|
||||
|
||||
dev_warn(connector->dev->dev,
|
||||
"%s: EDID is invalid:\n",
|
||||
connector->name);
|
||||
drm_warn(connector->dev, "%s: EDID is invalid:\n", connector->name);
|
||||
for (i = 0; i < num_blocks; i++) {
|
||||
u8 *block = edid + i * EDID_LENGTH;
|
||||
char prefix[20];
|
||||
@@ -5298,7 +5296,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
|
||||
}
|
||||
if (!drm_edid_is_valid(edid)) {
|
||||
clear_eld(connector);
|
||||
dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
|
||||
drm_warn(connector->dev, "%s: EDID invalid.\n",
|
||||
connector->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user