Merge tag 'drm-misc-next-2025-03-06' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v6.15: Cross-subsystem Changes: base: - component: Provide helper to query bound status fbdev: - fbtft: Remove access to page->index Core Changes: - Fix usage of logging macros in several places gem: - Add test function for imported dma-bufs and use it in core and helpers - Avoid struct drm_gem_object.import_attach tests: - Fix lockdep warnings ttm: - Add helpers for TTM shrinker Driver Changes: adp: - Add support for Apple Touch Bar displays on M1/M2 amdxdna: - Fix interrupt handling appletbdrm: - Add support for Apple Touch Bar displays on x86 bridge: - synopsys: Add HDMI audio support - ti-sn65dsi83: Support negative DE polarity ipu-v3: - Remove unused code nouveau: - Avoid multiple -Wflex-array-member-not-at-end warnings panthor: - Fix CS_STATUS_ defines - Improve locking rockchip: - analogix_dp: Add eDP support - lvds: Improve logging - vop2: Improve HDMI mode handling; Add support for RK3576 - Fix shutdown - Support rk3562-mali xe: - Use TTM shrinker Signed-off-by: Dave Airlie <airlied@redhat.com> From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20250306130700.GA485504@linux.fritz.box
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/apple,h7-display-pipe-mipi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple pre-DCP display controller MIPI interface
|
||||
|
||||
maintainers:
|
||||
- Sasha Finkelstein <fnkl.kernel@gmail.com>
|
||||
|
||||
description:
|
||||
The MIPI controller part of the pre-DCP Apple display controller
|
||||
|
||||
allOf:
|
||||
- $ref: dsi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t8112-display-pipe-mipi
|
||||
- apple,t8103-display-pipe-mipi
|
||||
- const: apple,h7-display-pipe-mipi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Input port. Always connected to the primary controller
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Output MIPI DSI port to the panel
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- ports
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
dsi@28200000 {
|
||||
compatible = "apple,t8103-display-pipe-mipi", "apple,h7-display-pipe-mipi";
|
||||
reg = <0x28200000 0xc000>;
|
||||
power-domains = <&ps_dispdfr_mipi>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
dfr_adp_out_mipi: endpoint {
|
||||
remote-endpoint = <&dfr_adp_out_mipi>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
dfr_panel_in: endpoint {
|
||||
remote-endpoint = <&dfr_mipi_out_panel>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -0,0 +1,88 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/apple,h7-display-pipe.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple pre-DCP display controller
|
||||
|
||||
maintainers:
|
||||
- Sasha Finkelstein <fnkl.kernel@gmail.com>
|
||||
|
||||
description:
|
||||
A secondary display controller used to drive the "touchbar" on
|
||||
certain Apple laptops.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t8112-display-pipe
|
||||
- apple,t8103-display-pipe
|
||||
- const: apple,h7-display-pipe
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: Primary register block, controls planes and blending
|
||||
- description:
|
||||
Contains other configuration registers like interrupt
|
||||
and FIFO control
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: be
|
||||
- const: fe
|
||||
|
||||
power-domains:
|
||||
description:
|
||||
Phandles to pmgr entries that are needed for this controller to turn on.
|
||||
Aside from that, their specific functions are unknown
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: Unknown function
|
||||
- description: Primary interrupt. Vsync events are reported via it
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: be
|
||||
- const: fe
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Output port. Always connected to apple,h7-display-pipe-mipi
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- port
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/apple-aic.h>
|
||||
display-pipe@28200000 {
|
||||
compatible = "apple,t8103-display-pipe", "apple,h7-display-pipe";
|
||||
reg = <0x28200000 0xc000>,
|
||||
<0x28400000 0x4000>;
|
||||
reg-names = "be", "fe";
|
||||
power-domains = <&ps_dispdfr_fe>, <&ps_dispdfr_be>;
|
||||
interrupt-parent = <&aic>;
|
||||
interrupts = <AIC_IRQ 502 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<AIC_IRQ 506 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "be", "fe";
|
||||
iommus = <&displaydfr_dart 0>;
|
||||
|
||||
port {
|
||||
dfr_adp_out_mipi: endpoint {
|
||||
remote-endpoint = <&dfr_mipi_in_adp>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -0,0 +1,58 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/apple,summit.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Apple "Summit" display panel
|
||||
|
||||
maintainers:
|
||||
- Sasha Finkelstein <fnkl.kernel@gmail.com>
|
||||
|
||||
description:
|
||||
An OLED panel used as a touchbar on certain Apple laptops.
|
||||
Contains a backlight device, which controls brightness of the panel itself.
|
||||
The backlight common properties are included for this reason
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
- $ref: /schemas/leds/backlight/common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,j293-summit
|
||||
- apple,j493-summit
|
||||
- const: apple,summit
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- max-brightness
|
||||
- port
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "apple,j293-summit", "apple,summit";
|
||||
reg = <0>;
|
||||
max-brightness = <255>;
|
||||
|
||||
port {
|
||||
endpoint {
|
||||
remote-endpoint = <&dfr_bridge_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -14,12 +14,14 @@ description:
|
||||
maintainers:
|
||||
- Sandy Huang <hjc@rock-chips.com>
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
- Andy Yan <andyshrk@163.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- rockchip,rk3566-vop
|
||||
- rockchip,rk3568-vop
|
||||
- rockchip,rk3576-vop
|
||||
- rockchip,rk3588-vop
|
||||
|
||||
reg:
|
||||
@@ -37,10 +39,21 @@ properties:
|
||||
- const: gamma-lut
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
description:
|
||||
The VOP interrupt is shared by several interrupt sources, such as
|
||||
frame start (VSYNC), line flag and other status interrupts.
|
||||
For VOP version under rk3576, the interrupt is shared by several interrupt
|
||||
sources, such as frame start (VSYNC), line flag and other interrupt status.
|
||||
For VOP version from rk3576 there is a system interrupt for bus error, and
|
||||
every video port has it's independent interrupts for vsync and other video
|
||||
port related error interrupts.
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: sys
|
||||
- const: vp0
|
||||
- const: vp1
|
||||
- const: vp2
|
||||
|
||||
# See compatible-specific constraints below.
|
||||
clocks:
|
||||
@@ -120,6 +133,72 @@ required:
|
||||
- ports
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- rockchip,rk3566-vop
|
||||
- rockchip,rk3568-vop
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 5
|
||||
|
||||
clock-names:
|
||||
maxItems: 5
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names: false
|
||||
|
||||
ports:
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
- port@2
|
||||
|
||||
rockchip,vo1-grf: false
|
||||
rockchip,vop-grf: false
|
||||
rockchip,pmu: false
|
||||
|
||||
required:
|
||||
- rockchip,grf
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- rockchip,rk3576-vop
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 5
|
||||
|
||||
clock-names:
|
||||
maxItems: 5
|
||||
|
||||
interrupts:
|
||||
minItems: 4
|
||||
|
||||
interrupt-names:
|
||||
minItems: 4
|
||||
|
||||
ports:
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
- port@2
|
||||
|
||||
rockchip,vo1-grf: false
|
||||
rockchip,vop-grf: false
|
||||
|
||||
required:
|
||||
- rockchip,grf
|
||||
- rockchip,pmu
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@@ -129,8 +208,16 @@ allOf:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 7
|
||||
maxItems: 9
|
||||
|
||||
clock-names:
|
||||
minItems: 7
|
||||
maxItems: 9
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names: false
|
||||
|
||||
ports:
|
||||
required:
|
||||
@@ -145,23 +232,6 @@ allOf:
|
||||
- rockchip,vop-grf
|
||||
- rockchip,pmu
|
||||
|
||||
else:
|
||||
properties:
|
||||
rockchip,vo1-grf: false
|
||||
rockchip,vop-grf: false
|
||||
rockchip,pmu: false
|
||||
|
||||
clocks:
|
||||
maxItems: 5
|
||||
clock-names:
|
||||
maxItems: 5
|
||||
|
||||
ports:
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
- port@2
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
@@ -188,6 +258,7 @@ examples:
|
||||
"dclk_vp1",
|
||||
"dclk_vp2";
|
||||
power-domains = <&power RK3568_PD_VO>;
|
||||
rockchip,grf = <&grf>;
|
||||
iommus = <&vop_mmu>;
|
||||
vop_out: ports {
|
||||
#address-cells = <1>;
|
||||
|
||||
@@ -25,6 +25,7 @@ properties:
|
||||
- renesas,r9a07g044-mali
|
||||
- renesas,r9a07g054-mali
|
||||
- rockchip,px30-mali
|
||||
- rockchip,rk3562-mali
|
||||
- rockchip,rk3568-mali
|
||||
- rockchip,rk3576-mali
|
||||
- const: arm,mali-bifrost # Mali Bifrost GPU model/revision is fully discoverable
|
||||
|
||||
+24
@@ -7149,6 +7149,14 @@ S: Supported
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/gpu/drm/sun4i/sun8i*
|
||||
|
||||
DRM DRIVER FOR APPLE TOUCH BARS
|
||||
M: Aun-Ali Zaidi <admin@kodeit.net>
|
||||
M: Aditya Garg <gargaditya08@live.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/gpu/drm/tiny/appletbdrm.c
|
||||
|
||||
DRM DRIVER FOR ARM PL111 CLCD
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
S: Maintained
|
||||
@@ -7828,6 +7836,22 @@ F: drivers/gpu/host1x/
|
||||
F: include/linux/host1x.h
|
||||
F: include/uapi/drm/tegra_drm.h
|
||||
|
||||
DRM DRIVERS FOR PRE-DCP APPLE DISPLAY OUTPUT
|
||||
M: Sasha Finkelstein <fnkl.kernel@gmail.com>
|
||||
R: Janne Grunau <j@jannau.net>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: asahi@lists.linux.dev
|
||||
S: Maintained
|
||||
W: https://asahilinux.org
|
||||
B: https://github.com/AsahiLinux/linux/issues
|
||||
C: irc://irc.oftc.net/asahi-dev
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml
|
||||
F: Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml
|
||||
F: Documentation/devicetree/bindings/display/panel/apple,summit.yaml
|
||||
F: drivers/gpu/drm/adp/
|
||||
F: drivers/gpu/drm/panel/panel-summit.c
|
||||
|
||||
DRM DRIVERS FOR RENESAS R-CAR
|
||||
M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
|
||||
M: Tomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
|
||||
|
||||
@@ -349,8 +349,6 @@ static irqreturn_t mailbox_irq_handler(int irq, void *p)
|
||||
trace_mbox_irq_handle(MAILBOX_NAME, irq);
|
||||
/* Schedule a rx_work to call the callback functions */
|
||||
queue_work(mb_chann->work_q, &mb_chann->rx_work);
|
||||
/* Clear IOHUB register */
|
||||
mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@@ -367,6 +365,9 @@ static void mailbox_rx_worker(struct work_struct *rx_work)
|
||||
return;
|
||||
}
|
||||
|
||||
again:
|
||||
mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
|
||||
|
||||
while (1) {
|
||||
/*
|
||||
* If return is 0, keep consuming next message, until there is
|
||||
@@ -380,10 +381,18 @@ static void mailbox_rx_worker(struct work_struct *rx_work)
|
||||
if (unlikely(ret)) {
|
||||
MB_ERR(mb_chann, "Unexpected ret %d, disable irq", ret);
|
||||
WRITE_ONCE(mb_chann->bad_state, true);
|
||||
disable_irq(mb_chann->msix_irq);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The hardware will not generate interrupt if firmware creates a new
|
||||
* response right after driver clears interrupt register. Check
|
||||
* the interrupt register to make sure there is not any new response
|
||||
* before exiting.
|
||||
*/
|
||||
if (mailbox_reg_read(mb_chann, mb_chann->iohub_int_addr))
|
||||
goto again;
|
||||
}
|
||||
|
||||
int xdna_mailbox_send_msg(struct mailbox_channel *mb_chann,
|
||||
|
||||
@@ -569,6 +569,20 @@ void component_master_del(struct device *parent,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_del);
|
||||
|
||||
bool component_master_is_bound(struct device *parent,
|
||||
const struct component_master_ops *ops)
|
||||
{
|
||||
struct aggregate_device *adev;
|
||||
|
||||
guard(mutex)(&component_mutex);
|
||||
adev = __aggregate_find(parent, ops);
|
||||
if (!adev)
|
||||
return 0;
|
||||
|
||||
return adev->bound;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_is_bound);
|
||||
|
||||
static void component_unbind(struct component *component,
|
||||
struct aggregate_device *adev, void *data)
|
||||
{
|
||||
|
||||
@@ -439,6 +439,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/tidss/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/adp/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/xlnx/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/gud/Kconfig"
|
||||
|
||||
@@ -206,6 +206,7 @@ obj-y += mxsfb/
|
||||
obj-y += tiny/
|
||||
obj-$(CONFIG_DRM_PL111) += pl111/
|
||||
obj-$(CONFIG_DRM_TVE200) += tve200/
|
||||
obj-$(CONFIG_DRM_ADP) += adp/
|
||||
obj-$(CONFIG_DRM_XEN) += xen/
|
||||
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
|
||||
obj-$(CONFIG_DRM_LIMA) += lima/
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
config DRM_ADP
|
||||
tristate "DRM Support for pre-DCP Apple display controllers"
|
||||
depends on DRM && OF && ARM64
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_BRIDGE_CONNECTOR
|
||||
select DRM_DISPLAY_HELPER
|
||||
select DRM_KMS_DMA_HELPER
|
||||
select DRM_GEM_DMA_HELPER
|
||||
select DRM_PANEL_BRIDGE
|
||||
select VIDEOMODE_HELPERS
|
||||
select DRM_MIPI_DSI
|
||||
help
|
||||
Chose this option if you have an Apple Arm laptop with a touchbar.
|
||||
|
||||
If M is selected, this module will be called adpdrm.
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
|
||||
adpdrm-y := adp_drv.o
|
||||
adpdrm-mipi-y := adp-mipi.o
|
||||
obj-$(CONFIG_DRM_ADP) += adpdrm.o adpdrm-mipi.o
|
||||
@@ -0,0 +1,276 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/component.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
|
||||
#define DSI_GEN_HDR 0x6c
|
||||
#define DSI_GEN_PLD_DATA 0x70
|
||||
|
||||
#define DSI_CMD_PKT_STATUS 0x74
|
||||
|
||||
#define GEN_PLD_R_EMPTY BIT(4)
|
||||
#define GEN_PLD_W_FULL BIT(3)
|
||||
#define GEN_PLD_W_EMPTY BIT(2)
|
||||
#define GEN_CMD_FULL BIT(1)
|
||||
#define GEN_CMD_EMPTY BIT(0)
|
||||
#define GEN_RD_CMD_BUSY BIT(6)
|
||||
#define CMD_PKT_STATUS_TIMEOUT_US 20000
|
||||
|
||||
struct adp_mipi_drv_private {
|
||||
struct mipi_dsi_host dsi;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next_bridge;
|
||||
void __iomem *mipi;
|
||||
};
|
||||
|
||||
#define mipi_to_adp(x) container_of(x, struct adp_mipi_drv_private, dsi)
|
||||
|
||||
static int adp_dsi_gen_pkt_hdr_write(struct adp_mipi_drv_private *adp, u32 hdr_val)
|
||||
{
|
||||
int ret;
|
||||
u32 val, mask;
|
||||
|
||||
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
|
||||
val, !(val & GEN_CMD_FULL), 1000,
|
||||
CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(adp->dsi.dev, "failed to get available command FIFO\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(hdr_val, adp->mipi + DSI_GEN_HDR);
|
||||
|
||||
mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
|
||||
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
|
||||
val, (val & mask) == mask,
|
||||
1000, CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(adp->dsi.dev, "failed to write command FIFO\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp_dsi_write(struct adp_mipi_drv_private *adp,
|
||||
const struct mipi_dsi_packet *packet)
|
||||
{
|
||||
const u8 *tx_buf = packet->payload;
|
||||
int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
|
||||
__le32 word;
|
||||
u32 val;
|
||||
|
||||
while (len) {
|
||||
if (len < pld_data_bytes) {
|
||||
word = 0;
|
||||
memcpy(&word, tx_buf, len);
|
||||
writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA);
|
||||
len = 0;
|
||||
} else {
|
||||
memcpy(&word, tx_buf, pld_data_bytes);
|
||||
writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA);
|
||||
tx_buf += pld_data_bytes;
|
||||
len -= pld_data_bytes;
|
||||
}
|
||||
|
||||
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
|
||||
val, !(val & GEN_PLD_W_FULL), 1000,
|
||||
CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(adp->dsi.dev,
|
||||
"failed to get available write payload FIFO\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
word = 0;
|
||||
memcpy(&word, packet->header, sizeof(packet->header));
|
||||
return adp_dsi_gen_pkt_hdr_write(adp, le32_to_cpu(word));
|
||||
}
|
||||
|
||||
static int adp_dsi_read(struct adp_mipi_drv_private *adp,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
int i, j, ret, len = msg->rx_len;
|
||||
u8 *buf = msg->rx_buf;
|
||||
u32 val;
|
||||
|
||||
/* Wait end of the read operation */
|
||||
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
|
||||
val, !(val & GEN_RD_CMD_BUSY),
|
||||
1000, CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(adp->dsi.dev, "Timeout during read operation\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
/* Read fifo must not be empty before all bytes are read */
|
||||
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
|
||||
val, !(val & GEN_PLD_R_EMPTY),
|
||||
1000, CMD_PKT_STATUS_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(adp->dsi.dev, "Read payload FIFO is empty\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = readl(adp->mipi + DSI_GEN_PLD_DATA);
|
||||
for (j = 0; j < 4 && j + i < len; j++)
|
||||
buf[i + j] = val >> (8 * j);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t adp_dsi_host_transfer(struct mipi_dsi_host *host,
|
||||
const struct mipi_dsi_msg *msg)
|
||||
{
|
||||
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
|
||||
struct mipi_dsi_packet packet;
|
||||
int ret, nb_bytes;
|
||||
|
||||
ret = mipi_dsi_create_packet(&packet, msg);
|
||||
if (ret) {
|
||||
dev_err(adp->dsi.dev, "failed to create packet: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = adp_dsi_write(adp, &packet);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (msg->rx_buf && msg->rx_len) {
|
||||
ret = adp_dsi_read(adp, msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
nb_bytes = msg->rx_len;
|
||||
} else {
|
||||
nb_bytes = packet.size;
|
||||
}
|
||||
|
||||
return nb_bytes;
|
||||
}
|
||||
|
||||
static int adp_dsi_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adp_dsi_unbind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct component_ops adp_dsi_component_ops = {
|
||||
.bind = adp_dsi_bind,
|
||||
.unbind = adp_dsi_unbind,
|
||||
};
|
||||
|
||||
static int adp_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
struct mipi_dsi_device *dev)
|
||||
{
|
||||
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
|
||||
struct drm_bridge *next;
|
||||
int ret;
|
||||
|
||||
next = devm_drm_of_get_bridge(adp->dsi.dev, adp->dsi.dev->of_node, 1, 0);
|
||||
if (IS_ERR(next))
|
||||
return PTR_ERR(next);
|
||||
|
||||
adp->next_bridge = next;
|
||||
|
||||
drm_bridge_add(&adp->bridge);
|
||||
|
||||
ret = component_add(host->dev, &adp_dsi_component_ops);
|
||||
if (ret) {
|
||||
pr_err("failed to add dsi_host component: %d\n", ret);
|
||||
drm_bridge_remove(&adp->bridge);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp_dsi_host_detach(struct mipi_dsi_host *host,
|
||||
struct mipi_dsi_device *dev)
|
||||
{
|
||||
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
|
||||
|
||||
component_del(host->dev, &adp_dsi_component_ops);
|
||||
drm_bridge_remove(&adp->bridge);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mipi_dsi_host_ops adp_dsi_host_ops = {
|
||||
.transfer = adp_dsi_host_transfer,
|
||||
.attach = adp_dsi_host_attach,
|
||||
.detach = adp_dsi_host_detach,
|
||||
};
|
||||
|
||||
static int adp_dsi_bridge_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct adp_mipi_drv_private *adp =
|
||||
container_of(bridge, struct adp_mipi_drv_private, bridge);
|
||||
|
||||
return drm_bridge_attach(bridge->encoder, adp->next_bridge, bridge, flags);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs adp_dsi_bridge_funcs = {
|
||||
.attach = adp_dsi_bridge_attach,
|
||||
};
|
||||
|
||||
static int adp_mipi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct adp_mipi_drv_private *adp;
|
||||
|
||||
adp = devm_kzalloc(&pdev->dev, sizeof(*adp), GFP_KERNEL);
|
||||
if (!adp)
|
||||
return -ENOMEM;
|
||||
|
||||
adp->mipi = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(adp->mipi)) {
|
||||
dev_err(&pdev->dev, "failed to map mipi mmio");
|
||||
return PTR_ERR(adp->mipi);
|
||||
}
|
||||
|
||||
adp->dsi.dev = &pdev->dev;
|
||||
adp->dsi.ops = &adp_dsi_host_ops;
|
||||
adp->bridge.funcs = &adp_dsi_bridge_funcs;
|
||||
adp->bridge.of_node = pdev->dev.of_node;
|
||||
adp->bridge.type = DRM_MODE_CONNECTOR_DSI;
|
||||
dev_set_drvdata(&pdev->dev, adp);
|
||||
return mipi_dsi_host_register(&adp->dsi);
|
||||
}
|
||||
|
||||
static void adp_mipi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct adp_mipi_drv_private *adp = dev_get_drvdata(dev);
|
||||
|
||||
mipi_dsi_host_unregister(&adp->dsi);
|
||||
}
|
||||
|
||||
static const struct of_device_id adp_mipi_of_match[] = {
|
||||
{ .compatible = "apple,h7-display-pipe-mipi", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adp_mipi_of_match);
|
||||
|
||||
static struct platform_driver adp_mipi_platform_driver = {
|
||||
.driver = {
|
||||
.name = "adp-mipi",
|
||||
.of_match_table = adp_mipi_of_match,
|
||||
},
|
||||
.probe = adp_mipi_probe,
|
||||
.remove = adp_mipi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(adp_mipi_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Apple Display Pipe MIPI driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,612 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/component.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_bridge_connector.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_dma_helper.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
#include <drm/drm_gem_dma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#define ADP_INT_STATUS 0x34
|
||||
#define ADP_INT_STATUS_INT_MASK 0x7
|
||||
#define ADP_INT_STATUS_VBLANK 0x1
|
||||
#define ADP_CTRL 0x100
|
||||
#define ADP_CTRL_VBLANK_ON 0x12
|
||||
#define ADP_CTRL_FIFO_ON 0x601
|
||||
#define ADP_SCREEN_SIZE 0x0c
|
||||
#define ADP_SCREEN_HSIZE GENMASK(15, 0)
|
||||
#define ADP_SCREEN_VSIZE GENMASK(31, 16)
|
||||
|
||||
#define ADBE_FIFO 0x10c0
|
||||
#define ADBE_FIFO_SYNC 0xc0000000
|
||||
|
||||
#define ADBE_BLEND_BYPASS 0x2020
|
||||
#define ADBE_BLEND_EN1 0x2028
|
||||
#define ADBE_BLEND_EN2 0x2074
|
||||
#define ADBE_BLEND_EN3 0x202c
|
||||
#define ADBE_BLEND_EN4 0x2034
|
||||
#define ADBE_MASK_BUF 0x2200
|
||||
|
||||
#define ADBE_SRC_START 0x4040
|
||||
#define ADBE_SRC_SIZE 0x4048
|
||||
#define ADBE_DST_START 0x4050
|
||||
#define ADBE_DST_SIZE 0x4054
|
||||
#define ADBE_STRIDE 0x4038
|
||||
#define ADBE_FB_BASE 0x4030
|
||||
|
||||
#define ADBE_LAYER_EN1 0x4020
|
||||
#define ADBE_LAYER_EN2 0x4068
|
||||
#define ADBE_LAYER_EN3 0x40b4
|
||||
#define ADBE_LAYER_EN4 0x40f4
|
||||
#define ADBE_SCALE_CTL 0x40ac
|
||||
#define ADBE_SCALE_CTL_BYPASS 0x100000
|
||||
|
||||
#define ADBE_LAYER_CTL 0x1038
|
||||
#define ADBE_LAYER_CTL_ENABLE 0x10000
|
||||
|
||||
#define ADBE_PIX_FMT 0x402c
|
||||
#define ADBE_PIX_FMT_XRGB32 0x53e4001
|
||||
|
||||
static int adp_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
/*
|
||||
* The modesetting driver does not check the non-desktop connector
|
||||
* property and keeps the device open and locked. If the touchbar daemon
|
||||
* opens the device first, modesetting breaks the whole X session.
|
||||
* Simply refuse to open the device for X11 server processes as
|
||||
* workaround.
|
||||
*/
|
||||
if (current->comm[0] == 'X')
|
||||
return -EBUSY;
|
||||
|
||||
return drm_open(inode, filp);
|
||||
}
|
||||
|
||||
static const struct file_operations adp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = adp_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = noop_llseek,
|
||||
.mmap = drm_gem_mmap,
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
DRM_GEM_DMA_UNMAPPED_AREA_FOPS
|
||||
};
|
||||
|
||||
static int adp_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *drm,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
args->height = ALIGN(args->height, 64);
|
||||
args->size = args->pitch * args->height;
|
||||
|
||||
return drm_gem_dma_dumb_create_internal(file_priv, drm, args);
|
||||
}
|
||||
|
||||
static const struct drm_driver adp_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
|
||||
.fops = &adp_fops,
|
||||
DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(adp_drm_gem_dumb_create),
|
||||
.name = "adp",
|
||||
.desc = "Apple Display Pipe DRM Driver",
|
||||
.major = 0,
|
||||
.minor = 1,
|
||||
};
|
||||
|
||||
struct adp_drv_private {
|
||||
struct drm_device drm;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
struct drm_bridge *next_bridge;
|
||||
void __iomem *be;
|
||||
void __iomem *fe;
|
||||
u32 *mask_buf;
|
||||
u64 mask_buf_size;
|
||||
dma_addr_t mask_iova;
|
||||
int be_irq;
|
||||
int fe_irq;
|
||||
spinlock_t irq_lock;
|
||||
struct drm_pending_vblank_event *event;
|
||||
};
|
||||
|
||||
#define to_adp(x) container_of(x, struct adp_drv_private, drm)
|
||||
#define crtc_to_adp(x) container_of(x, struct adp_drv_private, crtc)
|
||||
|
||||
static int adp_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *new_plane_state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
|
||||
new_plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
|
||||
if (!new_plane_state->crtc)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
return drm_atomic_helper_check_plane_state(new_plane_state,
|
||||
crtc_state,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
true, true);
|
||||
}
|
||||
|
||||
static void adp_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct adp_drv_private *adp;
|
||||
struct drm_rect src_rect;
|
||||
struct drm_gem_dma_object *obj;
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
u32 src_pos, src_size, dst_pos, dst_size;
|
||||
|
||||
if (!plane || !new_state)
|
||||
return;
|
||||
|
||||
fb = new_state->fb;
|
||||
if (!fb)
|
||||
return;
|
||||
adp = to_adp(plane->dev);
|
||||
|
||||
drm_rect_fp_to_int(&src_rect, &new_state->src);
|
||||
src_pos = src_rect.x1 << 16 | src_rect.y1;
|
||||
dst_pos = new_state->dst.x1 << 16 | new_state->dst.y1;
|
||||
src_size = drm_rect_width(&src_rect) << 16 | drm_rect_height(&src_rect);
|
||||
dst_size = drm_rect_width(&new_state->dst) << 16 |
|
||||
drm_rect_height(&new_state->dst);
|
||||
writel(src_pos, adp->be + ADBE_SRC_START);
|
||||
writel(src_size, adp->be + ADBE_SRC_SIZE);
|
||||
writel(dst_pos, adp->be + ADBE_DST_START);
|
||||
writel(dst_size, adp->be + ADBE_DST_SIZE);
|
||||
writel(fb->pitches[0], adp->be + ADBE_STRIDE);
|
||||
obj = drm_fb_dma_get_gem_obj(fb, 0);
|
||||
if (obj)
|
||||
writel(obj->dma_addr + fb->offsets[0], adp->be + ADBE_FB_BASE);
|
||||
|
||||
writel(BIT(0), adp->be + ADBE_LAYER_EN1);
|
||||
writel(BIT(0), adp->be + ADBE_LAYER_EN2);
|
||||
writel(BIT(0), adp->be + ADBE_LAYER_EN3);
|
||||
writel(BIT(0), adp->be + ADBE_LAYER_EN4);
|
||||
writel(ADBE_SCALE_CTL_BYPASS, adp->be + ADBE_SCALE_CTL);
|
||||
writel(ADBE_LAYER_CTL_ENABLE | BIT(0), adp->be + ADBE_LAYER_CTL);
|
||||
writel(ADBE_PIX_FMT_XRGB32, adp->be + ADBE_PIX_FMT);
|
||||
}
|
||||
|
||||
static void adp_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct adp_drv_private *adp = to_adp(plane->dev);
|
||||
|
||||
writel(0x0, adp->be + ADBE_LAYER_EN1);
|
||||
writel(0x0, adp->be + ADBE_LAYER_EN2);
|
||||
writel(0x0, adp->be + ADBE_LAYER_EN3);
|
||||
writel(0x0, adp->be + ADBE_LAYER_EN4);
|
||||
writel(ADBE_LAYER_CTL_ENABLE, adp->be + ADBE_LAYER_CTL);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs adp_plane_helper_funcs = {
|
||||
.atomic_check = adp_plane_atomic_check,
|
||||
.atomic_update = adp_plane_atomic_update,
|
||||
.atomic_disable = adp_plane_atomic_disable,
|
||||
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs adp_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
DRM_GEM_SHADOW_PLANE_FUNCS
|
||||
};
|
||||
|
||||
static const u32 plane_formats[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
};
|
||||
|
||||
#define ALL_CRTCS 1
|
||||
|
||||
static struct drm_plane *adp_plane_new(struct adp_drv_private *adp)
|
||||
{
|
||||
struct drm_device *drm = &adp->drm;
|
||||
struct drm_plane *plane;
|
||||
|
||||
plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0,
|
||||
ALL_CRTCS, &adp_plane_funcs,
|
||||
plane_formats, ARRAY_SIZE(plane_formats),
|
||||
NULL, DRM_PLANE_TYPE_PRIMARY, "plane");
|
||||
if (!plane) {
|
||||
drm_err(drm, "failed to allocate plane");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
drm_plane_helper_add(plane, &adp_plane_helper_funcs);
|
||||
return plane;
|
||||
}
|
||||
|
||||
static void adp_enable_vblank(struct adp_drv_private *adp)
|
||||
{
|
||||
u32 cur_ctrl;
|
||||
|
||||
writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS);
|
||||
|
||||
cur_ctrl = readl(adp->fe + ADP_CTRL);
|
||||
writel(cur_ctrl | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
|
||||
}
|
||||
|
||||
static int adp_crtc_enable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct adp_drv_private *adp = to_adp(dev);
|
||||
|
||||
adp_enable_vblank(adp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adp_disable_vblank(struct adp_drv_private *adp)
|
||||
{
|
||||
u32 cur_ctrl;
|
||||
|
||||
cur_ctrl = readl(adp->fe + ADP_CTRL);
|
||||
writel(cur_ctrl & ~ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
|
||||
writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS);
|
||||
}
|
||||
|
||||
static void adp_crtc_disable_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct adp_drv_private *adp = to_adp(dev);
|
||||
|
||||
adp_disable_vblank(adp);
|
||||
}
|
||||
|
||||
static void adp_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct adp_drv_private *adp = crtc_to_adp(crtc);
|
||||
|
||||
writel(BIT(0), adp->be + ADBE_BLEND_EN2);
|
||||
writel(BIT(4), adp->be + ADBE_BLEND_EN1);
|
||||
writel(BIT(0), adp->be + ADBE_BLEND_EN3);
|
||||
writel(BIT(0), adp->be + ADBE_BLEND_BYPASS);
|
||||
writel(BIT(0), adp->be + ADBE_BLEND_EN4);
|
||||
}
|
||||
|
||||
static void adp_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct adp_drv_private *adp = crtc_to_adp(crtc);
|
||||
struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
|
||||
|
||||
drm_atomic_helper_disable_planes_on_crtc(old_state, false);
|
||||
|
||||
writel(0x0, adp->be + ADBE_BLEND_EN2);
|
||||
writel(0x0, adp->be + ADBE_BLEND_EN1);
|
||||
writel(0x0, adp->be + ADBE_BLEND_EN3);
|
||||
writel(0x0, adp->be + ADBE_BLEND_BYPASS);
|
||||
writel(0x0, adp->be + ADBE_BLEND_EN4);
|
||||
drm_crtc_vblank_off(crtc);
|
||||
}
|
||||
|
||||
static void adp_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
u32 frame_num = 1;
|
||||
struct adp_drv_private *adp = crtc_to_adp(crtc);
|
||||
struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
u64 new_size = ALIGN(new_state->mode.hdisplay *
|
||||
new_state->mode.vdisplay * 4, PAGE_SIZE);
|
||||
|
||||
if (new_size != adp->mask_buf_size) {
|
||||
if (adp->mask_buf)
|
||||
dma_free_coherent(crtc->dev->dev, adp->mask_buf_size,
|
||||
adp->mask_buf, adp->mask_iova);
|
||||
adp->mask_buf = NULL;
|
||||
if (new_size != 0) {
|
||||
adp->mask_buf = dma_alloc_coherent(crtc->dev->dev, new_size,
|
||||
&adp->mask_iova, GFP_KERNEL);
|
||||
memset(adp->mask_buf, 0xFF, new_size);
|
||||
writel(adp->mask_iova, adp->be + ADBE_MASK_BUF);
|
||||
}
|
||||
adp->mask_buf_size = new_size;
|
||||
}
|
||||
writel(ADBE_FIFO_SYNC | frame_num, adp->be + ADBE_FIFO);
|
||||
//FIXME: use adbe flush interrupt
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
if (crtc->state->event) {
|
||||
drm_crtc_vblank_get(crtc);
|
||||
adp->event = crtc->state->event;
|
||||
}
|
||||
crtc->state->event = NULL;
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs adp_crtc_funcs = {
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.enable_vblank = adp_crtc_enable_vblank,
|
||||
.disable_vblank = adp_crtc_disable_vblank,
|
||||
};
|
||||
|
||||
|
||||
static const struct drm_crtc_helper_funcs adp_crtc_helper_funcs = {
|
||||
.atomic_enable = adp_crtc_atomic_enable,
|
||||
.atomic_disable = adp_crtc_atomic_disable,
|
||||
.atomic_flush = adp_crtc_atomic_flush,
|
||||
};
|
||||
|
||||
static int adp_setup_crtc(struct adp_drv_private *adp)
|
||||
{
|
||||
struct drm_device *drm = &adp->drm;
|
||||
struct drm_plane *primary;
|
||||
int ret;
|
||||
|
||||
primary = adp_plane_new(adp);
|
||||
if (IS_ERR(primary))
|
||||
return PTR_ERR(primary);
|
||||
|
||||
ret = drm_crtc_init_with_planes(drm, &adp->crtc, primary,
|
||||
NULL, &adp_crtc_funcs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_crtc_helper_add(&adp->crtc, &adp_crtc_helper_funcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs adp_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create_with_dirty,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static int adp_setup_mode_config(struct adp_drv_private *adp)
|
||||
{
|
||||
struct drm_device *drm = &adp->drm;
|
||||
int ret;
|
||||
u32 size;
|
||||
|
||||
ret = drmm_mode_config_init(drm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Query screen size restrict the frame buffer size to the screen size
|
||||
* aligned to the next multiple of 64. This is not necessary but can be
|
||||
* used as simple check for non-desktop devices.
|
||||
* Xorg's modesetting driver does not care about the connector
|
||||
* "non-desktop" property. The max frame buffer width or height can be
|
||||
* easily checked and a device can be reject if the max width/height is
|
||||
* smaller than 120 for example.
|
||||
* Any touchbar daemon is not limited by this small framebuffer size.
|
||||
*/
|
||||
size = readl(adp->fe + ADP_SCREEN_SIZE);
|
||||
|
||||
drm->mode_config.min_width = 32;
|
||||
drm->mode_config.min_height = 32;
|
||||
drm->mode_config.max_width = ALIGN(FIELD_GET(ADP_SCREEN_HSIZE, size), 64);
|
||||
drm->mode_config.max_height = ALIGN(FIELD_GET(ADP_SCREEN_VSIZE, size), 64);
|
||||
drm->mode_config.preferred_depth = 24;
|
||||
drm->mode_config.prefer_shadow = 0;
|
||||
drm->mode_config.funcs = &adp_mode_config_funcs;
|
||||
|
||||
ret = adp_setup_crtc(adp);
|
||||
if (ret) {
|
||||
drm_err(drm, "failed to create crtc");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adp->encoder = drmm_plain_encoder_alloc(drm, NULL, DRM_MODE_ENCODER_DSI, NULL);
|
||||
if (IS_ERR(adp->encoder)) {
|
||||
drm_err(drm, "failed to init encoder");
|
||||
return PTR_ERR(adp->encoder);
|
||||
}
|
||||
adp->encoder->possible_crtcs = ALL_CRTCS;
|
||||
|
||||
ret = drm_bridge_attach(adp->encoder, adp->next_bridge, NULL,
|
||||
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
|
||||
if (ret) {
|
||||
drm_err(drm, "failed to init bridge chain");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adp->connector = drm_bridge_connector_init(drm, adp->encoder);
|
||||
if (IS_ERR(adp->connector))
|
||||
return PTR_ERR(adp->connector);
|
||||
|
||||
drm_connector_attach_encoder(adp->connector, adp->encoder);
|
||||
|
||||
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
|
||||
if (ret < 0) {
|
||||
drm_err(drm, "failed to initialize vblank");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp_parse_of(struct platform_device *pdev, struct adp_drv_private *adp)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
adp->be = devm_platform_ioremap_resource_byname(pdev, "be");
|
||||
if (IS_ERR(adp->be)) {
|
||||
dev_err(dev, "failed to map display backend mmio");
|
||||
return PTR_ERR(adp->be);
|
||||
}
|
||||
|
||||
adp->fe = devm_platform_ioremap_resource_byname(pdev, "fe");
|
||||
if (IS_ERR(adp->fe)) {
|
||||
dev_err(dev, "failed to map display pipe mmio");
|
||||
return PTR_ERR(adp->fe);
|
||||
}
|
||||
|
||||
adp->be_irq = platform_get_irq_byname(pdev, "be");
|
||||
if (adp->be_irq < 0)
|
||||
return adp->be_irq;
|
||||
|
||||
adp->fe_irq = platform_get_irq_byname(pdev, "fe");
|
||||
if (adp->fe_irq < 0)
|
||||
return adp->fe_irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t adp_fe_irq(int irq, void *arg)
|
||||
{
|
||||
struct adp_drv_private *adp = (struct adp_drv_private *)arg;
|
||||
u32 int_status;
|
||||
u32 int_ctl;
|
||||
|
||||
spin_lock(&adp->irq_lock);
|
||||
|
||||
int_status = readl(adp->fe + ADP_INT_STATUS);
|
||||
if (int_status & ADP_INT_STATUS_VBLANK) {
|
||||
drm_crtc_handle_vblank(&adp->crtc);
|
||||
spin_lock(&adp->crtc.dev->event_lock);
|
||||
if (adp->event) {
|
||||
int_ctl = readl(adp->fe + ADP_CTRL);
|
||||
if ((int_ctl & 0xF00) == 0x600) {
|
||||
drm_crtc_send_vblank_event(&adp->crtc, adp->event);
|
||||
adp->event = NULL;
|
||||
drm_crtc_vblank_put(&adp->crtc);
|
||||
}
|
||||
}
|
||||
spin_unlock(&adp->crtc.dev->event_lock);
|
||||
}
|
||||
|
||||
writel(int_status, adp->fe + ADP_INT_STATUS);
|
||||
|
||||
spin_unlock(&adp->irq_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adp_drm_bind(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(dev);
|
||||
struct adp_drv_private *adp = to_adp(drm);
|
||||
int err;
|
||||
|
||||
adp_disable_vblank(adp);
|
||||
writel(ADP_CTRL_FIFO_ON | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
|
||||
|
||||
adp->next_bridge = drmm_of_get_bridge(&adp->drm, dev->of_node, 0, 0);
|
||||
if (IS_ERR(adp->next_bridge)) {
|
||||
dev_err(dev, "failed to find next bridge");
|
||||
return PTR_ERR(adp->next_bridge);
|
||||
}
|
||||
|
||||
err = adp_setup_mode_config(adp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = request_irq(adp->fe_irq, adp_fe_irq, 0, "adp-fe", adp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = drm_dev_register(&adp->drm, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adp_drm_unbind(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(dev);
|
||||
struct adp_drv_private *adp = to_adp(drm);
|
||||
|
||||
drm_dev_unregister(drm);
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
free_irq(adp->fe_irq, adp);
|
||||
}
|
||||
|
||||
static const struct component_master_ops adp_master_ops = {
|
||||
.bind = adp_drm_bind,
|
||||
.unbind = adp_drm_unbind,
|
||||
};
|
||||
|
||||
static int compare_dev(struct device *dev, void *data)
|
||||
{
|
||||
return dev->of_node == data;
|
||||
}
|
||||
|
||||
static int adp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *port;
|
||||
struct component_match *match = NULL;
|
||||
struct adp_drv_private *adp;
|
||||
int err;
|
||||
|
||||
adp = devm_drm_dev_alloc(&pdev->dev, &adp_driver, struct adp_drv_private, drm);
|
||||
if (IS_ERR(adp))
|
||||
return PTR_ERR(adp);
|
||||
|
||||
spin_lock_init(&adp->irq_lock);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, &adp->drm);
|
||||
|
||||
err = adp_parse_of(pdev, adp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
|
||||
if (!port)
|
||||
return -ENODEV;
|
||||
|
||||
drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
|
||||
of_node_put(port);
|
||||
|
||||
return component_master_add_with_match(&pdev->dev, &adp_master_ops, match);
|
||||
}
|
||||
|
||||
static void adp_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_master_del(&pdev->dev, &adp_master_ops);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
}
|
||||
|
||||
static const struct of_device_id adp_of_match[] = {
|
||||
{ .compatible = "apple,h7-display-pipe", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adp_of_match);
|
||||
|
||||
static struct platform_driver adp_platform_driver = {
|
||||
.driver = {
|
||||
.name = "adp",
|
||||
.of_match_table = adp_of_match,
|
||||
},
|
||||
.probe = adp_probe,
|
||||
.remove = adp_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(adp_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Apple Display Pipe DRM driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -887,7 +887,7 @@ static int sii902x_audio_codec_init(struct sii902x *sii902x,
|
||||
lanes[0] = 0;
|
||||
} else if (num_lanes < 0) {
|
||||
dev_err(dev,
|
||||
"%s: Error gettin \"sil,i2s-data-lanes\": %d\n",
|
||||
"%s: Error getting \"sil,i2s-data-lanes\": %d\n",
|
||||
__func__, num_lanes);
|
||||
return num_lanes;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,88 @@
|
||||
|
||||
#define SCRAMB_POLL_DELAY_MS 3000
|
||||
|
||||
/*
|
||||
* Unless otherwise noted, entries in this table are 100% optimization.
|
||||
* Values can be obtained from dw_hdmi_qp_compute_n() but that function is
|
||||
* slow so we pre-compute values we expect to see.
|
||||
*
|
||||
* The values for TMDS 25175, 25200, 27000, 54000, 74250 and 148500 kHz are
|
||||
* the recommended N values specified in the Audio chapter of the HDMI
|
||||
* specification.
|
||||
*/
|
||||
static const struct dw_hdmi_audio_tmds_n {
|
||||
unsigned long tmds;
|
||||
unsigned int n_32k;
|
||||
unsigned int n_44k1;
|
||||
unsigned int n_48k;
|
||||
} common_tmds_n_table[] = {
|
||||
{ .tmds = 25175000, .n_32k = 4576, .n_44k1 = 7007, .n_48k = 6864, },
|
||||
{ .tmds = 25200000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 27000000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
|
||||
{ .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
|
||||
{ .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
|
||||
{ .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
|
||||
{ .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
|
||||
{ .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
|
||||
{ .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
|
||||
{ .tmds = 54000000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
|
||||
{ .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
|
||||
{ .tmds = 73250000, .n_32k = 11648, .n_44k1 = 14112, .n_48k = 6144, },
|
||||
{ .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
|
||||
{ .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
|
||||
{ .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
|
||||
{ .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
|
||||
{ .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
|
||||
{ .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
|
||||
{ .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
|
||||
{ .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
|
||||
{ .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
|
||||
{ .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
|
||||
{ .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
|
||||
{ .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
|
||||
{ .tmds = 146250000, .n_32k = 11648, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 148500000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
|
||||
{ .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
|
||||
{ .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
|
||||
|
||||
/* For 297 MHz+ HDMI spec have some other rule for setting N */
|
||||
{ .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
|
||||
{ .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240,},
|
||||
|
||||
/* End of table */
|
||||
{ .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, },
|
||||
};
|
||||
|
||||
/*
|
||||
* These are the CTS values as recommended in the Audio chapter of the HDMI
|
||||
* specification.
|
||||
*/
|
||||
static const struct dw_hdmi_audio_tmds_cts {
|
||||
unsigned long tmds;
|
||||
unsigned int cts_32k;
|
||||
unsigned int cts_44k1;
|
||||
unsigned int cts_48k;
|
||||
} common_tmds_cts_table[] = {
|
||||
{ .tmds = 25175000, .cts_32k = 28125, .cts_44k1 = 31250, .cts_48k = 28125, },
|
||||
{ .tmds = 25200000, .cts_32k = 25200, .cts_44k1 = 28000, .cts_48k = 25200, },
|
||||
{ .tmds = 27000000, .cts_32k = 27000, .cts_44k1 = 30000, .cts_48k = 27000, },
|
||||
{ .tmds = 54000000, .cts_32k = 54000, .cts_44k1 = 60000, .cts_48k = 54000, },
|
||||
{ .tmds = 74250000, .cts_32k = 74250, .cts_44k1 = 82500, .cts_48k = 74250, },
|
||||
{ .tmds = 148500000, .cts_32k = 148500, .cts_44k1 = 165000, .cts_48k = 148500, },
|
||||
|
||||
/* End of table */
|
||||
{ .tmds = 0, .cts_32k = 0, .cts_44k1 = 0, .cts_48k = 0, },
|
||||
};
|
||||
|
||||
struct dw_hdmi_qp_i2c {
|
||||
struct i2c_adapter adap;
|
||||
|
||||
@@ -60,6 +142,8 @@ struct dw_hdmi_qp {
|
||||
} phy;
|
||||
|
||||
struct regmap *regm;
|
||||
|
||||
unsigned long tmds_char_rate;
|
||||
};
|
||||
|
||||
static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val,
|
||||
@@ -83,6 +167,346 @@ static void dw_hdmi_qp_mod(struct dw_hdmi_qp *hdmi, unsigned int data,
|
||||
regmap_update_bits(hdmi->regm, reg, mask, data);
|
||||
}
|
||||
|
||||
static struct dw_hdmi_qp *dw_hdmi_qp_from_bridge(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct dw_hdmi_qp, bridge);
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_set_cts_n(struct dw_hdmi_qp *hdmi, unsigned int cts,
|
||||
unsigned int n)
|
||||
{
|
||||
/* Set N */
|
||||
dw_hdmi_qp_mod(hdmi, n, AUDPKT_ACR_N_VALUE, AUDPKT_ACR_CONTROL0);
|
||||
|
||||
/* Set CTS */
|
||||
if (cts)
|
||||
dw_hdmi_qp_mod(hdmi, AUDPKT_ACR_CTS_OVR_EN, AUDPKT_ACR_CTS_OVR_EN_MSK,
|
||||
AUDPKT_ACR_CONTROL1);
|
||||
else
|
||||
dw_hdmi_qp_mod(hdmi, 0, AUDPKT_ACR_CTS_OVR_EN_MSK,
|
||||
AUDPKT_ACR_CONTROL1);
|
||||
|
||||
dw_hdmi_qp_mod(hdmi, AUDPKT_ACR_CTS_OVR_VAL(cts), AUDPKT_ACR_CTS_OVR_VAL_MSK,
|
||||
AUDPKT_ACR_CONTROL1);
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_match_tmds_n_table(struct dw_hdmi_qp *hdmi,
|
||||
unsigned long pixel_clk,
|
||||
unsigned long freq)
|
||||
{
|
||||
const struct dw_hdmi_audio_tmds_n *tmds_n = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; common_tmds_n_table[i].tmds != 0; i++) {
|
||||
if (pixel_clk == common_tmds_n_table[i].tmds) {
|
||||
tmds_n = &common_tmds_n_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tmds_n)
|
||||
return -ENOENT;
|
||||
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
return tmds_n->n_32k;
|
||||
case 44100:
|
||||
case 88200:
|
||||
case 176400:
|
||||
return (freq / 44100) * tmds_n->n_44k1;
|
||||
case 48000:
|
||||
case 96000:
|
||||
case 192000:
|
||||
return (freq / 48000) * tmds_n->n_48k;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 dw_hdmi_qp_audio_math_diff(unsigned int freq, unsigned int n,
|
||||
unsigned int pixel_clk)
|
||||
{
|
||||
u64 cts = mul_u32_u32(pixel_clk, n);
|
||||
|
||||
return do_div(cts, 128 * freq);
|
||||
}
|
||||
|
||||
static unsigned int dw_hdmi_qp_compute_n(struct dw_hdmi_qp *hdmi,
|
||||
unsigned long pixel_clk,
|
||||
unsigned long freq)
|
||||
{
|
||||
unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500);
|
||||
unsigned int max_n = (128 * freq) / 300;
|
||||
unsigned int ideal_n = (128 * freq) / 1000;
|
||||
unsigned int best_n_distance = ideal_n;
|
||||
unsigned int best_n = 0;
|
||||
u64 best_diff = U64_MAX;
|
||||
int n;
|
||||
|
||||
/* If the ideal N could satisfy the audio math, then just take it */
|
||||
if (dw_hdmi_qp_audio_math_diff(freq, ideal_n, pixel_clk) == 0)
|
||||
return ideal_n;
|
||||
|
||||
for (n = min_n; n <= max_n; n++) {
|
||||
u64 diff = dw_hdmi_qp_audio_math_diff(freq, n, pixel_clk);
|
||||
|
||||
if (diff < best_diff ||
|
||||
(diff == best_diff && abs(n - ideal_n) < best_n_distance)) {
|
||||
best_n = n;
|
||||
best_diff = diff;
|
||||
best_n_distance = abs(best_n - ideal_n);
|
||||
}
|
||||
|
||||
/*
|
||||
* The best N already satisfy the audio math, and also be
|
||||
* the closest value to ideal N, so just cut the loop.
|
||||
*/
|
||||
if (best_diff == 0 && (abs(n - ideal_n) > best_n_distance))
|
||||
break;
|
||||
}
|
||||
|
||||
return best_n;
|
||||
}
|
||||
|
||||
static unsigned int dw_hdmi_qp_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk,
|
||||
unsigned long sample_rate)
|
||||
{
|
||||
int n = dw_hdmi_qp_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
|
||||
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n",
|
||||
pixel_clk);
|
||||
|
||||
return dw_hdmi_qp_compute_n(hdmi, pixel_clk, sample_rate);
|
||||
}
|
||||
|
||||
static unsigned int dw_hdmi_qp_find_cts(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk,
|
||||
unsigned long sample_rate)
|
||||
{
|
||||
const struct dw_hdmi_audio_tmds_cts *tmds_cts = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; common_tmds_cts_table[i].tmds != 0; i++) {
|
||||
if (pixel_clk == common_tmds_cts_table[i].tmds) {
|
||||
tmds_cts = &common_tmds_cts_table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tmds_cts)
|
||||
return 0;
|
||||
|
||||
switch (sample_rate) {
|
||||
case 32000:
|
||||
return tmds_cts->cts_32k;
|
||||
case 44100:
|
||||
case 88200:
|
||||
case 176400:
|
||||
return tmds_cts->cts_44k1;
|
||||
case 48000:
|
||||
case 96000:
|
||||
case 192000:
|
||||
return tmds_cts->cts_48k;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_set_audio_interface(struct dw_hdmi_qp *hdmi,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
u32 conf0 = 0;
|
||||
|
||||
/* Reset the audio data path of the AVP */
|
||||
dw_hdmi_qp_write(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWINIT_P, GLOBAL_SWRESET_REQUEST);
|
||||
|
||||
/* Disable AUDS, ACR, AUDI */
|
||||
dw_hdmi_qp_mod(hdmi, 0,
|
||||
PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | PKTSCHED_AUDI_TX_EN,
|
||||
PKTSCHED_PKT_EN);
|
||||
|
||||
/* Clear the audio FIFO */
|
||||
dw_hdmi_qp_write(hdmi, AUDIO_FIFO_CLR_P, AUDIO_INTERFACE_CONTROL0);
|
||||
|
||||
/* Select I2S interface as the audio source */
|
||||
dw_hdmi_qp_mod(hdmi, AUD_IF_I2S, AUD_IF_SEL_MSK, AUDIO_INTERFACE_CONFIG0);
|
||||
|
||||
/* Enable the active i2s lanes */
|
||||
switch (hparms->channels) {
|
||||
case 7 ... 8:
|
||||
conf0 |= I2S_LINES_EN(3);
|
||||
fallthrough;
|
||||
case 5 ... 6:
|
||||
conf0 |= I2S_LINES_EN(2);
|
||||
fallthrough;
|
||||
case 3 ... 4:
|
||||
conf0 |= I2S_LINES_EN(1);
|
||||
fallthrough;
|
||||
default:
|
||||
conf0 |= I2S_LINES_EN(0);
|
||||
break;
|
||||
}
|
||||
|
||||
dw_hdmi_qp_mod(hdmi, conf0, I2S_LINES_EN_MSK, AUDIO_INTERFACE_CONFIG0);
|
||||
|
||||
/*
|
||||
* Enable bpcuv generated internally for L-PCM, or received
|
||||
* from stream for NLPCM/HBR.
|
||||
*/
|
||||
switch (fmt->bit_fmt) {
|
||||
case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
|
||||
conf0 = (hparms->channels == 8) ? AUD_HBR : AUD_ASP;
|
||||
conf0 |= I2S_BPCUV_RCV_EN;
|
||||
break;
|
||||
default:
|
||||
conf0 = AUD_ASP | I2S_BPCUV_RCV_DIS;
|
||||
break;
|
||||
}
|
||||
|
||||
dw_hdmi_qp_mod(hdmi, conf0, I2S_BPCUV_RCV_MSK | AUD_FORMAT_MSK,
|
||||
AUDIO_INTERFACE_CONFIG0);
|
||||
|
||||
/* Enable audio FIFO auto clear when overflow */
|
||||
dw_hdmi_qp_mod(hdmi, AUD_FIFO_INIT_ON_OVF_EN, AUD_FIFO_INIT_ON_OVF_MSK,
|
||||
AUDIO_INTERFACE_CONFIG0);
|
||||
}
|
||||
|
||||
/*
|
||||
* When transmitting IEC60958 linear PCM audio, these registers allow to
|
||||
* configure the channel status information of all the channel status
|
||||
* bits in the IEC60958 frame. For the moment this configuration is only
|
||||
* used when the I2S audio interface, General Purpose Audio (GPA),
|
||||
* or AHB audio DMA (AHBAUDDMA) interface is active
|
||||
* (for S/PDIF interface this information comes from the stream).
|
||||
*/
|
||||
static void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi,
|
||||
u8 *channel_status, bool ref2stream)
|
||||
{
|
||||
/*
|
||||
* AUDPKT_CHSTATUS_OVR0: { RSV, RSV, CS1, CS0 }
|
||||
* AUDPKT_CHSTATUS_OVR1: { CS6, CS5, CS4, CS3 }
|
||||
*
|
||||
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
* CS0: | Mode | d | c | b | a |
|
||||
* CS1: | Category Code |
|
||||
* CS2: | Channel Number | Source Number |
|
||||
* CS3: | Clock Accuracy | Sample Freq |
|
||||
* CS4: | Ori Sample Freq | Word Length |
|
||||
* CS5: | | CGMS-A |
|
||||
* CS6~CS23: Reserved
|
||||
*
|
||||
* a: use of channel status block
|
||||
* b: linear PCM identification: 0 for lpcm, 1 for nlpcm
|
||||
* c: copyright information
|
||||
* d: additional format information
|
||||
*/
|
||||
|
||||
if (ref2stream)
|
||||
channel_status[0] |= IEC958_AES0_NONAUDIO;
|
||||
|
||||
if ((dw_hdmi_qp_read(hdmi, AUDIO_INTERFACE_CONFIG0) & GENMASK(25, 24)) == AUD_HBR) {
|
||||
/* fixup cs for HBR */
|
||||
channel_status[3] = (channel_status[3] & 0xf0) | IEC958_AES3_CON_FS_768000;
|
||||
channel_status[4] = (channel_status[4] & 0x0f) | IEC958_AES4_CON_ORIGFS_NOTID;
|
||||
}
|
||||
|
||||
dw_hdmi_qp_write(hdmi, channel_status[0] | (channel_status[1] << 8),
|
||||
AUDPKT_CHSTATUS_OVR0);
|
||||
|
||||
regmap_bulk_write(hdmi->regm, AUDPKT_CHSTATUS_OVR1, &channel_status[3], 1);
|
||||
|
||||
if (ref2stream)
|
||||
dw_hdmi_qp_mod(hdmi, 0,
|
||||
AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK,
|
||||
AUDPKT_CONTROL0);
|
||||
else
|
||||
dw_hdmi_qp_mod(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN,
|
||||
AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK,
|
||||
AUDPKT_CONTROL0);
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned long long tmds_char_rate,
|
||||
unsigned int sample_rate)
|
||||
{
|
||||
unsigned int n, cts;
|
||||
|
||||
n = dw_hdmi_qp_find_n(hdmi, tmds_char_rate, sample_rate);
|
||||
cts = dw_hdmi_qp_find_cts(hdmi, tmds_char_rate, sample_rate);
|
||||
|
||||
dw_hdmi_qp_set_cts_n(hdmi, cts, n);
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_audio_enable(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
|
||||
if (hdmi->tmds_char_rate)
|
||||
dw_hdmi_qp_mod(hdmi, 0, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_audio_prepare(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
bool ref2stream = false;
|
||||
|
||||
if (!hdmi->tmds_char_rate)
|
||||
return -ENODEV;
|
||||
|
||||
if (fmt->bit_clk_provider | fmt->frame_clk_provider) {
|
||||
dev_err(hdmi->dev, "unsupported clock settings\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
|
||||
ref2stream = true;
|
||||
|
||||
dw_hdmi_qp_set_audio_interface(hdmi, fmt, hparms);
|
||||
dw_hdmi_qp_set_sample_rate(hdmi, hdmi->tmds_char_rate, hparms->sample_rate);
|
||||
dw_hdmi_qp_set_channel_status(hdmi, hparms->iec.status, ref2stream);
|
||||
drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector, &hparms->cea);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_audio_disable_regs(struct dw_hdmi_qp *hdmi)
|
||||
{
|
||||
/*
|
||||
* Keep ACR, AUDI, AUDS packet always on to make SINK device
|
||||
* active for better compatibility and user experience.
|
||||
*
|
||||
* This also fix POP sound on some SINK devices which wakeup
|
||||
* from suspend to active.
|
||||
*/
|
||||
dw_hdmi_qp_mod(hdmi, I2S_BPCUV_RCV_DIS, I2S_BPCUV_RCV_MSK,
|
||||
AUDIO_INTERFACE_CONFIG0);
|
||||
dw_hdmi_qp_mod(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN,
|
||||
AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK,
|
||||
AUDPKT_CONTROL0);
|
||||
|
||||
dw_hdmi_qp_mod(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE,
|
||||
AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE);
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_audio_disable(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
|
||||
|
||||
drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
|
||||
|
||||
if (hdmi->tmds_char_rate)
|
||||
dw_hdmi_qp_audio_disable_regs(hdmi);
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi,
|
||||
unsigned char *buf, unsigned int length)
|
||||
{
|
||||
@@ -361,6 +785,51 @@ static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Static values documented in the TRM
|
||||
* Different values are only used for debug purposes
|
||||
*/
|
||||
#define DW_HDMI_QP_AUDIO_INFOFRAME_HB1 0x1
|
||||
#define DW_HDMI_QP_AUDIO_INFOFRAME_HB2 0xa
|
||||
|
||||
static int dw_hdmi_qp_config_audio_infoframe(struct dw_hdmi_qp *hdmi,
|
||||
const u8 *buffer, size_t len)
|
||||
{
|
||||
/*
|
||||
* AUDI_CONTENTS0: { RSV, HB2, HB1, RSV }
|
||||
* AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 }
|
||||
* AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 }
|
||||
*
|
||||
* PB0: CheckSum
|
||||
* PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 |
|
||||
* PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 |
|
||||
* PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 |
|
||||
* PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 |
|
||||
* PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 |
|
||||
* PB6~PB10: Reserved
|
||||
*
|
||||
* AUDI_CONTENTS0 default value defined by HDMI specification,
|
||||
* and shall only be changed for debug purposes.
|
||||
*/
|
||||
u32 header_bytes = (DW_HDMI_QP_AUDIO_INFOFRAME_HB1 << 8) |
|
||||
(DW_HDMI_QP_AUDIO_INFOFRAME_HB2 << 16);
|
||||
|
||||
regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS0, &header_bytes, 1);
|
||||
regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &buffer[3], 1);
|
||||
regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS2, &buffer[4], 1);
|
||||
|
||||
/* Enable ACR, AUDI, AMD */
|
||||
dw_hdmi_qp_mod(hdmi,
|
||||
PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN,
|
||||
PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN,
|
||||
PKTSCHED_PKT_EN);
|
||||
|
||||
/* Enable AUDS */
|
||||
dw_hdmi_qp_mod(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
@@ -381,6 +850,7 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
|
||||
dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n",
|
||||
__func__, conn_state->hdmi.tmds_char_rate);
|
||||
op_mode = 0;
|
||||
hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
|
||||
} else {
|
||||
dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
|
||||
op_mode = OPMODE_DVI;
|
||||
@@ -399,6 +869,8 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
||||
|
||||
hdmi->tmds_char_rate = 0;
|
||||
|
||||
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
|
||||
}
|
||||
|
||||
@@ -454,6 +926,13 @@ static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge,
|
||||
dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
|
||||
break;
|
||||
|
||||
case HDMI_INFOFRAME_TYPE_AUDIO:
|
||||
dw_hdmi_qp_mod(hdmi, 0,
|
||||
PKTSCHED_ACR_TX_EN |
|
||||
PKTSCHED_AUDS_TX_EN |
|
||||
PKTSCHED_AUDI_TX_EN,
|
||||
PKTSCHED_PKT_EN);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
|
||||
}
|
||||
@@ -476,6 +955,9 @@ static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
|
||||
case HDMI_INFOFRAME_TYPE_DRM:
|
||||
return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
|
||||
|
||||
case HDMI_INFOFRAME_TYPE_AUDIO:
|
||||
return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len);
|
||||
|
||||
default:
|
||||
dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
|
||||
return 0;
|
||||
@@ -493,6 +975,9 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
|
||||
.hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid,
|
||||
.hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe,
|
||||
.hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe,
|
||||
.hdmi_audio_startup = dw_hdmi_qp_audio_enable,
|
||||
.hdmi_audio_shutdown = dw_hdmi_qp_audio_disable,
|
||||
.hdmi_audio_prepare = dw_hdmi_qp_audio_prepare,
|
||||
};
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
|
||||
@@ -602,6 +1087,10 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
|
||||
if (IS_ERR(hdmi->bridge.ddc))
|
||||
return ERR_CAST(hdmi->bridge.ddc);
|
||||
|
||||
hdmi->bridge.hdmi_audio_max_i2s_playback_channels = 8;
|
||||
hdmi->bridge.hdmi_audio_dev = dev;
|
||||
hdmi->bridge.hdmi_audio_dai_port = 1;
|
||||
|
||||
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@@ -561,6 +561,8 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
|
||||
REG_LVDS_FMT_HS_NEG_POLARITY : 0) |
|
||||
(mode->flags & DRM_MODE_FLAG_NVSYNC ?
|
||||
REG_LVDS_FMT_VS_NEG_POLARITY : 0);
|
||||
val |= bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_LOW ?
|
||||
REG_LVDS_FMT_DE_NEG_POLARITY : 0;
|
||||
|
||||
/* Set up bits-per-pixel, 18bpp or 24bpp. */
|
||||
if (lvds_format_24bpp) {
|
||||
|
||||
@@ -178,7 +178,7 @@ int drm_fb_dma_get_scanout_buffer(struct drm_plane *plane,
|
||||
dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
|
||||
|
||||
/* Buffer should be accessible from the CPU */
|
||||
if (dma_obj->base.import_attach)
|
||||
if (drm_gem_is_imported(&dma_obj->base))
|
||||
return -ENODEV;
|
||||
|
||||
/* Buffer should be already mapped to CPU */
|
||||
|
||||
@@ -702,6 +702,57 @@ void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pi
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888);
|
||||
|
||||
static void drm_fb_xrgb8888_to_bgr888_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u8 *dbuf8 = dbuf;
|
||||
const __le32 *sbuf32 = sbuf;
|
||||
unsigned int x;
|
||||
u32 pix;
|
||||
|
||||
for (x = 0; x < pixels; x++) {
|
||||
pix = le32_to_cpu(sbuf32[x]);
|
||||
/* write red-green-blue to output in little endianness */
|
||||
*dbuf8++ = (pix & 0x00ff0000) >> 16;
|
||||
*dbuf8++ = (pix & 0x0000ff00) >> 8;
|
||||
*dbuf8++ = (pix & 0x000000ff) >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_fb_xrgb8888_to_bgr888 - Convert XRGB8888 to BGR888 clip buffer
|
||||
* @dst: Array of BGR888 destination buffers
|
||||
* @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
|
||||
* within @dst; can be NULL if scanlines are stored next to each other.
|
||||
* @src: Array of XRGB8888 source buffers
|
||||
* @fb: DRM framebuffer
|
||||
* @clip: Clip rectangle area to copy
|
||||
* @state: Transform and conversion state
|
||||
*
|
||||
* This function copies parts of a framebuffer to display memory and converts the
|
||||
* color format during the process. Destination and framebuffer formats must match. The
|
||||
* parameters @dst, @dst_pitch and @src refer to arrays. Each array must have at
|
||||
* least as many entries as there are planes in @fb's format. Each entry stores the
|
||||
* value for the format's respective color plane at the same index.
|
||||
*
|
||||
* This function does not apply clipping on @dst (i.e. the destination is at the
|
||||
* top-left corner).
|
||||
*
|
||||
* Drivers can use this function for BGR888 devices that don't natively
|
||||
* support XRGB8888.
|
||||
*/
|
||||
void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch,
|
||||
const struct iosys_map *src, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state)
|
||||
{
|
||||
static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = {
|
||||
3,
|
||||
};
|
||||
|
||||
drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state,
|
||||
drm_fb_xrgb8888_to_bgr888_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_bgr888);
|
||||
|
||||
static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
__le32 *dbuf32 = dbuf;
|
||||
@@ -1104,6 +1155,9 @@ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t d
|
||||
} else if (dst_format == DRM_FORMAT_RGB888) {
|
||||
drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip, state);
|
||||
return 0;
|
||||
} else if (dst_format == DRM_FORMAT_BGR888) {
|
||||
drm_fb_xrgb8888_to_bgr888(dst, dst_pitch, src, fb, clip, state);
|
||||
return 0;
|
||||
} else if (dst_format == DRM_FORMAT_ARGB8888) {
|
||||
drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip, state);
|
||||
return 0;
|
||||
|
||||
@@ -348,7 +348,7 @@ int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
|
||||
return -ENOENT;
|
||||
|
||||
/* Don't allow imported objects to be mapped */
|
||||
if (obj->import_attach) {
|
||||
if (drm_gem_is_imported(obj)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@@ -1178,7 +1178,7 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent,
|
||||
drm_vma_node_start(&obj->vma_node));
|
||||
drm_printf_indent(p, indent, "size=%zu\n", obj->size);
|
||||
drm_printf_indent(p, indent, "imported=%s\n",
|
||||
str_yes_no(obj->import_attach));
|
||||
str_yes_no(drm_gem_is_imported(obj)));
|
||||
|
||||
if (obj->funcs->print_info)
|
||||
obj->funcs->print_info(p, indent, obj);
|
||||
|
||||
@@ -228,9 +228,9 @@ void drm_gem_dma_free(struct drm_gem_dma_object *dma_obj)
|
||||
struct drm_gem_object *gem_obj = &dma_obj->base;
|
||||
struct iosys_map map = IOSYS_MAP_INIT_VADDR(dma_obj->vaddr);
|
||||
|
||||
if (gem_obj->import_attach) {
|
||||
if (drm_gem_is_imported(gem_obj)) {
|
||||
if (dma_obj->vaddr)
|
||||
dma_buf_vunmap_unlocked(gem_obj->import_attach->dmabuf, &map);
|
||||
dma_buf_vunmap_unlocked(gem_obj->dma_buf, &map);
|
||||
drm_prime_gem_destroy(gem_obj, dma_obj->sgt);
|
||||
} else if (dma_obj->vaddr) {
|
||||
if (dma_obj->map_noncoherent)
|
||||
|
||||
@@ -419,7 +419,6 @@ EXPORT_SYMBOL(drm_gem_fb_vunmap);
|
||||
static void __drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir,
|
||||
unsigned int num_planes)
|
||||
{
|
||||
struct dma_buf_attachment *import_attach;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
@@ -428,10 +427,9 @@ static void __drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_dat
|
||||
obj = drm_gem_fb_get_obj(fb, num_planes);
|
||||
if (!obj)
|
||||
continue;
|
||||
import_attach = obj->import_attach;
|
||||
if (!import_attach)
|
||||
if (!drm_gem_is_imported(obj))
|
||||
continue;
|
||||
ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
|
||||
ret = dma_buf_end_cpu_access(obj->dma_buf, dir);
|
||||
if (ret)
|
||||
drm_err(fb->dev, "dma_buf_end_cpu_access(%u, %d) failed: %d\n",
|
||||
ret, num_planes, dir);
|
||||
@@ -454,7 +452,6 @@ static void __drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_dat
|
||||
*/
|
||||
int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
|
||||
{
|
||||
struct dma_buf_attachment *import_attach;
|
||||
struct drm_gem_object *obj;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
@@ -465,10 +462,9 @@ int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direct
|
||||
ret = -EINVAL;
|
||||
goto err___drm_gem_fb_end_cpu_access;
|
||||
}
|
||||
import_attach = obj->import_attach;
|
||||
if (!import_attach)
|
||||
if (!drm_gem_is_imported(obj))
|
||||
continue;
|
||||
ret = dma_buf_begin_cpu_access(import_attach->dmabuf, dir);
|
||||
ret = dma_buf_begin_cpu_access(obj->dma_buf, dir);
|
||||
if (ret)
|
||||
goto err___drm_gem_fb_end_cpu_access;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
|
||||
if (obj->import_attach) {
|
||||
if (drm_gem_is_imported(obj)) {
|
||||
drm_prime_gem_destroy(obj, shmem->sgt);
|
||||
} else {
|
||||
dma_resv_lock(shmem->base.resv, NULL);
|
||||
@@ -255,7 +255,7 @@ int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem)
|
||||
|
||||
dma_resv_assert_held(shmem->base.resv);
|
||||
|
||||
drm_WARN_ON(shmem->base.dev, shmem->base.import_attach);
|
||||
drm_WARN_ON(shmem->base.dev, drm_gem_is_imported(&shmem->base));
|
||||
|
||||
ret = drm_gem_shmem_get_pages(shmem);
|
||||
|
||||
@@ -286,7 +286,7 @@ int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem)
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
int ret;
|
||||
|
||||
drm_WARN_ON(obj->dev, obj->import_attach);
|
||||
drm_WARN_ON(obj->dev, drm_gem_is_imported(obj));
|
||||
|
||||
ret = dma_resv_lock_interruptible(shmem->base.resv, NULL);
|
||||
if (ret)
|
||||
@@ -309,7 +309,7 @@ void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
|
||||
drm_WARN_ON(obj->dev, obj->import_attach);
|
||||
drm_WARN_ON(obj->dev, drm_gem_is_imported(obj));
|
||||
|
||||
dma_resv_lock(shmem->base.resv, NULL);
|
||||
drm_gem_shmem_unpin_locked(shmem);
|
||||
@@ -338,11 +338,11 @@ int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem,
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
int ret = 0;
|
||||
|
||||
if (obj->import_attach) {
|
||||
ret = dma_buf_vmap(obj->import_attach->dmabuf, map);
|
||||
if (drm_gem_is_imported(obj)) {
|
||||
ret = dma_buf_vmap(obj->dma_buf, map);
|
||||
if (!ret) {
|
||||
if (drm_WARN_ON(obj->dev, map->is_iomem)) {
|
||||
dma_buf_vunmap(obj->import_attach->dmabuf, map);
|
||||
dma_buf_vunmap(obj->dma_buf, map);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
@@ -378,7 +378,7 @@ int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem,
|
||||
return 0;
|
||||
|
||||
err_put_pages:
|
||||
if (!obj->import_attach)
|
||||
if (!drm_gem_is_imported(obj))
|
||||
drm_gem_shmem_put_pages(shmem);
|
||||
err_zero_use:
|
||||
shmem->vmap_use_count = 0;
|
||||
@@ -404,8 +404,8 @@ void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem,
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
|
||||
if (obj->import_attach) {
|
||||
dma_buf_vunmap(obj->import_attach->dmabuf, map);
|
||||
if (drm_gem_is_imported(obj)) {
|
||||
dma_buf_vunmap(obj->dma_buf, map);
|
||||
} else {
|
||||
dma_resv_assert_held(shmem->base.resv);
|
||||
|
||||
@@ -566,7 +566,7 @@ static void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
|
||||
struct drm_gem_object *obj = vma->vm_private_data;
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
drm_WARN_ON(obj->dev, obj->import_attach);
|
||||
drm_WARN_ON(obj->dev, drm_gem_is_imported(obj));
|
||||
|
||||
dma_resv_lock(shmem->base.resv, NULL);
|
||||
|
||||
@@ -618,7 +618,7 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
int ret;
|
||||
|
||||
if (obj->import_attach) {
|
||||
if (drm_gem_is_imported(obj)) {
|
||||
/* Reset both vm_ops and vm_private_data, so we don't end up with
|
||||
* vm_ops pointing to our implementation if the dma-buf backend
|
||||
* doesn't set those fields.
|
||||
@@ -663,7 +663,7 @@ EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap);
|
||||
void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
|
||||
struct drm_printer *p, unsigned int indent)
|
||||
{
|
||||
if (shmem->base.import_attach)
|
||||
if (drm_gem_is_imported(&shmem->base))
|
||||
return;
|
||||
|
||||
drm_printf_indent(p, indent, "pages_use_count=%u\n", shmem->pages_use_count);
|
||||
@@ -690,7 +690,7 @@ struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
|
||||
drm_WARN_ON(obj->dev, obj->import_attach);
|
||||
drm_WARN_ON(obj->dev, drm_gem_is_imported(obj));
|
||||
|
||||
return drm_prime_pages_to_sg(obj->dev, shmem->pages, obj->size >> PAGE_SHIFT);
|
||||
}
|
||||
@@ -705,7 +705,7 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_
|
||||
if (shmem->sgt)
|
||||
return shmem->sgt;
|
||||
|
||||
drm_WARN_ON(obj->dev, obj->import_attach);
|
||||
drm_WARN_ON(obj->dev, drm_gem_is_imported(obj));
|
||||
|
||||
ret = drm_gem_shmem_get_pages(shmem);
|
||||
if (ret)
|
||||
|
||||
@@ -218,7 +218,7 @@ int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
if (swap)
|
||||
drm_fb_swab(&dst_map, NULL, src, fb, clip, !gem->import_attach,
|
||||
drm_fb_swab(&dst_map, NULL, src, fb, clip, !drm_gem_is_imported(gem),
|
||||
fmtcnv_state);
|
||||
else
|
||||
drm_fb_memcpy(&dst_map, NULL, src, fb, clip);
|
||||
|
||||
@@ -162,13 +162,13 @@ of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
|
||||
u32 reg;
|
||||
|
||||
if (of_alias_from_compatible(node, info.type, sizeof(info.type)) < 0) {
|
||||
drm_err(host, "modalias failure on %pOF\n", node);
|
||||
dev_err(host->dev, "modalias failure on %pOF\n", node);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "reg", ®);
|
||||
if (ret) {
|
||||
drm_err(host, "device node %pOF has no valid reg property: %d\n",
|
||||
dev_err(host->dev, "device node %pOF has no valid reg property: %d\n",
|
||||
node, ret);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
@@ -206,18 +206,18 @@ mipi_dsi_device_register_full(struct mipi_dsi_host *host,
|
||||
int ret;
|
||||
|
||||
if (!info) {
|
||||
drm_err(host, "invalid mipi_dsi_device_info pointer\n");
|
||||
dev_err(host->dev, "invalid mipi_dsi_device_info pointer\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (info->channel > 3) {
|
||||
drm_err(host, "invalid virtual channel: %u\n", info->channel);
|
||||
dev_err(host->dev, "invalid virtual channel: %u\n", info->channel);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
dsi = mipi_dsi_device_alloc(host);
|
||||
if (IS_ERR(dsi)) {
|
||||
drm_err(host, "failed to allocate DSI device %ld\n",
|
||||
dev_err(host->dev, "failed to allocate DSI device %ld\n",
|
||||
PTR_ERR(dsi));
|
||||
return dsi;
|
||||
}
|
||||
@@ -228,7 +228,7 @@ mipi_dsi_device_register_full(struct mipi_dsi_host *host,
|
||||
|
||||
ret = mipi_dsi_device_add(dsi);
|
||||
if (ret) {
|
||||
drm_err(host, "failed to add DSI device %d\n", ret);
|
||||
dev_err(host->dev, "failed to add DSI device %d\n", ret);
|
||||
kfree(dsi);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
@@ -453,13 +453,7 @@ struct dma_buf *drm_gem_prime_handle_to_dmabuf(struct drm_device *dev,
|
||||
}
|
||||
|
||||
mutex_lock(&dev->object_name_lock);
|
||||
/* re-export the original imported object */
|
||||
if (obj->import_attach) {
|
||||
dmabuf = obj->import_attach->dmabuf;
|
||||
get_dma_buf(dmabuf);
|
||||
goto out_have_obj;
|
||||
}
|
||||
|
||||
/* re-export the original imported/exported object */
|
||||
if (obj->dma_buf) {
|
||||
get_dma_buf(obj->dma_buf);
|
||||
dmabuf = obj->dma_buf;
|
||||
|
||||
@@ -215,7 +215,7 @@ static void cdv_errata(struct drm_device *dev)
|
||||
* Bonus Launch to work around the issue, by degrading
|
||||
* performance.
|
||||
*/
|
||||
CDV_MSG_WRITE32(pci_domain_nr(pdev->bus), 3, 0x30, 0x08027108);
|
||||
CDV_MSG_WRITE32(pci_domain_nr(pdev->bus), 3, 0x30, 0x08027108);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -855,8 +855,7 @@ cdv_intel_dp_i2c_init(struct gma_connector *connector,
|
||||
|
||||
memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter));
|
||||
intel_dp->adapter.owner = THIS_MODULE;
|
||||
strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
|
||||
intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
|
||||
strscpy(intel_dp->adapter.name, name);
|
||||
intel_dp->adapter.algo_data = &intel_dp->algo;
|
||||
intel_dp->adapter.dev.parent = connector->base.kdev;
|
||||
|
||||
|
||||
@@ -3,25 +3,30 @@
|
||||
#define __NVIF_IOCTL_H__
|
||||
|
||||
struct nvif_ioctl_v0 {
|
||||
__u8 version;
|
||||
/* New members MUST be added within the struct_group() macro below. */
|
||||
struct_group_tagged(nvif_ioctl_v0_hdr, __hdr,
|
||||
__u8 version;
|
||||
#define NVIF_IOCTL_V0_SCLASS 0x01
|
||||
#define NVIF_IOCTL_V0_NEW 0x02
|
||||
#define NVIF_IOCTL_V0_DEL 0x03
|
||||
#define NVIF_IOCTL_V0_MTHD 0x04
|
||||
#define NVIF_IOCTL_V0_MAP 0x07
|
||||
#define NVIF_IOCTL_V0_UNMAP 0x08
|
||||
__u8 type;
|
||||
__u8 pad02[4];
|
||||
__u8 type;
|
||||
__u8 pad02[4];
|
||||
#define NVIF_IOCTL_V0_OWNER_NVIF 0x00
|
||||
#define NVIF_IOCTL_V0_OWNER_ANY 0xff
|
||||
__u8 owner;
|
||||
__u8 owner;
|
||||
#define NVIF_IOCTL_V0_ROUTE_NVIF 0x00
|
||||
#define NVIF_IOCTL_V0_ROUTE_HIDDEN 0xff
|
||||
__u8 route;
|
||||
__u64 token;
|
||||
__u64 object;
|
||||
__u8 route;
|
||||
__u64 token;
|
||||
__u64 object;
|
||||
);
|
||||
__u8 data[]; /* ioctl data (below) */
|
||||
};
|
||||
static_assert(offsetof(struct nvif_ioctl_v0, data) == sizeof(struct nvif_ioctl_v0_hdr),
|
||||
"struct member likely outside of struct_group()");
|
||||
|
||||
struct nvif_ioctl_sclass_v0 {
|
||||
/* nvif_ioctl ... */
|
||||
@@ -51,12 +56,17 @@ struct nvif_ioctl_del {
|
||||
};
|
||||
|
||||
struct nvif_ioctl_mthd_v0 {
|
||||
/* nvif_ioctl ... */
|
||||
__u8 version;
|
||||
__u8 method;
|
||||
__u8 pad02[6];
|
||||
/* New members MUST be added within the struct_group() macro below. */
|
||||
struct_group_tagged(nvif_ioctl_mthd_v0_hdr, __hdr,
|
||||
/* nvif_ioctl ... */
|
||||
__u8 version;
|
||||
__u8 method;
|
||||
__u8 pad02[6];
|
||||
);
|
||||
__u8 data[]; /* method data (class.h) */
|
||||
};
|
||||
static_assert(offsetof(struct nvif_ioctl_mthd_v0, data) == sizeof(struct nvif_ioctl_mthd_v0_hdr),
|
||||
"struct member likely outside of struct_group()");
|
||||
|
||||
struct nvif_ioctl_map_v0 {
|
||||
/* nvif_ioctl ... */
|
||||
|
||||
@@ -79,8 +79,8 @@ struct nouveau_svm {
|
||||
#define SVM_ERR(s,f,a...) NV_WARN((s)->drm, "svm: "f"\n", ##a)
|
||||
|
||||
struct nouveau_pfnmap_args {
|
||||
struct nvif_ioctl_v0 i;
|
||||
struct nvif_ioctl_mthd_v0 m;
|
||||
struct nvif_ioctl_v0_hdr i;
|
||||
struct nvif_ioctl_mthd_v0_hdr m;
|
||||
struct nvif_vmm_pfnmap_v0 p;
|
||||
};
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ int
|
||||
nvif_object_sclass_get(struct nvif_object *object, struct nvif_sclass **psclass)
|
||||
{
|
||||
struct {
|
||||
struct nvif_ioctl_v0 ioctl;
|
||||
struct nvif_ioctl_v0_hdr ioctl;
|
||||
struct nvif_ioctl_sclass_v0 sclass;
|
||||
} *args = NULL;
|
||||
int ret, cnt = 0, i;
|
||||
@@ -101,7 +101,7 @@ int
|
||||
nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size)
|
||||
{
|
||||
struct {
|
||||
struct nvif_ioctl_v0 ioctl;
|
||||
struct nvif_ioctl_v0_hdr ioctl;
|
||||
struct nvif_ioctl_mthd_v0 mthd;
|
||||
} *args;
|
||||
u32 args_size;
|
||||
@@ -135,7 +135,7 @@ void
|
||||
nvif_object_unmap_handle(struct nvif_object *object)
|
||||
{
|
||||
struct {
|
||||
struct nvif_ioctl_v0 ioctl;
|
||||
struct nvif_ioctl_v0_hdr ioctl;
|
||||
struct nvif_ioctl_unmap unmap;
|
||||
} args = {
|
||||
.ioctl.type = NVIF_IOCTL_V0_UNMAP,
|
||||
@@ -149,7 +149,7 @@ nvif_object_map_handle(struct nvif_object *object, void *argv, u32 argc,
|
||||
u64 *handle, u64 *length)
|
||||
{
|
||||
struct {
|
||||
struct nvif_ioctl_v0 ioctl;
|
||||
struct nvif_ioctl_v0_hdr ioctl;
|
||||
struct nvif_ioctl_map_v0 map;
|
||||
} *args;
|
||||
u32 argn = sizeof(*args) + argc;
|
||||
@@ -211,7 +211,7 @@ void
|
||||
nvif_object_dtor(struct nvif_object *object)
|
||||
{
|
||||
struct {
|
||||
struct nvif_ioctl_v0 ioctl;
|
||||
struct nvif_ioctl_v0_hdr ioctl;
|
||||
struct nvif_ioctl_del del;
|
||||
} args = {
|
||||
.ioctl.type = NVIF_IOCTL_V0_DEL,
|
||||
@@ -230,7 +230,7 @@ nvif_object_ctor(struct nvif_object *parent, const char *name, u32 handle,
|
||||
s32 oclass, void *data, u32 size, struct nvif_object *object)
|
||||
{
|
||||
struct {
|
||||
struct nvif_ioctl_v0 ioctl;
|
||||
struct nvif_ioctl_v0_hdr ioctl;
|
||||
struct nvif_ioctl_new_v0 new;
|
||||
} *args;
|
||||
int ret = 0;
|
||||
|
||||
@@ -1020,6 +1020,8 @@ config DRM_PANEL_VISIONOX_RM692E5
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select DRM_DISPLAY_DSC_HELPER
|
||||
select DRM_DISPLAY_HELPER
|
||||
help
|
||||
Say Y here if you want to enable support for Visionox RM692E5 amoled
|
||||
display panels, such as the one found in the Nothing Phone (1)
|
||||
|
||||
@@ -636,8 +636,8 @@ static int panthor_fw_read_build_info(struct panthor_device *ptdev,
|
||||
u32 ehdr)
|
||||
{
|
||||
struct panthor_fw_build_info_hdr hdr;
|
||||
char header[9];
|
||||
const char git_sha_header[sizeof(header)] = "git_sha: ";
|
||||
static const char git_sha_header[] = "git_sha: ";
|
||||
const int header_len = sizeof(git_sha_header) - 1;
|
||||
int ret;
|
||||
|
||||
ret = panthor_fw_binary_iter_read(ptdev, iter, &hdr, sizeof(hdr));
|
||||
@@ -651,8 +651,7 @@ static int panthor_fw_read_build_info(struct panthor_device *ptdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memcmp(git_sha_header, fw->data + hdr.meta_start,
|
||||
sizeof(git_sha_header))) {
|
||||
if (memcmp(git_sha_header, fw->data + hdr.meta_start, header_len)) {
|
||||
/* Not the expected header, this isn't metadata we understand */
|
||||
return 0;
|
||||
}
|
||||
@@ -665,7 +664,7 @@ static int panthor_fw_read_build_info(struct panthor_device *ptdev,
|
||||
}
|
||||
|
||||
drm_info(&ptdev->base, "Firmware git sha: %s\n",
|
||||
fw->data + hdr.meta_start + sizeof(git_sha_header));
|
||||
fw->data + hdr.meta_start + header_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -102,9 +102,9 @@ struct panthor_fw_cs_output_iface {
|
||||
#define CS_STATUS_BLOCKED_REASON_SB_WAIT 1
|
||||
#define CS_STATUS_BLOCKED_REASON_PROGRESS_WAIT 2
|
||||
#define CS_STATUS_BLOCKED_REASON_SYNC_WAIT 3
|
||||
#define CS_STATUS_BLOCKED_REASON_DEFERRED 5
|
||||
#define CS_STATUS_BLOCKED_REASON_RES 6
|
||||
#define CS_STATUS_BLOCKED_REASON_FLUSH 7
|
||||
#define CS_STATUS_BLOCKED_REASON_DEFERRED 4
|
||||
#define CS_STATUS_BLOCKED_REASON_RESOURCE 5
|
||||
#define CS_STATUS_BLOCKED_REASON_FLUSH 6
|
||||
#define CS_STATUS_BLOCKED_REASON_MASK GENMASK(3, 0)
|
||||
u32 status_blocked_reason;
|
||||
u32 status_wait_sync_value_hi;
|
||||
|
||||
@@ -97,6 +97,9 @@ struct panthor_heap_pool {
|
||||
|
||||
/** @gpu_contexts: Buffer object containing the GPU heap contexts. */
|
||||
struct panthor_kernel_bo *gpu_contexts;
|
||||
|
||||
/** @size: Size of all chunks across all heaps in the pool. */
|
||||
atomic_t size;
|
||||
};
|
||||
|
||||
static int panthor_heap_ctx_stride(struct panthor_device *ptdev)
|
||||
@@ -118,7 +121,7 @@ static void *panthor_get_heap_ctx(struct panthor_heap_pool *pool, int id)
|
||||
panthor_get_heap_ctx_offset(pool, id);
|
||||
}
|
||||
|
||||
static void panthor_free_heap_chunk(struct panthor_vm *vm,
|
||||
static void panthor_free_heap_chunk(struct panthor_heap_pool *pool,
|
||||
struct panthor_heap *heap,
|
||||
struct panthor_heap_chunk *chunk)
|
||||
{
|
||||
@@ -127,12 +130,13 @@ static void panthor_free_heap_chunk(struct panthor_vm *vm,
|
||||
heap->chunk_count--;
|
||||
mutex_unlock(&heap->lock);
|
||||
|
||||
atomic_sub(heap->chunk_size, &pool->size);
|
||||
|
||||
panthor_kernel_bo_destroy(chunk->bo);
|
||||
kfree(chunk);
|
||||
}
|
||||
|
||||
static int panthor_alloc_heap_chunk(struct panthor_device *ptdev,
|
||||
struct panthor_vm *vm,
|
||||
static int panthor_alloc_heap_chunk(struct panthor_heap_pool *pool,
|
||||
struct panthor_heap *heap,
|
||||
bool initial_chunk)
|
||||
{
|
||||
@@ -144,7 +148,7 @@ static int panthor_alloc_heap_chunk(struct panthor_device *ptdev,
|
||||
if (!chunk)
|
||||
return -ENOMEM;
|
||||
|
||||
chunk->bo = panthor_kernel_bo_create(ptdev, vm, heap->chunk_size,
|
||||
chunk->bo = panthor_kernel_bo_create(pool->ptdev, pool->vm, heap->chunk_size,
|
||||
DRM_PANTHOR_BO_NO_MMAP,
|
||||
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC,
|
||||
PANTHOR_VM_KERNEL_AUTO_VA);
|
||||
@@ -180,6 +184,8 @@ static int panthor_alloc_heap_chunk(struct panthor_device *ptdev,
|
||||
heap->chunk_count++;
|
||||
mutex_unlock(&heap->lock);
|
||||
|
||||
atomic_add(heap->chunk_size, &pool->size);
|
||||
|
||||
return 0;
|
||||
|
||||
err_destroy_bo:
|
||||
@@ -191,17 +197,16 @@ err_free_chunk:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void panthor_free_heap_chunks(struct panthor_vm *vm,
|
||||
static void panthor_free_heap_chunks(struct panthor_heap_pool *pool,
|
||||
struct panthor_heap *heap)
|
||||
{
|
||||
struct panthor_heap_chunk *chunk, *tmp;
|
||||
|
||||
list_for_each_entry_safe(chunk, tmp, &heap->chunks, node)
|
||||
panthor_free_heap_chunk(vm, heap, chunk);
|
||||
panthor_free_heap_chunk(pool, heap, chunk);
|
||||
}
|
||||
|
||||
static int panthor_alloc_heap_chunks(struct panthor_device *ptdev,
|
||||
struct panthor_vm *vm,
|
||||
static int panthor_alloc_heap_chunks(struct panthor_heap_pool *pool,
|
||||
struct panthor_heap *heap,
|
||||
u32 chunk_count)
|
||||
{
|
||||
@@ -209,7 +214,7 @@ static int panthor_alloc_heap_chunks(struct panthor_device *ptdev,
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < chunk_count; i++) {
|
||||
ret = panthor_alloc_heap_chunk(ptdev, vm, heap, true);
|
||||
ret = panthor_alloc_heap_chunk(pool, heap, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -226,7 +231,7 @@ panthor_heap_destroy_locked(struct panthor_heap_pool *pool, u32 handle)
|
||||
if (!heap)
|
||||
return -EINVAL;
|
||||
|
||||
panthor_free_heap_chunks(pool->vm, heap);
|
||||
panthor_free_heap_chunks(pool, heap);
|
||||
mutex_destroy(&heap->lock);
|
||||
kfree(heap);
|
||||
return 0;
|
||||
@@ -308,8 +313,7 @@ int panthor_heap_create(struct panthor_heap_pool *pool,
|
||||
heap->max_chunks = max_chunks;
|
||||
heap->target_in_flight = target_in_flight;
|
||||
|
||||
ret = panthor_alloc_heap_chunks(pool->ptdev, vm, heap,
|
||||
initial_chunk_count);
|
||||
ret = panthor_alloc_heap_chunks(pool, heap, initial_chunk_count);
|
||||
if (ret)
|
||||
goto err_free_heap;
|
||||
|
||||
@@ -342,7 +346,7 @@ int panthor_heap_create(struct panthor_heap_pool *pool,
|
||||
return id;
|
||||
|
||||
err_free_heap:
|
||||
panthor_free_heap_chunks(pool->vm, heap);
|
||||
panthor_free_heap_chunks(pool, heap);
|
||||
mutex_destroy(&heap->lock);
|
||||
kfree(heap);
|
||||
|
||||
@@ -389,6 +393,7 @@ int panthor_heap_return_chunk(struct panthor_heap_pool *pool,
|
||||
removed = chunk;
|
||||
list_del(&chunk->node);
|
||||
heap->chunk_count--;
|
||||
atomic_sub(heap->chunk_size, &pool->size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -466,7 +471,7 @@ int panthor_heap_grow(struct panthor_heap_pool *pool,
|
||||
* further jobs in this queue fail immediately instead of having to
|
||||
* wait for the job timeout.
|
||||
*/
|
||||
ret = panthor_alloc_heap_chunk(pool->ptdev, pool->vm, heap, false);
|
||||
ret = panthor_alloc_heap_chunk(pool, heap, false);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
@@ -560,6 +565,8 @@ panthor_heap_pool_create(struct panthor_device *ptdev, struct panthor_vm *vm)
|
||||
if (ret)
|
||||
goto err_destroy_pool;
|
||||
|
||||
atomic_add(pool->gpu_contexts->obj->size, &pool->size);
|
||||
|
||||
return pool;
|
||||
|
||||
err_destroy_pool:
|
||||
@@ -594,8 +601,10 @@ void panthor_heap_pool_destroy(struct panthor_heap_pool *pool)
|
||||
xa_for_each(&pool->xa, i, heap)
|
||||
drm_WARN_ON(&pool->ptdev->base, panthor_heap_destroy_locked(pool, i));
|
||||
|
||||
if (!IS_ERR_OR_NULL(pool->gpu_contexts))
|
||||
if (!IS_ERR_OR_NULL(pool->gpu_contexts)) {
|
||||
atomic_sub(pool->gpu_contexts->obj->size, &pool->size);
|
||||
panthor_kernel_bo_destroy(pool->gpu_contexts);
|
||||
}
|
||||
|
||||
/* Reflects the fact the pool has been destroyed. */
|
||||
pool->vm = NULL;
|
||||
@@ -605,27 +614,16 @@ void panthor_heap_pool_destroy(struct panthor_heap_pool *pool)
|
||||
}
|
||||
|
||||
/**
|
||||
* panthor_heap_pool_size() - Calculate size of all chunks across all heaps in a pool
|
||||
* @pool: Pool whose total chunk size to calculate.
|
||||
* panthor_heap_pool_size() - Get a heap pool's total size
|
||||
* @pool: Pool whose total chunks size to return
|
||||
*
|
||||
* This function adds the size of all heap chunks across all heaps in the
|
||||
* argument pool. It also adds the size of the gpu contexts kernel bo.
|
||||
* It is meant to be used by fdinfo for displaying the size of internal
|
||||
* driver BO's that aren't exposed to userspace through a GEM handle.
|
||||
* Returns the aggregated size of all chunks for all heaps in the pool
|
||||
*
|
||||
*/
|
||||
size_t panthor_heap_pool_size(struct panthor_heap_pool *pool)
|
||||
{
|
||||
struct panthor_heap *heap;
|
||||
unsigned long i;
|
||||
size_t size = 0;
|
||||
if (!pool)
|
||||
return 0;
|
||||
|
||||
down_read(&pool->lock);
|
||||
xa_for_each(&pool->xa, i, heap)
|
||||
size += heap->chunk_size * heap->chunk_count;
|
||||
up_read(&pool->lock);
|
||||
|
||||
size += pool->gpu_contexts->obj->size;
|
||||
|
||||
return size;
|
||||
return atomic_read(&pool->size);
|
||||
}
|
||||
|
||||
@@ -1963,13 +1963,7 @@ void panthor_vm_heaps_sizes(struct panthor_file *pfile, struct drm_memory_stats
|
||||
|
||||
xa_lock(&pfile->vms->xa);
|
||||
xa_for_each(&pfile->vms->xa, i, vm) {
|
||||
size_t size = 0;
|
||||
|
||||
mutex_lock(&vm->heaps.lock);
|
||||
if (vm->heaps.pool)
|
||||
size = panthor_heap_pool_size(vm->heaps.pool);
|
||||
mutex_unlock(&vm->heaps.lock);
|
||||
|
||||
size_t size = panthor_heap_pool_size(vm->heaps.pool);
|
||||
stats->resident += size;
|
||||
if (vm->as.id >= 0)
|
||||
stats->active += size;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <drm/panthor_drm.h>
|
||||
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@@ -631,10 +632,10 @@ struct panthor_group {
|
||||
struct panthor_gpu_usage data;
|
||||
|
||||
/**
|
||||
* @lock: Mutex to govern concurrent access from drm file's fdinfo callback
|
||||
* and job post-completion processing function
|
||||
* @fdinfo.lock: Spinlock to govern concurrent access from drm file's fdinfo
|
||||
* callback and job post-completion processing function
|
||||
*/
|
||||
struct mutex lock;
|
||||
spinlock_t lock;
|
||||
|
||||
/** @fdinfo.kbo_sizes: Aggregate size of private kernel BO's held by the group. */
|
||||
size_t kbo_sizes;
|
||||
@@ -910,8 +911,6 @@ static void group_release_work(struct work_struct *work)
|
||||
release_work);
|
||||
u32 i;
|
||||
|
||||
mutex_destroy(&group->fdinfo.lock);
|
||||
|
||||
for (i = 0; i < group->queue_count; i++)
|
||||
group_free_queue(group, group->queues[i]);
|
||||
|
||||
@@ -2861,12 +2860,12 @@ static void update_fdinfo_stats(struct panthor_job *job)
|
||||
struct panthor_job_profiling_data *slots = queue->profiling.slots->kmap;
|
||||
struct panthor_job_profiling_data *data = &slots[job->profiling.slot];
|
||||
|
||||
mutex_lock(&group->fdinfo.lock);
|
||||
if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_CYCLES)
|
||||
fdinfo->cycles += data->cycles.after - data->cycles.before;
|
||||
if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_TIMESTAMP)
|
||||
fdinfo->time += data->time.after - data->time.before;
|
||||
mutex_unlock(&group->fdinfo.lock);
|
||||
scoped_guard(spinlock, &group->fdinfo.lock) {
|
||||
if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_CYCLES)
|
||||
fdinfo->cycles += data->cycles.after - data->cycles.before;
|
||||
if (job->profiling.mask & PANTHOR_DEVICE_PROFILING_TIMESTAMP)
|
||||
fdinfo->time += data->time.after - data->time.before;
|
||||
}
|
||||
}
|
||||
|
||||
void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile)
|
||||
@@ -2880,12 +2879,11 @@ void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile)
|
||||
|
||||
xa_lock(&gpool->xa);
|
||||
xa_for_each(&gpool->xa, i, group) {
|
||||
mutex_lock(&group->fdinfo.lock);
|
||||
guard(spinlock)(&group->fdinfo.lock);
|
||||
pfile->stats.cycles += group->fdinfo.data.cycles;
|
||||
pfile->stats.time += group->fdinfo.data.time;
|
||||
group->fdinfo.data.cycles = 0;
|
||||
group->fdinfo.data.time = 0;
|
||||
mutex_unlock(&group->fdinfo.lock);
|
||||
}
|
||||
xa_unlock(&gpool->xa);
|
||||
}
|
||||
@@ -3537,7 +3535,7 @@ int panthor_group_create(struct panthor_file *pfile,
|
||||
mutex_unlock(&sched->reset.lock);
|
||||
|
||||
add_group_kbo_sizes(group->ptdev, group);
|
||||
mutex_init(&group->fdinfo.lock);
|
||||
spin_lock_init(&group->fdinfo.lock);
|
||||
|
||||
return gid;
|
||||
|
||||
|
||||
@@ -32,27 +32,32 @@
|
||||
|
||||
#include "rockchip_drm_drv.h"
|
||||
|
||||
#define RK3288_GRF_SOC_CON6 0x25c
|
||||
#define RK3288_EDP_LCDC_SEL BIT(5)
|
||||
#define RK3399_GRF_SOC_CON20 0x6250
|
||||
#define RK3399_EDP_LCDC_SEL BIT(5)
|
||||
|
||||
#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
|
||||
|
||||
#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS 100
|
||||
|
||||
#define GRF_REG_FIELD(_reg, _lsb, _msb) { \
|
||||
.reg = _reg, \
|
||||
.lsb = _lsb, \
|
||||
.msb = _msb, \
|
||||
.valid = true, \
|
||||
}
|
||||
|
||||
struct rockchip_grf_reg_field {
|
||||
u32 reg;
|
||||
u32 lsb;
|
||||
u32 msb;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rockchip_dp_chip_data - splite the grf setting of kind of chips
|
||||
* @lcdsel_grf_reg: grf register offset of lcdc select
|
||||
* @lcdsel_big: reg value of selecting vop big for eDP
|
||||
* @lcdsel_lit: reg value of selecting vop little for eDP
|
||||
* @lcdc_sel: grf register field of lcdc_sel
|
||||
* @chip_type: specific chip type
|
||||
* @reg: register base address
|
||||
*/
|
||||
struct rockchip_dp_chip_data {
|
||||
u32 lcdsel_grf_reg;
|
||||
u32 lcdsel_big;
|
||||
u32 lcdsel_lit;
|
||||
const struct rockchip_grf_reg_field lcdc_sel;
|
||||
u32 chip_type;
|
||||
u32 reg;
|
||||
};
|
||||
|
||||
struct rockchip_dp_device {
|
||||
@@ -84,6 +89,26 @@ static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_da
|
||||
return container_of(plat_data, struct rockchip_dp_device, plat_data);
|
||||
}
|
||||
|
||||
static int rockchip_grf_write(struct regmap *grf, u32 reg, u32 mask, u32 val)
|
||||
{
|
||||
return regmap_write(grf, reg, (mask << 16) | (val & mask));
|
||||
}
|
||||
|
||||
static int rockchip_grf_field_write(struct regmap *grf,
|
||||
const struct rockchip_grf_reg_field *field,
|
||||
u32 val)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
if (!field->valid)
|
||||
return 0;
|
||||
|
||||
mask = GENMASK(field->msb, field->lsb);
|
||||
val <<= field->lsb;
|
||||
|
||||
return rockchip_grf_write(grf, field->reg, mask, val);
|
||||
}
|
||||
|
||||
static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
|
||||
{
|
||||
reset_control_assert(dp->rst);
|
||||
@@ -181,7 +206,6 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *old_crtc_state;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
crtc = rockchip_dp_drm_get_new_crtc(encoder, state);
|
||||
if (!crtc)
|
||||
@@ -192,24 +216,19 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
|
||||
if (old_crtc_state && old_crtc_state->self_refresh_active)
|
||||
return;
|
||||
|
||||
ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
if (ret)
|
||||
val = dp->data->lcdsel_lit;
|
||||
else
|
||||
val = dp->data->lcdsel_big;
|
||||
|
||||
DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
|
||||
|
||||
ret = clk_prepare_enable(dp->grfclk);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(dp->dev, "failed to enable grfclk %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val);
|
||||
ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
|
||||
|
||||
ret = rockchip_grf_field_write(dp->grf, &dp->data->lcdc_sel, ret);
|
||||
if (ret != 0)
|
||||
DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
|
||||
|
||||
@@ -379,6 +398,8 @@ static int rockchip_dp_probe(struct platform_device *pdev)
|
||||
const struct rockchip_dp_chip_data *dp_data;
|
||||
struct drm_panel *panel = NULL;
|
||||
struct rockchip_dp_device *dp;
|
||||
struct resource *res;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
dp_data = of_device_get_match_data(dev);
|
||||
@@ -393,9 +414,24 @@ static int rockchip_dp_probe(struct platform_device *pdev)
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
i = 0;
|
||||
while (dp_data[i].reg) {
|
||||
if (dp_data[i].reg == res->start) {
|
||||
dp->data = &dp_data[i];
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!dp->data)
|
||||
return dev_err_probe(dev, -EINVAL, "no chip-data for %s node\n",
|
||||
dev->of_node->name);
|
||||
|
||||
dp->dev = dev;
|
||||
dp->adp = ERR_PTR(-ENODEV);
|
||||
dp->data = dp_data;
|
||||
dp->plat_data.panel = panel;
|
||||
dp->plat_data.dev_type = dp->data->chip_type;
|
||||
dp->plat_data.power_on = rockchip_dp_poweron;
|
||||
@@ -447,18 +483,22 @@ static int rockchip_dp_resume(struct device *dev)
|
||||
static DEFINE_RUNTIME_DEV_PM_OPS(rockchip_dp_pm_ops, rockchip_dp_suspend,
|
||||
rockchip_dp_resume, NULL);
|
||||
|
||||
static const struct rockchip_dp_chip_data rk3399_edp = {
|
||||
.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
|
||||
.lcdsel_big = HIWORD_UPDATE(0, RK3399_EDP_LCDC_SEL),
|
||||
.lcdsel_lit = HIWORD_UPDATE(RK3399_EDP_LCDC_SEL, RK3399_EDP_LCDC_SEL),
|
||||
.chip_type = RK3399_EDP,
|
||||
static const struct rockchip_dp_chip_data rk3399_edp[] = {
|
||||
{
|
||||
.lcdc_sel = GRF_REG_FIELD(0x6250, 5, 5),
|
||||
.chip_type = RK3399_EDP,
|
||||
.reg = 0xff970000,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct rockchip_dp_chip_data rk3288_dp = {
|
||||
.lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
|
||||
.lcdsel_big = HIWORD_UPDATE(0, RK3288_EDP_LCDC_SEL),
|
||||
.lcdsel_lit = HIWORD_UPDATE(RK3288_EDP_LCDC_SEL, RK3288_EDP_LCDC_SEL),
|
||||
.chip_type = RK3288_DP,
|
||||
static const struct rockchip_dp_chip_data rk3288_dp[] = {
|
||||
{
|
||||
.lcdc_sel = GRF_REG_FIELD(0x025c, 5, 5),
|
||||
.chip_type = RK3288_DP,
|
||||
.reg = 0xff970000,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_dp_dt_ids[] = {
|
||||
|
||||
@@ -203,7 +203,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
|
||||
hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(hdmi->regmap)) {
|
||||
drm_err(hdmi, "Unable to get rockchip,grf\n");
|
||||
dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
|
||||
return PTR_ERR(hdmi->regmap);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
if (IS_ERR(hdmi->ref_clk)) {
|
||||
ret = PTR_ERR(hdmi->ref_clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
drm_err(hdmi, "failed to get reference clock\n");
|
||||
dev_err(hdmi->dev, "failed to get reference clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
if (IS_ERR(hdmi->grf_clk)) {
|
||||
ret = PTR_ERR(hdmi->grf_clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
drm_err(hdmi, "failed to get grf clock\n");
|
||||
dev_err(hdmi->dev, "failed to get grf clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -302,16 +302,16 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
||||
|
||||
ret = clk_prepare_enable(hdmi->grf_clk);
|
||||
if (ret < 0) {
|
||||
drm_err(hdmi, "failed to enable grfclk %d\n", ret);
|
||||
dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
|
||||
if (ret != 0)
|
||||
drm_err(hdmi, "Could not write to GRF: %d\n", ret);
|
||||
dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret);
|
||||
|
||||
clk_disable_unprepare(hdmi->grf_clk);
|
||||
drm_dbg(hdmi, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
|
||||
dev_dbg(hdmi->dev, "vop %s output to hdmi\n", ret ? "LIT" : "BIG");
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -574,7 +574,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
ret = rockchip_hdmi_parse_dt(hdmi);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
drm_err(hdmi, "Unable to parse OF data\n");
|
||||
dev_err(hdmi->dev, "Unable to parse OF data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -582,7 +582,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
if (IS_ERR(hdmi->phy)) {
|
||||
ret = PTR_ERR(hdmi->phy);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
drm_err(hdmi, "failed to get phy\n");
|
||||
dev_err(hdmi->dev, "failed to get phy\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
|
||||
if (drm) {
|
||||
changed = drm_helper_hpd_irq_event(drm);
|
||||
if (changed)
|
||||
drm_dbg(hdmi, "connector status changed\n");
|
||||
dev_dbg(hdmi->dev, "connector status changed\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
||||
}
|
||||
}
|
||||
if (hdmi->port_id < 0) {
|
||||
drm_err(hdmi, "Failed to match HDMI port ID\n");
|
||||
dev_err(hdmi->dev, "Failed to match HDMI port ID\n");
|
||||
return hdmi->port_id;
|
||||
}
|
||||
|
||||
@@ -496,20 +496,20 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
||||
hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"rockchip,grf");
|
||||
if (IS_ERR(hdmi->regmap)) {
|
||||
drm_err(hdmi, "Unable to get rockchip,grf\n");
|
||||
dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
|
||||
return PTR_ERR(hdmi->regmap);
|
||||
}
|
||||
|
||||
hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"rockchip,vo-grf");
|
||||
if (IS_ERR(hdmi->vo_regmap)) {
|
||||
drm_err(hdmi, "Unable to get rockchip,vo-grf\n");
|
||||
dev_err(hdmi->dev, "Unable to get rockchip,vo-grf\n");
|
||||
return PTR_ERR(hdmi->vo_regmap);
|
||||
}
|
||||
|
||||
ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks);
|
||||
if (ret < 0) {
|
||||
drm_err(hdmi, "Failed to get clocks: %d\n", ret);
|
||||
dev_err(hdmi->dev, "Failed to get clocks: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -517,7 +517,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(hdmi->enable_gpio)) {
|
||||
ret = PTR_ERR(hdmi->enable_gpio);
|
||||
drm_err(hdmi, "Failed to request enable GPIO: %d\n", ret);
|
||||
dev_err(hdmi->dev, "Failed to request enable GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -525,7 +525,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
||||
if (IS_ERR(hdmi->phy)) {
|
||||
ret = PTR_ERR(hdmi->phy);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
drm_err(hdmi, "failed to get phy: %d\n", ret);
|
||||
dev_err(hdmi->dev, "failed to get phy: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -564,7 +564,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
|
||||
connector = drm_bridge_connector_init(drm, encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
ret = PTR_ERR(connector);
|
||||
drm_err(hdmi, "failed to init bridge connector: %d\n", ret);
|
||||
dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -484,9 +484,11 @@ static void rockchip_drm_platform_remove(struct platform_device *pdev)
|
||||
|
||||
static void rockchip_drm_platform_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm = platform_get_drvdata(pdev);
|
||||
if (component_master_is_bound(&pdev->dev, &rockchip_drm_ops)) {
|
||||
struct drm_device *drm = platform_get_drvdata(pdev);
|
||||
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id rockchip_drm_dt_ids[] = {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,19 @@
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <dt-bindings/soc/rockchip,vop2.h>
|
||||
#include "rockchip_drm_drv.h"
|
||||
#include "rockchip_drm_vop.h"
|
||||
|
||||
#define VOP2_VERSION(major, minor, build) ((major) << 24 | (minor) << 16 | (build))
|
||||
|
||||
/* The VOP version of new SoC is bigger than the old */
|
||||
#define VOP_VERSION_RK3568 VOP2_VERSION(0x40, 0x15, 0x8023)
|
||||
#define VOP_VERSION_RK3588 VOP2_VERSION(0x40, 0x17, 0x6786)
|
||||
#define VOP_VERSION_RK3528 VOP2_VERSION(0x50, 0x17, 0x1263)
|
||||
#define VOP_VERSION_RK3562 VOP2_VERSION(0x50, 0x17, 0x4350)
|
||||
#define VOP_VERSION_RK3576 VOP2_VERSION(0x50, 0x19, 0x9765)
|
||||
|
||||
#define VOP2_VP_FEATURE_OUTPUT_10BIT BIT(0)
|
||||
|
||||
#define VOP2_FEATURE_HAS_SYS_GRF BIT(0)
|
||||
@@ -34,6 +44,13 @@ enum win_dly_mode {
|
||||
VOP2_DLY_MODE_MAX,
|
||||
};
|
||||
|
||||
enum vop2_dly_module {
|
||||
VOP2_DLY_WIN, /** Win delay cycle for this VP */
|
||||
VOP2_DLY_LAYER_MIX, /** Layer Mix delay cycle for this VP */
|
||||
VOP2_DLY_HDR_MIX, /** HDR delay cycle for this VP */
|
||||
VOP2_DLY_MAX,
|
||||
};
|
||||
|
||||
enum vop2_scale_up_mode {
|
||||
VOP2_SCALE_UP_NRST_NBOR,
|
||||
VOP2_SCALE_UP_BIL,
|
||||
@@ -58,6 +75,23 @@ enum vop2_scale_down_mode {
|
||||
#define VOP2_PD_DSC_4K BIT(6)
|
||||
#define VOP2_PD_ESMART BIT(7)
|
||||
|
||||
#define vop2_output_if_is_hdmi(x) ((x) == ROCKCHIP_VOP2_EP_HDMI0 || \
|
||||
(x) == ROCKCHIP_VOP2_EP_HDMI1)
|
||||
|
||||
#define vop2_output_if_is_dp(x) ((x) == ROCKCHIP_VOP2_EP_DP0 || \
|
||||
(x) == ROCKCHIP_VOP2_EP_DP1)
|
||||
|
||||
#define vop2_output_if_is_edp(x) ((x) == ROCKCHIP_VOP2_EP_EDP0 || \
|
||||
(x) == ROCKCHIP_VOP2_EP_EDP1)
|
||||
|
||||
#define vop2_output_if_is_mipi(x) ((x) == ROCKCHIP_VOP2_EP_MIPI0 || \
|
||||
(x) == ROCKCHIP_VOP2_EP_MIPI1)
|
||||
|
||||
#define vop2_output_if_is_lvds(x) ((x) == ROCKCHIP_VOP2_EP_LVDS0 || \
|
||||
(x) == ROCKCHIP_VOP2_EP_LVDS1)
|
||||
|
||||
#define vop2_output_if_is_dpi(x) ((x) == ROCKCHIP_VOP2_EP_RGB0)
|
||||
|
||||
enum vop2_win_regs {
|
||||
VOP2_WIN_ENABLE,
|
||||
VOP2_WIN_FORMAT,
|
||||
@@ -113,16 +147,22 @@ enum vop2_win_regs {
|
||||
VOP2_WIN_AFBC_UV_SWAP,
|
||||
VOP2_WIN_AFBC_AUTO_GATING_EN,
|
||||
VOP2_WIN_AFBC_BLOCK_SPLIT_EN,
|
||||
VOP2_WIN_AFBC_PLD_OFFSET_EN,
|
||||
VOP2_WIN_AFBC_PIC_VIR_WIDTH,
|
||||
VOP2_WIN_AFBC_TILE_NUM,
|
||||
VOP2_WIN_AFBC_PIC_OFFSET,
|
||||
VOP2_WIN_AFBC_PIC_SIZE,
|
||||
VOP2_WIN_AFBC_DSP_OFFSET,
|
||||
VOP2_WIN_AFBC_TRANSFORM_OFFSET,
|
||||
VOP2_WIN_AFBC_PLD_OFFSET,
|
||||
VOP2_WIN_TRANSFORM_OFFSET,
|
||||
VOP2_WIN_AFBC_HDR_PTR,
|
||||
VOP2_WIN_AFBC_HALF_BLOCK_EN,
|
||||
VOP2_WIN_AFBC_ROTATE_270,
|
||||
VOP2_WIN_AFBC_ROTATE_90,
|
||||
|
||||
VOP2_WIN_VP_SEL,
|
||||
VOP2_WIN_DLY_NUM,
|
||||
|
||||
VOP2_WIN_MAX_REG,
|
||||
};
|
||||
|
||||
@@ -140,6 +180,7 @@ struct vop2_win_data {
|
||||
unsigned int phys_id;
|
||||
|
||||
u32 base;
|
||||
u32 possible_vp_mask;
|
||||
enum drm_plane_type type;
|
||||
|
||||
u32 nformats;
|
||||
@@ -148,9 +189,9 @@ struct vop2_win_data {
|
||||
const unsigned int supported_rotations;
|
||||
|
||||
/**
|
||||
* @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
|
||||
* @layer_sel_id: defined by register OVERLAY_LAYER_SEL or PORTn_LAYER_SEL
|
||||
*/
|
||||
unsigned int layer_sel_id;
|
||||
unsigned int layer_sel_id[ROCKCHIP_MAX_CRTC];
|
||||
uint64_t feature;
|
||||
|
||||
uint8_t axi_bus_id;
|
||||
@@ -162,6 +203,23 @@ struct vop2_win_data {
|
||||
const u8 dly[VOP2_DLY_MODE_MAX];
|
||||
};
|
||||
|
||||
struct vop2_win {
|
||||
struct vop2 *vop2;
|
||||
struct drm_plane base;
|
||||
const struct vop2_win_data *data;
|
||||
struct regmap_field *reg[VOP2_WIN_MAX_REG];
|
||||
|
||||
/**
|
||||
* @win_id: graphic window id, a cluster may be split into two
|
||||
* graphics windows.
|
||||
*/
|
||||
u8 win_id;
|
||||
u8 delay;
|
||||
u32 offset;
|
||||
|
||||
enum drm_plane_type type;
|
||||
};
|
||||
|
||||
struct vop2_video_port_data {
|
||||
unsigned int id;
|
||||
u32 feature;
|
||||
@@ -170,22 +228,116 @@ struct vop2_video_port_data {
|
||||
struct vop_rect max_output;
|
||||
const u8 pre_scan_max_dly[4];
|
||||
unsigned int offset;
|
||||
/**
|
||||
* @pixel_rate: pixel per cycle
|
||||
*/
|
||||
u8 pixel_rate;
|
||||
};
|
||||
|
||||
struct vop2_video_port {
|
||||
struct drm_crtc crtc;
|
||||
struct vop2 *vop2;
|
||||
struct clk *dclk;
|
||||
struct clk *dclk_src;
|
||||
unsigned int id;
|
||||
const struct vop2_video_port_data *data;
|
||||
|
||||
struct completion dsp_hold_completion;
|
||||
|
||||
/**
|
||||
* @win_mask: Bitmask of windows attached to the video port;
|
||||
*/
|
||||
u32 win_mask;
|
||||
|
||||
struct vop2_win *primary_plane;
|
||||
struct drm_pending_vblank_event *event;
|
||||
|
||||
unsigned int nlayers;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vop2_ops - helper operations for vop2 hardware
|
||||
*
|
||||
* These hooks are used by the common part of the vop2 driver to
|
||||
* implement the proper behaviour of different variants.
|
||||
*/
|
||||
struct vop2_ops {
|
||||
unsigned long (*setup_intf_mux)(struct vop2_video_port *vp, int ep_id, u32 polflags);
|
||||
void (*setup_bg_dly)(struct vop2_video_port *vp);
|
||||
void (*setup_overlay)(struct vop2_video_port *vp);
|
||||
};
|
||||
|
||||
struct vop2_data {
|
||||
u8 nr_vps;
|
||||
u64 feature;
|
||||
u32 version;
|
||||
const struct vop2_ops *ops;
|
||||
const struct vop2_win_data *win;
|
||||
const struct vop2_video_port_data *vp;
|
||||
const struct reg_field *cluster_reg;
|
||||
const struct reg_field *smart_reg;
|
||||
const struct vop2_regs_dump *regs_dump;
|
||||
struct vop_rect max_input;
|
||||
struct vop_rect max_output;
|
||||
|
||||
unsigned int nr_cluster_regs;
|
||||
unsigned int nr_smart_regs;
|
||||
unsigned int win_size;
|
||||
unsigned int regs_dump_size;
|
||||
unsigned int soc_id;
|
||||
};
|
||||
|
||||
struct vop2 {
|
||||
u32 version;
|
||||
struct device *dev;
|
||||
struct drm_device *drm;
|
||||
struct vop2_video_port vps[ROCKCHIP_MAX_CRTC];
|
||||
|
||||
const struct vop2_data *data;
|
||||
const struct vop2_ops *ops;
|
||||
/*
|
||||
* Number of windows that are registered as plane, may be less than the
|
||||
* total number of hardware windows.
|
||||
*/
|
||||
u32 registered_num_wins;
|
||||
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
struct regmap *map;
|
||||
|
||||
struct regmap *sys_grf;
|
||||
struct regmap *vop_grf;
|
||||
struct regmap *vo1_grf;
|
||||
struct regmap *sys_pmu;
|
||||
|
||||
/* physical map length of vop2 register */
|
||||
u32 len;
|
||||
|
||||
void __iomem *lut_regs;
|
||||
|
||||
/* protects crtc enable/disable */
|
||||
struct mutex vop2_lock;
|
||||
|
||||
int irq;
|
||||
|
||||
/*
|
||||
* Some global resources are shared between all video ports(crtcs), so
|
||||
* we need a ref counter here.
|
||||
*/
|
||||
unsigned int enable_count;
|
||||
struct clk *hclk;
|
||||
struct clk *aclk;
|
||||
struct clk *pclk;
|
||||
struct clk *pll_hdmiphy0;
|
||||
struct clk *pll_hdmiphy1;
|
||||
|
||||
/* optional internal rgb encoder */
|
||||
struct rockchip_rgb *rgb;
|
||||
|
||||
/* must be put at the end of the struct */
|
||||
struct vop2_win win[];
|
||||
};
|
||||
|
||||
/* interrupt define */
|
||||
#define FS_NEW_INTR BIT(4)
|
||||
#define ADDR_SAME_INTR BIT(5)
|
||||
@@ -240,10 +392,13 @@ enum dst_factor_mode {
|
||||
#define RK3568_REG_CFG_DONE 0x000
|
||||
#define RK3568_VERSION_INFO 0x004
|
||||
#define RK3568_SYS_AUTO_GATING_CTRL 0x008
|
||||
#define RK3576_SYS_MMU_CTRL_IMD 0x020
|
||||
#define RK3568_SYS_AXI_LUT_CTRL 0x024
|
||||
#define RK3568_DSP_IF_EN 0x028
|
||||
#define RK3576_SYS_PORT_CTRL_IMD 0x028
|
||||
#define RK3568_DSP_IF_CTRL 0x02c
|
||||
#define RK3568_DSP_IF_POL 0x030
|
||||
#define RK3576_SYS_CLUSTER_PD_CTRL_IMD 0x030
|
||||
#define RK3588_SYS_PD_CTRL 0x034
|
||||
#define RK3568_WB_CTRL 0x40
|
||||
#define RK3568_WB_XSCAL_FACTOR 0x44
|
||||
@@ -263,6 +418,55 @@ enum dst_factor_mode {
|
||||
#define RK3568_VP_INT_CLR(vp) (0xA4 + (vp) * 0x10)
|
||||
#define RK3568_VP_INT_STATUS(vp) (0xA8 + (vp) * 0x10)
|
||||
#define RK3568_VP_INT_RAW_STATUS(vp) (0xAC + (vp) * 0x10)
|
||||
#define RK3576_WB_CTRL 0x100
|
||||
#define RK3576_WB_XSCAL_FACTOR 0x104
|
||||
#define RK3576_WB_YRGB_MST 0x108
|
||||
#define RK3576_WB_CBR_MST 0x10C
|
||||
#define RK3576_WB_VIR_STRIDE 0x110
|
||||
#define RK3576_WB_TIMEOUT_CTRL 0x114
|
||||
#define RK3576_MIPI0_IF_CTRL 0x180
|
||||
#define RK3576_HDMI0_IF_CTRL 0x184
|
||||
#define RK3576_EDP0_IF_CTRL 0x188
|
||||
#define RK3576_DP0_IF_CTRL 0x18C
|
||||
#define RK3576_RGB_IF_CTRL 0x194
|
||||
#define RK3576_DP1_IF_CTRL 0x1A4
|
||||
#define RK3576_DP2_IF_CTRL 0x1B0
|
||||
|
||||
/* Extra OVL register definition */
|
||||
#define RK3576_SYS_EXTRA_ALPHA_CTRL 0x500
|
||||
#define RK3576_CLUSTER0_MIX_SRC_COLOR_CTRL 0x530
|
||||
#define RK3576_CLUSTER0_MIX_DST_COLOR_CTRL 0x534
|
||||
#define RK3576_CLUSTER0_MIX_SRC_ALPHA_CTRL 0x538
|
||||
#define RK3576_CLUSTER0_MIX_DST_ALPHA_CTRL 0x53c
|
||||
#define RK3576_CLUSTER1_MIX_SRC_COLOR_CTRL 0x540
|
||||
#define RK3576_CLUSTER1_MIX_DST_COLOR_CTRL 0x544
|
||||
#define RK3576_CLUSTER1_MIX_SRC_ALPHA_CTRL 0x548
|
||||
#define RK3576_CLUSTER1_MIX_DST_ALPHA_CTRL 0x54c
|
||||
|
||||
/* OVL registers for Video Port definition */
|
||||
#define RK3576_OVL_CTRL(vp) (0x600 + (vp) * 0x100)
|
||||
#define RK3576_OVL_LAYER_SEL(vp) (0x604 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX0_SRC_COLOR_CTRL(vp) (0x620 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX0_DST_COLOR_CTRL(vp) (0x624 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX0_SRC_ALPHA_CTRL(vp) (0x628 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX0_DST_ALPHA_CTRL(vp) (0x62C + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX1_SRC_COLOR_CTRL(vp) (0x630 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX1_DST_COLOR_CTRL(vp) (0x634 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX1_SRC_ALPHA_CTRL(vp) (0x638 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX1_DST_ALPHA_CTRL(vp) (0x63C + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX2_SRC_COLOR_CTRL(vp) (0x640 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX2_DST_COLOR_CTRL(vp) (0x644 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX2_SRC_ALPHA_CTRL(vp) (0x648 + (vp) * 0x100)
|
||||
#define RK3576_OVL_MIX2_DST_ALPHA_CTRL(vp) (0x64C + (vp) * 0x100)
|
||||
#define RK3576_EXTRA_OVL_SRC_COLOR_CTRL(vp) (0x650 + (vp) * 0x100)
|
||||
#define RK3576_EXTRA_OVL_DST_COLOR_CTRL(vp) (0x654 + (vp) * 0x100)
|
||||
#define RK3576_EXTRA_OVL_SRC_ALPHA_CTRL(vp) (0x658 + (vp) * 0x100)
|
||||
#define RK3576_EXTRA_OVL_DST_ALPHA_CTRL(vp) (0x65C + (vp) * 0x100)
|
||||
#define RK3576_OVL_HDR_SRC_COLOR_CTRL(vp) (0x660 + (vp) * 0x100)
|
||||
#define RK3576_OVL_HDR_DST_COLOR_CTRL(vp) (0x664 + (vp) * 0x100)
|
||||
#define RK3576_OVL_HDR_SRC_ALPHA_CTRL(vp) (0x668 + (vp) * 0x100)
|
||||
#define RK3576_OVL_HDR_DST_ALPHA_CTRL(vp) (0x66C + (vp) * 0x100)
|
||||
#define RK3576_OVL_BG_MIX_CTRL(vp) (0x670 + (vp) * 0x100)
|
||||
|
||||
/* Video Port registers definition */
|
||||
#define RK3568_VP0_CTRL_BASE 0x0C00
|
||||
@@ -335,7 +539,7 @@ enum dst_factor_mode {
|
||||
#define RK3568_CLUSTER_WIN_DSP_INFO 0x24
|
||||
#define RK3568_CLUSTER_WIN_DSP_ST 0x28
|
||||
#define RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB 0x30
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_TRANSFORM_OFFSET 0x3C
|
||||
#define RK3568_CLUSTER_WIN_TRANSFORM_OFFSET 0x3C
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_OUTPUT_CTRL 0x50
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE 0x54
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR 0x58
|
||||
@@ -345,7 +549,11 @@ enum dst_factor_mode {
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET 0x68
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_CTRL 0x6C
|
||||
|
||||
#define RK3576_CLUSTER_WIN_AFBCD_PLD_PTR_OFFSET 0x78
|
||||
|
||||
#define RK3568_CLUSTER_CTRL 0x100
|
||||
#define RK3576_CLUSTER_PORT_SEL_IMD 0x1F4
|
||||
#define RK3576_CLUSTER_DLY_NUM 0x1F8
|
||||
|
||||
/* (E)smart register definition, offset relative to window base */
|
||||
#define RK3568_SMART_CTRL0 0x00
|
||||
@@ -396,6 +604,9 @@ enum dst_factor_mode {
|
||||
#define RK3568_SMART_REGION3_SCL_FACTOR_CBR 0xC8
|
||||
#define RK3568_SMART_REGION3_SCL_OFFSET 0xCC
|
||||
#define RK3568_SMART_COLOR_KEY_CTRL 0xD0
|
||||
#define RK3576_SMART_ALPHA_MAP 0xD8
|
||||
#define RK3576_SMART_PORT_SEL_IMD 0xF4
|
||||
#define RK3576_SMART_DLY_NUM 0xF8
|
||||
|
||||
/* HDR register definition */
|
||||
#define RK3568_HDR_LUT_CTRL 0x2000
|
||||
@@ -544,6 +755,17 @@ enum dst_factor_mode {
|
||||
|
||||
#define POLFLAG_DCLK_INV BIT(3)
|
||||
|
||||
#define RK3576_OVL_CTRL__YUV_MODE BIT(0)
|
||||
#define RK3576_OVL_BG_MIX_CTRL__BG_DLY GENMASK(31, 24)
|
||||
|
||||
#define RK3576_DSP_IF_CFG_DONE_IMD BIT(31)
|
||||
#define RK3576_DSP_IF_DCLK_SEL_OUT BIT(21)
|
||||
#define RK3576_DSP_IF_PCLK_DIV BIT(20)
|
||||
#define RK3576_DSP_IF_PIN_POL GENMASK(5, 4)
|
||||
#define RK3576_DSP_IF_MUX GENMASK(3, 2)
|
||||
#define RK3576_DSP_IF_CLK_OUT_EN BIT(1)
|
||||
#define RK3576_DSP_IF_EN BIT(0)
|
||||
|
||||
enum vop2_layer_phy_id {
|
||||
ROCKCHIP_VOP2_CLUSTER0 = 0,
|
||||
ROCKCHIP_VOP2_CLUSTER1,
|
||||
@@ -560,4 +782,52 @@ enum vop2_layer_phy_id {
|
||||
|
||||
extern const struct component_ops vop2_component_ops;
|
||||
|
||||
static inline void vop2_writel(struct vop2 *vop2, u32 offset, u32 v)
|
||||
{
|
||||
regmap_write(vop2->map, offset, v);
|
||||
}
|
||||
|
||||
static inline void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
|
||||
{
|
||||
regmap_write(vp->vop2->map, vp->data->offset + offset, v);
|
||||
}
|
||||
|
||||
static inline u32 vop2_readl(struct vop2 *vop2, u32 offset)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
regmap_read(vop2->map, offset, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 vop2_vp_read(struct vop2_video_port *vp, u32 offset)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
regmap_read(vp->vop2->map, vp->data->offset + offset, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void vop2_win_write(const struct vop2_win *win, unsigned int reg, u32 v)
|
||||
{
|
||||
regmap_field_write(win->reg[reg], v);
|
||||
}
|
||||
|
||||
static inline bool vop2_cluster_window(const struct vop2_win *win)
|
||||
{
|
||||
return win->data->feature & WIN_FEATURE_CLUSTER;
|
||||
}
|
||||
|
||||
static inline struct vop2_video_port *to_vop2_video_port(struct drm_crtc *crtc)
|
||||
{
|
||||
return container_of(crtc, struct vop2_video_port, crtc);
|
||||
}
|
||||
|
||||
static inline struct vop2_win *to_vop2_win(struct drm_plane *p)
|
||||
{
|
||||
return container_of(p, struct vop2_win, base);
|
||||
}
|
||||
|
||||
#endif /* _ROCKCHIP_DRM_VOP2_H */
|
||||
|
||||
@@ -448,17 +448,14 @@ struct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = {
|
||||
static int rk3288_lvds_probe(struct platform_device *pdev,
|
||||
struct rockchip_lvds *lvds)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lvds->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(lvds->regs))
|
||||
return PTR_ERR(lvds->regs);
|
||||
|
||||
lvds->pclk = devm_clk_get(lvds->dev, "pclk_lvds");
|
||||
if (IS_ERR(lvds->pclk)) {
|
||||
DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n");
|
||||
return PTR_ERR(lvds->pclk);
|
||||
}
|
||||
lvds->pclk = devm_clk_get_prepared(lvds->dev, "pclk_lvds");
|
||||
if (IS_ERR(lvds->pclk))
|
||||
return dev_err_probe(lvds->dev, PTR_ERR(lvds->pclk),
|
||||
"could not get or prepare pclk_lvds\n");
|
||||
|
||||
lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins),
|
||||
GFP_KERNEL);
|
||||
@@ -467,25 +464,19 @@ static int rk3288_lvds_probe(struct platform_device *pdev,
|
||||
|
||||
lvds->pins->p = devm_pinctrl_get(lvds->dev);
|
||||
if (IS_ERR(lvds->pins->p)) {
|
||||
DRM_DEV_ERROR(lvds->dev, "no pinctrl handle\n");
|
||||
dev_warn(lvds->dev, "no pinctrl handle\n");
|
||||
devm_kfree(lvds->dev, lvds->pins);
|
||||
lvds->pins = NULL;
|
||||
} else {
|
||||
lvds->pins->default_state =
|
||||
pinctrl_lookup_state(lvds->pins->p, "lcdc");
|
||||
if (IS_ERR(lvds->pins->default_state)) {
|
||||
DRM_DEV_ERROR(lvds->dev, "no default pinctrl state\n");
|
||||
dev_warn(lvds->dev, "no default pinctrl state\n");
|
||||
devm_kfree(lvds->dev, lvds->pins);
|
||||
lvds->pins = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_prepare(lvds->pclk);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(lvds->dev, "failed to prepare pclk_lvds\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -555,11 +546,10 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
|
||||
lvds->drm_dev = drm_dev;
|
||||
port = of_graph_get_port_by_id(dev->of_node, 1);
|
||||
if (!port) {
|
||||
DRM_DEV_ERROR(dev,
|
||||
"can't found port point, please init lvds panel port!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!port)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"can't found port point, please init lvds panel port!\n");
|
||||
|
||||
for_each_child_of_node(port, endpoint) {
|
||||
child_count++;
|
||||
of_property_read_u32(endpoint, "reg", &endpoint_id);
|
||||
@@ -571,8 +561,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
}
|
||||
}
|
||||
if (!child_count) {
|
||||
DRM_DEV_ERROR(dev, "lvds port does not have any children\n");
|
||||
ret = -EINVAL;
|
||||
ret = dev_err_probe(dev, -EINVAL, "lvds port does not have any children\n");
|
||||
goto err_put_port;
|
||||
} else if (ret) {
|
||||
dev_err_probe(dev, ret, "failed to find panel and bridge node\n");
|
||||
@@ -589,8 +578,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
lvds->output = rockchip_lvds_name_to_output(name);
|
||||
|
||||
if (lvds->output < 0) {
|
||||
DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name);
|
||||
ret = lvds->output;
|
||||
ret = dev_err_probe(dev, lvds->output, "invalid output type [%s]\n", name);
|
||||
goto err_put_remote;
|
||||
}
|
||||
|
||||
@@ -601,8 +589,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
lvds->format = rockchip_lvds_name_to_format(name);
|
||||
|
||||
if (lvds->format < 0) {
|
||||
DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name);
|
||||
ret = lvds->format;
|
||||
ret = dev_err_probe(dev, lvds->format,
|
||||
"invalid data-mapping format [%s]\n", name);
|
||||
goto err_put_remote;
|
||||
}
|
||||
|
||||
@@ -612,8 +600,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
|
||||
ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to initialize encoder: %d\n", ret);
|
||||
drm_err(drm_dev,
|
||||
"failed to initialize encoder: %d\n", ret);
|
||||
goto err_put_remote;
|
||||
}
|
||||
|
||||
@@ -626,8 +614,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
&rockchip_lvds_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to initialize connector: %d\n", ret);
|
||||
drm_err(drm_dev,
|
||||
"failed to initialize connector: %d\n", ret);
|
||||
goto err_free_encoder;
|
||||
}
|
||||
|
||||
@@ -641,9 +629,9 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
|
||||
connector = drm_bridge_connector_init(lvds->drm_dev, encoder);
|
||||
if (IS_ERR(connector)) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to initialize bridge connector: %pe\n",
|
||||
connector);
|
||||
drm_err(drm_dev,
|
||||
"failed to initialize bridge connector: %pe\n",
|
||||
connector);
|
||||
ret = PTR_ERR(connector);
|
||||
goto err_free_encoder;
|
||||
}
|
||||
@@ -651,8 +639,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
|
||||
ret = drm_connector_attach_encoder(connector, encoder);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(drm_dev->dev,
|
||||
"failed to attach encoder: %d\n", ret);
|
||||
drm_err(drm_dev, "failed to attach encoder: %d\n", ret);
|
||||
goto err_free_connector;
|
||||
}
|
||||
|
||||
@@ -714,34 +701,25 @@ static int rockchip_lvds_probe(struct platform_device *pdev)
|
||||
|
||||
lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"rockchip,grf");
|
||||
if (IS_ERR(lvds->grf)) {
|
||||
DRM_DEV_ERROR(dev, "missing rockchip,grf property\n");
|
||||
return PTR_ERR(lvds->grf);
|
||||
}
|
||||
if (IS_ERR(lvds->grf))
|
||||
return dev_err_probe(dev, PTR_ERR(lvds->grf), "missing rockchip,grf property\n");
|
||||
|
||||
ret = lvds->soc_data->probe(pdev, lvds);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Platform initialization failed\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Platform initialization failed\n");
|
||||
|
||||
dev_set_drvdata(dev, lvds);
|
||||
|
||||
ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(dev, "failed to add component\n");
|
||||
clk_unprepare(lvds->pclk);
|
||||
}
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed to add component\n");
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rockchip_lvds_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rockchip_lvds *lvds = platform_get_drvdata(pdev);
|
||||
|
||||
component_del(&pdev->dev, &rockchip_lvds_component_ops);
|
||||
clk_unprepare(lvds->pclk);
|
||||
}
|
||||
|
||||
struct platform_driver rockchip_lvds_driver = {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -91,7 +91,7 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
|
||||
* the lowest priority available.
|
||||
*/
|
||||
if (entity->priority >= sched_list[0]->num_rqs) {
|
||||
drm_err(sched_list[0], "entity with out-of-bounds priority:%u num_rqs:%u\n",
|
||||
dev_err(sched_list[0]->dev, "entity has out-of-bounds priority: %u. num_rqs: %u\n",
|
||||
entity->priority, sched_list[0]->num_rqs);
|
||||
entity->priority = max_t(s32, (s32) sched_list[0]->num_rqs - 1,
|
||||
(s32) DRM_SCHED_PRIORITY_KERNEL);
|
||||
|
||||
@@ -102,9 +102,9 @@ static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched)
|
||||
{
|
||||
u32 credits;
|
||||
|
||||
drm_WARN_ON(sched, check_sub_overflow(sched->credit_limit,
|
||||
atomic_read(&sched->credit_count),
|
||||
&credits));
|
||||
WARN_ON(check_sub_overflow(sched->credit_limit,
|
||||
atomic_read(&sched->credit_count),
|
||||
&credits));
|
||||
|
||||
return credits;
|
||||
}
|
||||
@@ -129,9 +129,11 @@ static bool drm_sched_can_queue(struct drm_gpu_scheduler *sched,
|
||||
/* If a job exceeds the credit limit, truncate it to the credit limit
|
||||
* itself to guarantee forward progress.
|
||||
*/
|
||||
if (drm_WARN(sched, s_job->credits > sched->credit_limit,
|
||||
"Jobs may not exceed the credit limit, truncate.\n"))
|
||||
if (s_job->credits > sched->credit_limit) {
|
||||
dev_WARN(sched->dev,
|
||||
"Jobs may not exceed the credit limit, truncate.\n");
|
||||
s_job->credits = sched->credit_limit;
|
||||
}
|
||||
|
||||
return drm_sched_available_credits(sched) >= s_job->credits;
|
||||
}
|
||||
@@ -789,7 +791,7 @@ int drm_sched_job_init(struct drm_sched_job *job,
|
||||
* or worse--a blank screen--leave a trail in the
|
||||
* logs, so this can be debugged easier.
|
||||
*/
|
||||
drm_err(job->sched, "%s: entity has no rq!\n", __func__);
|
||||
dev_err(job->sched->dev, "%s: entity has no rq!\n", __func__);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
@@ -1013,11 +1015,13 @@ EXPORT_SYMBOL(drm_sched_job_has_dependency);
|
||||
* Cleans up the resources allocated with drm_sched_job_init().
|
||||
*
|
||||
* Drivers should call this from their error unwind code if @job is aborted
|
||||
* before drm_sched_job_arm() is called.
|
||||
* before it was submitted to an entity with drm_sched_entity_push_job().
|
||||
*
|
||||
* After that point of no return @job is committed to be executed by the
|
||||
* scheduler, and this function should be called from the
|
||||
* &drm_sched_backend_ops.free_job callback.
|
||||
* Since calling drm_sched_job_arm() causes the job's fences to be initialized,
|
||||
* it is up to the driver to ensure that fences that were exposed to external
|
||||
* parties get signaled. drm_sched_job_cleanup() does not ensure this.
|
||||
*
|
||||
* This function must also be called in &struct drm_sched_backend_ops.free_job
|
||||
*/
|
||||
void drm_sched_job_cleanup(struct drm_sched_job *job)
|
||||
{
|
||||
@@ -1028,7 +1032,7 @@ void drm_sched_job_cleanup(struct drm_sched_job *job)
|
||||
/* drm_sched_job_arm() has been called */
|
||||
dma_fence_put(&job->s_fence->finished);
|
||||
} else {
|
||||
/* aborted job before committing to run it */
|
||||
/* aborted job before arming */
|
||||
drm_sched_fence_free(job->s_fence);
|
||||
}
|
||||
|
||||
@@ -1263,7 +1267,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
|
||||
if (args->num_rqs > DRM_SCHED_PRIORITY_COUNT) {
|
||||
/* This is a gross violation--tell drivers what the problem is.
|
||||
*/
|
||||
drm_err(sched, "%s: num_rqs cannot be greater than DRM_SCHED_PRIORITY_COUNT\n",
|
||||
dev_err(sched->dev, "%s: num_rqs cannot be greater than DRM_SCHED_PRIORITY_COUNT\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
} else if (sched->sched_rq) {
|
||||
@@ -1271,7 +1275,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
|
||||
* fine-tune their DRM calling order, and return all
|
||||
* is good.
|
||||
*/
|
||||
drm_warn(sched, "%s: scheduler already initialized!\n", __func__);
|
||||
dev_warn(sched->dev, "%s: scheduler already initialized!\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1326,7 +1330,7 @@ Out_unroll:
|
||||
Out_check_own:
|
||||
if (sched->own_submit_wq)
|
||||
destroy_workqueue(sched->submit_wq);
|
||||
drm_err(sched, "%s: Failed to setup GPU scheduler--out of memory\n", __func__);
|
||||
dev_err(sched->dev, "%s: Failed to setup GPU scheduler--out of memory\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_sched_init);
|
||||
|
||||
@@ -189,7 +189,7 @@ static int set_up_atomic_state(struct kunit *test,
|
||||
static void drm_test_check_connector_changed_modeset(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_test_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector *old_conn, *new_conn;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_device *drm;
|
||||
@@ -203,14 +203,13 @@ static void drm_test_check_connector_changed_modeset(struct kunit *test)
|
||||
old_conn = &priv->connectors[0];
|
||||
new_conn = &priv->connectors[1];
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
// first modeset to enable
|
||||
ret = set_up_atomic_state(test, priv, old_conn, ctx);
|
||||
ret = set_up_atomic_state(test, priv, old_conn, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
new_conn_state = drm_atomic_get_connector_state(state, new_conn);
|
||||
@@ -231,6 +230,9 @@ static void drm_test_check_connector_changed_modeset(struct kunit *test)
|
||||
ret = drm_atomic_commit(state);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
KUNIT_ASSERT_EQ(test, modeset_counter, initial_modeset_count + 1);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -263,7 +265,7 @@ static void drm_test_check_valid_clones(struct kunit *test)
|
||||
int ret;
|
||||
const struct drm_clone_mode_test *param = test->param_value;
|
||||
struct drm_atomic_test_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_device *drm;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@@ -273,13 +275,12 @@ static void drm_test_check_valid_clones(struct kunit *test)
|
||||
|
||||
drm = &priv->drm;
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = set_up_atomic_state(test, priv, NULL, ctx);
|
||||
ret = set_up_atomic_state(test, priv, NULL, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
|
||||
@@ -292,6 +293,9 @@ static void drm_test_check_valid_clones(struct kunit *test)
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(drm, state);
|
||||
KUNIT_ASSERT_EQ(test, ret, param->expected_result);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
static void drm_check_in_clone_mode_desc(const struct drm_clone_mode_test *t,
|
||||
|
||||
@@ -60,6 +60,11 @@ struct convert_to_rgb888_result {
|
||||
const u8 expected[TEST_BUF_SIZE];
|
||||
};
|
||||
|
||||
struct convert_to_bgr888_result {
|
||||
unsigned int dst_pitch;
|
||||
const u8 expected[TEST_BUF_SIZE];
|
||||
};
|
||||
|
||||
struct convert_to_argb8888_result {
|
||||
unsigned int dst_pitch;
|
||||
const u32 expected[TEST_BUF_SIZE];
|
||||
@@ -107,6 +112,7 @@ struct convert_xrgb8888_case {
|
||||
struct convert_to_argb1555_result argb1555_result;
|
||||
struct convert_to_rgba5551_result rgba5551_result;
|
||||
struct convert_to_rgb888_result rgb888_result;
|
||||
struct convert_to_bgr888_result bgr888_result;
|
||||
struct convert_to_argb8888_result argb8888_result;
|
||||
struct convert_to_xrgb2101010_result xrgb2101010_result;
|
||||
struct convert_to_argb2101010_result argb2101010_result;
|
||||
@@ -151,6 +157,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = { 0x00, 0x00, 0xFF },
|
||||
},
|
||||
.bgr888_result = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = { 0xFF, 0x00, 0x00 },
|
||||
},
|
||||
.argb8888_result = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = { 0xFFFF0000 },
|
||||
@@ -217,6 +227,10 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = { 0x00, 0x00, 0xFF },
|
||||
},
|
||||
.bgr888_result = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = { 0xFF, 0x00, 0x00 },
|
||||
},
|
||||
.argb8888_result = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = { 0xFFFF0000 },
|
||||
@@ -330,6 +344,15 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
|
||||
},
|
||||
},
|
||||
.bgr888_result = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = {
|
||||
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||
0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF,
|
||||
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
|
||||
},
|
||||
},
|
||||
.argb8888_result = {
|
||||
.dst_pitch = TEST_USE_DEFAULT_PITCH,
|
||||
.expected = {
|
||||
@@ -468,6 +491,17 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
},
|
||||
.bgr888_result = {
|
||||
.dst_pitch = 15,
|
||||
.expected = {
|
||||
0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05, 0xA8, 0xF3, 0x03,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C, 0x11, 0x4D, 0x05,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xA8, 0x03, 0x03, 0x6C, 0xF0, 0x73, 0x0E, 0x44, 0x9C,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
},
|
||||
.argb8888_result = {
|
||||
.dst_pitch = 20,
|
||||
.expected = {
|
||||
@@ -914,6 +948,52 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test)
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
|
||||
}
|
||||
|
||||
static void drm_test_fb_xrgb8888_to_bgr888(struct kunit *test)
|
||||
{
|
||||
const struct convert_xrgb8888_case *params = test->param_value;
|
||||
const struct convert_to_bgr888_result *result = ¶ms->bgr888_result;
|
||||
size_t dst_size;
|
||||
u8 *buf = NULL;
|
||||
__le32 *xrgb8888 = NULL;
|
||||
struct iosys_map dst, src;
|
||||
|
||||
struct drm_framebuffer fb = {
|
||||
.format = drm_format_info(DRM_FORMAT_XRGB8888),
|
||||
.pitches = { params->pitch, 0, 0 },
|
||||
};
|
||||
|
||||
dst_size = conversion_buf_size(DRM_FORMAT_BGR888, result->dst_pitch,
|
||||
¶ms->clip, 0);
|
||||
KUNIT_ASSERT_GT(test, dst_size, 0);
|
||||
|
||||
buf = kunit_kzalloc(test, dst_size, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
|
||||
iosys_map_set_vaddr(&dst, buf);
|
||||
|
||||
xrgb8888 = cpubuf_to_le32(test, params->xrgb8888, TEST_BUF_SIZE);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888);
|
||||
iosys_map_set_vaddr(&src, xrgb8888);
|
||||
|
||||
/*
|
||||
* BGR888 expected results are already in little-endian
|
||||
* order, so there's no need to convert the test output.
|
||||
*/
|
||||
drm_fb_xrgb8888_to_bgr888(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip,
|
||||
&fmtcnv_state);
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
|
||||
|
||||
buf = dst.vaddr; /* restore original value of buf */
|
||||
memset(buf, 0, dst_size);
|
||||
|
||||
int blit_result = 0;
|
||||
|
||||
blit_result = drm_fb_blit(&dst, &result->dst_pitch, DRM_FORMAT_BGR888, &src, &fb, ¶ms->clip,
|
||||
&fmtcnv_state);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, blit_result);
|
||||
KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size);
|
||||
}
|
||||
|
||||
static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test)
|
||||
{
|
||||
const struct convert_xrgb8888_case *params = test->param_value;
|
||||
@@ -1851,6 +1931,7 @@ static struct kunit_case drm_format_helper_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb1555, convert_xrgb8888_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgba5551, convert_xrgb8888_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb888, convert_xrgb8888_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_bgr888, convert_xrgb8888_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb8888, convert_xrgb8888_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_xrgb2101010, convert_xrgb8888_gen_params),
|
||||
KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb2101010, convert_xrgb8888_gen_params),
|
||||
|
||||
@@ -273,7 +273,7 @@ drm_kunit_helper_connector_hdmi_init(struct kunit *test,
|
||||
static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *old_conn_state;
|
||||
struct drm_connector_state *new_conn_state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@@ -296,13 +296,12 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
|
||||
preferred = find_preferred_mode(conn);
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
new_conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -327,6 +326,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
|
||||
KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -337,7 +339,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_changed(struct kunit *test)
|
||||
static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *old_conn_state;
|
||||
struct drm_connector_state *new_conn_state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@@ -360,13 +362,12 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
|
||||
preferred = find_preferred_mode(conn);
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
new_conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -393,6 +394,9 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
|
||||
KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -403,7 +407,7 @@ static void drm_test_check_broadcast_rgb_crtc_mode_not_changed(struct kunit *tes
|
||||
static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -426,13 +430,12 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -449,6 +452,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -459,7 +465,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode(struct kunit *test)
|
||||
static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_display_mode *mode;
|
||||
@@ -477,17 +483,16 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
|
||||
conn = &priv->connector;
|
||||
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1);
|
||||
KUNIT_ASSERT_NOT_NULL(test, mode);
|
||||
|
||||
crtc = priv->crtc;
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -504,6 +509,9 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -514,7 +522,7 @@ static void drm_test_check_broadcast_rgb_auto_cea_mode_vic_1(struct kunit *test)
|
||||
static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -537,13 +545,12 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -562,6 +569,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
|
||||
DRM_HDMI_BROADCAST_RGB_FULL);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -572,7 +582,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode(struct kunit *test)
|
||||
static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_display_mode *mode;
|
||||
@@ -590,17 +600,16 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
|
||||
conn = &priv->connector;
|
||||
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1);
|
||||
KUNIT_ASSERT_NOT_NULL(test, mode);
|
||||
|
||||
crtc = priv->crtc;
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -619,6 +628,9 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
|
||||
DRM_HDMI_BROADCAST_RGB_FULL);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, conn_state->hdmi.is_limited_range);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -629,7 +641,7 @@ static void drm_test_check_broadcast_rgb_full_cea_mode_vic_1(struct kunit *test)
|
||||
static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -652,13 +664,12 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
KUNIT_ASSERT_NE(test, drm_match_cea_mode(preferred), 1);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -677,6 +688,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
|
||||
DRM_HDMI_BROADCAST_RGB_LIMITED);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -687,7 +701,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode(struct kunit *test)
|
||||
static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_display_mode *mode;
|
||||
@@ -705,17 +719,16 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
|
||||
conn = &priv->connector;
|
||||
KUNIT_ASSERT_TRUE(test, conn->display_info.is_hdmi);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
mode = drm_kunit_display_mode_from_cea_vic(test, drm, 1);
|
||||
KUNIT_ASSERT_NOT_NULL(test, mode);
|
||||
|
||||
crtc = priv->crtc;
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -734,6 +747,9 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
|
||||
DRM_HDMI_BROADCAST_RGB_LIMITED);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, conn_state->hdmi.is_limited_range);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -744,7 +760,7 @@ static void drm_test_check_broadcast_rgb_limited_cea_mode_vic_1(struct kunit *te
|
||||
static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *old_conn_state;
|
||||
struct drm_connector_state *new_conn_state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@@ -771,13 +787,12 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
|
||||
preferred = find_preferred_mode(conn);
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
new_conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -808,6 +823,9 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
|
||||
KUNIT_EXPECT_TRUE(test, crtc_state->mode_changed);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -818,7 +836,7 @@ static void drm_test_check_output_bpc_crtc_mode_changed(struct kunit *test)
|
||||
static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *old_conn_state;
|
||||
struct drm_connector_state *new_conn_state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@@ -845,13 +863,12 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
|
||||
preferred = find_preferred_mode(conn);
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
new_conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
@@ -880,6 +897,9 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
|
||||
KUNIT_EXPECT_FALSE(test, crtc_state->mode_changed);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -889,7 +909,7 @@ static void drm_test_check_output_bpc_crtc_mode_not_changed(struct kunit *test)
|
||||
static void drm_test_check_output_bpc_dvi(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -919,10 +939,9 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test)
|
||||
preferred = find_preferred_mode(conn);
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -930,6 +949,9 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -939,7 +961,7 @@ static void drm_test_check_output_bpc_dvi(struct kunit *test)
|
||||
static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_mode *preferred;
|
||||
struct drm_connector *conn;
|
||||
@@ -964,10 +986,9 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -976,6 +997,9 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test)
|
||||
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 8);
|
||||
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1000);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -986,7 +1010,7 @@ static void drm_test_check_tmds_char_rate_rgb_8bpc(struct kunit *test)
|
||||
static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_mode *preferred;
|
||||
struct drm_connector *conn;
|
||||
@@ -1011,10 +1035,9 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1023,6 +1046,9 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
|
||||
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 10);
|
||||
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1033,7 +1059,7 @@ static void drm_test_check_tmds_char_rate_rgb_10bpc(struct kunit *test)
|
||||
static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_mode *preferred;
|
||||
struct drm_connector *conn;
|
||||
@@ -1058,10 +1084,9 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
KUNIT_ASSERT_FALSE(test, preferred->flags & DRM_MODE_FLAG_DBLCLK);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1070,6 +1095,9 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
|
||||
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_bpc, 12);
|
||||
KUNIT_ASSERT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1500);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1083,7 +1111,7 @@ static void drm_test_check_tmds_char_rate_rgb_12bpc(struct kunit *test)
|
||||
static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_display_mode *preferred;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
@@ -1104,16 +1132,15 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
|
||||
preferred = find_preferred_mode(conn);
|
||||
KUNIT_ASSERT_NOT_NULL(test, preferred);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
/* You shouldn't be doing that at home. */
|
||||
conn->hdmi.funcs = &reject_connector_hdmi_funcs;
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
||||
@@ -1123,6 +1150,9 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
|
||||
|
||||
ret = drm_atomic_check_only(state);
|
||||
KUNIT_EXPECT_LT(test, ret, 0);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1139,7 +1169,7 @@ static void drm_test_check_hdmi_funcs_reject_rate(struct kunit *test)
|
||||
static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -1176,10 +1206,9 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test)
|
||||
rate = drm_hdmi_compute_mode_clock(preferred, 10, HDMI_COLORSPACE_RGB);
|
||||
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1188,6 +1217,9 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.tmds_char_rate, preferred->clock * 1250);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1206,7 +1238,7 @@ static void drm_test_check_max_tmds_rate_bpc_fallback(struct kunit *test)
|
||||
static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -1248,10 +1280,9 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test)
|
||||
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
|
||||
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1259,6 +1290,9 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1269,7 +1303,7 @@ static void drm_test_check_max_tmds_rate_format_fallback(struct kunit *test)
|
||||
static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *mode;
|
||||
@@ -1310,11 +1344,10 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
|
||||
rate = mode->clock * 1500;
|
||||
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
crtc = priv->crtc;
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, mode, &ctx);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1322,6 +1355,9 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1331,7 +1367,7 @@ static void drm_test_check_output_bpc_format_vic_1(struct kunit *test)
|
||||
static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -1376,10 +1412,9 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
|
||||
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
|
||||
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1387,6 +1422,9 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1396,7 +1434,7 @@ static void drm_test_check_output_bpc_format_driver_rgb_only(struct kunit *test)
|
||||
static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -1443,10 +1481,9 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test
|
||||
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_YUV422);
|
||||
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1454,6 +1491,9 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test
|
||||
|
||||
KUNIT_EXPECT_LT(test, conn_state->hdmi.output_bpc, 12);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1464,7 +1504,7 @@ static void drm_test_check_output_bpc_format_display_rgb_only(struct kunit *test
|
||||
static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -1501,10 +1541,9 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test
|
||||
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
|
||||
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1512,6 +1551,9 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test
|
||||
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1522,7 +1564,7 @@ static void drm_test_check_output_bpc_format_driver_8bpc_only(struct kunit *test
|
||||
static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_display_info *info;
|
||||
struct drm_display_mode *preferred;
|
||||
@@ -1561,10 +1603,9 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes
|
||||
rate = drm_hdmi_compute_mode_clock(preferred, 12, HDMI_COLORSPACE_RGB);
|
||||
KUNIT_ASSERT_LT(test, rate, info->max_tmds_clock * 1000);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
conn_state = conn->state;
|
||||
@@ -1572,13 +1613,16 @@ static void drm_test_check_output_bpc_format_display_8bpc_only(struct kunit *tes
|
||||
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8);
|
||||
KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/* Test that atomic check succeeds when disabling a connector. */
|
||||
static void drm_test_check_disable_connector(struct kunit *test)
|
||||
{
|
||||
struct drm_atomic_helper_connector_hdmi_priv *priv;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_atomic_state *state;
|
||||
@@ -1593,8 +1637,7 @@ static void drm_test_check_disable_connector(struct kunit *test)
|
||||
8);
|
||||
KUNIT_ASSERT_NOT_NULL(test, priv);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
conn = &priv->connector;
|
||||
preferred = find_preferred_mode(conn);
|
||||
@@ -1602,10 +1645,10 @@ static void drm_test_check_disable_connector(struct kunit *test)
|
||||
|
||||
drm = &priv->drm;
|
||||
crtc = priv->crtc;
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, ctx);
|
||||
ret = light_up_connector(test, drm, crtc, conn, preferred, &ctx);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
||||
@@ -1623,6 +1666,9 @@ static void drm_test_check_disable_connector(struct kunit *test)
|
||||
|
||||
ret = drm_atomic_check_only(state);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = {
|
||||
|
||||
@@ -80,47 +80,6 @@ __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver);
|
||||
|
||||
static void action_drm_release_context(void *ptr)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *ctx = ptr;
|
||||
|
||||
drm_modeset_drop_locks(ctx);
|
||||
drm_modeset_acquire_fini(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_kunit_helper_acquire_ctx_alloc - Allocates an acquire context
|
||||
* @test: The test context object
|
||||
*
|
||||
* Allocates and initializes a modeset acquire context.
|
||||
*
|
||||
* The context is tied to the kunit test context, so we must not call
|
||||
* drm_modeset_acquire_fini() on it, it will be done so automatically.
|
||||
*
|
||||
* Returns:
|
||||
* An ERR_PTR on error, a pointer to the newly allocated context otherwise
|
||||
*/
|
||||
struct drm_modeset_acquire_ctx *
|
||||
drm_kunit_helper_acquire_ctx_alloc(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx);
|
||||
|
||||
drm_modeset_acquire_init(ctx, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
action_drm_release_context,
|
||||
ctx);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc);
|
||||
|
||||
static void kunit_action_drm_atomic_state_put(void *ptr)
|
||||
{
|
||||
struct drm_atomic_state *state = ptr;
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config DRM_APPLETBDRM
|
||||
tristate "DRM support for Apple Touch Bars"
|
||||
depends on DRM && USB && MMU
|
||||
select DRM_GEM_SHMEM_HELPER
|
||||
select DRM_KMS_HELPER
|
||||
help
|
||||
Say Y here if you want support for the display of Touch Bars on x86
|
||||
MacBook Pros.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called appletbdrm.
|
||||
|
||||
config DRM_ARCPGU
|
||||
tristate "ARC PGU"
|
||||
depends on DRM && OF
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_DRM_APPLETBDRM) += appletbdrm.o
|
||||
obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o
|
||||
obj-$(CONFIG_DRM_BOCHS) += bochs.o
|
||||
obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus-qemu.o
|
||||
|
||||
@@ -0,0 +1,841 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Apple Touch Bar DRM Driver
|
||||
*
|
||||
* Copyright (c) 2023 Kerem Karabay <kekrby@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/align.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_format_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#define APPLETBDRM_PIXEL_FORMAT cpu_to_le32(0x52474241) /* RGBA, the actual format is BGR888 */
|
||||
#define APPLETBDRM_BITS_PER_PIXEL 24
|
||||
|
||||
#define APPLETBDRM_MSG_CLEAR_DISPLAY cpu_to_le32(0x434c5244) /* CLRD */
|
||||
#define APPLETBDRM_MSG_GET_INFORMATION cpu_to_le32(0x47494e46) /* GINF */
|
||||
#define APPLETBDRM_MSG_UPDATE_COMPLETE cpu_to_le32(0x5544434c) /* UDCL */
|
||||
#define APPLETBDRM_MSG_SIGNAL_READINESS cpu_to_le32(0x52454459) /* REDY */
|
||||
|
||||
#define APPLETBDRM_BULK_MSG_TIMEOUT 1000
|
||||
|
||||
#define drm_to_adev(_drm) container_of(_drm, struct appletbdrm_device, drm)
|
||||
#define adev_to_udev(adev) interface_to_usbdev(to_usb_interface(adev->dmadev))
|
||||
|
||||
struct appletbdrm_msg_request_header {
|
||||
__le16 unk_00;
|
||||
__le16 unk_02;
|
||||
__le32 unk_04;
|
||||
__le32 unk_08;
|
||||
__le32 size;
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_msg_response_header {
|
||||
u8 unk_00[16];
|
||||
__le32 msg;
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_msg_simple_request {
|
||||
struct appletbdrm_msg_request_header header;
|
||||
__le32 msg;
|
||||
u8 unk_14[8];
|
||||
__le32 size;
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_msg_information {
|
||||
struct appletbdrm_msg_response_header header;
|
||||
u8 unk_14[12];
|
||||
__le32 width;
|
||||
__le32 height;
|
||||
u8 bits_per_pixel;
|
||||
__le32 bytes_per_row;
|
||||
__le32 orientation;
|
||||
__le32 bitmap_info;
|
||||
__le32 pixel_format;
|
||||
__le32 width_inches; /* floating point */
|
||||
__le32 height_inches; /* floating point */
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_frame {
|
||||
__le16 begin_x;
|
||||
__le16 begin_y;
|
||||
__le16 width;
|
||||
__le16 height;
|
||||
__le32 buf_size;
|
||||
u8 buf[];
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_fb_request_footer {
|
||||
u8 unk_00[12];
|
||||
__le32 unk_0c;
|
||||
u8 unk_10[12];
|
||||
__le32 unk_1c;
|
||||
__le64 timestamp;
|
||||
u8 unk_28[12];
|
||||
__le32 unk_34;
|
||||
u8 unk_38[20];
|
||||
__le32 unk_4c;
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_fb_request {
|
||||
struct appletbdrm_msg_request_header header;
|
||||
__le16 unk_10;
|
||||
u8 msg_id;
|
||||
u8 unk_13[29];
|
||||
/*
|
||||
* Contents of `data`:
|
||||
* - struct appletbdrm_frame frames[];
|
||||
* - struct appletbdrm_fb_request_footer footer;
|
||||
* - padding to make the total size a multiple of 16
|
||||
*/
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_fb_request_response {
|
||||
struct appletbdrm_msg_response_header header;
|
||||
u8 unk_14[12];
|
||||
__le64 timestamp;
|
||||
} __packed;
|
||||
|
||||
struct appletbdrm_device {
|
||||
struct device *dmadev;
|
||||
|
||||
unsigned int in_ep;
|
||||
unsigned int out_ep;
|
||||
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
|
||||
struct drm_device drm;
|
||||
struct drm_display_mode mode;
|
||||
struct drm_connector connector;
|
||||
struct drm_plane primary_plane;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
};
|
||||
|
||||
struct appletbdrm_plane_state {
|
||||
struct drm_shadow_plane_state base;
|
||||
struct appletbdrm_fb_request *request;
|
||||
struct appletbdrm_fb_request_response *response;
|
||||
size_t request_size;
|
||||
size_t frames_size;
|
||||
};
|
||||
|
||||
static inline struct appletbdrm_plane_state *to_appletbdrm_plane_state(struct drm_plane_state *state)
|
||||
{
|
||||
return container_of(state, struct appletbdrm_plane_state, base.base);
|
||||
}
|
||||
|
||||
static int appletbdrm_send_request(struct appletbdrm_device *adev,
|
||||
struct appletbdrm_msg_request_header *request, size_t size)
|
||||
{
|
||||
struct usb_device *udev = adev_to_udev(adev);
|
||||
struct drm_device *drm = &adev->drm;
|
||||
int ret, actual_size;
|
||||
|
||||
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, adev->out_ep),
|
||||
request, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to send message (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (actual_size != size) {
|
||||
drm_err(drm, "Actual size (%d) doesn't match expected size (%zu)\n",
|
||||
actual_size, size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int appletbdrm_read_response(struct appletbdrm_device *adev,
|
||||
struct appletbdrm_msg_response_header *response,
|
||||
size_t size, __le32 expected_response)
|
||||
{
|
||||
struct usb_device *udev = adev_to_udev(adev);
|
||||
struct drm_device *drm = &adev->drm;
|
||||
int ret, actual_size;
|
||||
bool readiness_signal_received = false;
|
||||
|
||||
retry:
|
||||
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, adev->in_ep),
|
||||
response, size, &actual_size, APPLETBDRM_BULK_MSG_TIMEOUT);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to read response (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The device responds to the first request sent in a particular
|
||||
* timeframe after the USB device configuration is set with a readiness
|
||||
* signal, in which case the response should be read again
|
||||
*/
|
||||
if (response->msg == APPLETBDRM_MSG_SIGNAL_READINESS) {
|
||||
if (!readiness_signal_received) {
|
||||
readiness_signal_received = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
drm_err(drm, "Encountered unexpected readiness signal\n");
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
if (actual_size != size) {
|
||||
drm_err(drm, "Actual size (%d) doesn't match expected size (%zu)\n",
|
||||
actual_size, size);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (response->msg != expected_response) {
|
||||
drm_err(drm, "Unexpected response from device (expected %p4cc found %p4cc)\n",
|
||||
&expected_response, &response->msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int appletbdrm_send_msg(struct appletbdrm_device *adev, __le32 msg)
|
||||
{
|
||||
struct appletbdrm_msg_simple_request *request;
|
||||
int ret;
|
||||
|
||||
request = kzalloc(sizeof(*request), GFP_KERNEL);
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
|
||||
request->header.unk_00 = cpu_to_le16(2);
|
||||
request->header.unk_02 = cpu_to_le16(0x1512);
|
||||
request->header.size = cpu_to_le32(sizeof(*request) - sizeof(request->header));
|
||||
request->msg = msg;
|
||||
request->size = request->header.size;
|
||||
|
||||
ret = appletbdrm_send_request(adev, &request->header, sizeof(*request));
|
||||
|
||||
kfree(request);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int appletbdrm_clear_display(struct appletbdrm_device *adev)
|
||||
{
|
||||
return appletbdrm_send_msg(adev, APPLETBDRM_MSG_CLEAR_DISPLAY);
|
||||
}
|
||||
|
||||
static int appletbdrm_signal_readiness(struct appletbdrm_device *adev)
|
||||
{
|
||||
return appletbdrm_send_msg(adev, APPLETBDRM_MSG_SIGNAL_READINESS);
|
||||
}
|
||||
|
||||
static int appletbdrm_get_information(struct appletbdrm_device *adev)
|
||||
{
|
||||
struct appletbdrm_msg_information *info;
|
||||
struct drm_device *drm = &adev->drm;
|
||||
u8 bits_per_pixel;
|
||||
__le32 pixel_format;
|
||||
int ret;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = appletbdrm_send_msg(adev, APPLETBDRM_MSG_GET_INFORMATION);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = appletbdrm_read_response(adev, &info->header, sizeof(*info),
|
||||
APPLETBDRM_MSG_GET_INFORMATION);
|
||||
if (ret)
|
||||
goto free_info;
|
||||
|
||||
bits_per_pixel = info->bits_per_pixel;
|
||||
pixel_format = get_unaligned(&info->pixel_format);
|
||||
|
||||
adev->width = get_unaligned_le32(&info->width);
|
||||
adev->height = get_unaligned_le32(&info->height);
|
||||
|
||||
if (bits_per_pixel != APPLETBDRM_BITS_PER_PIXEL) {
|
||||
drm_err(drm, "Encountered unexpected bits per pixel value (%d)\n", bits_per_pixel);
|
||||
ret = -EINVAL;
|
||||
goto free_info;
|
||||
}
|
||||
|
||||
if (pixel_format != APPLETBDRM_PIXEL_FORMAT) {
|
||||
drm_err(drm, "Encountered unknown pixel format (%p4cc)\n", &pixel_format);
|
||||
ret = -EINVAL;
|
||||
goto free_info;
|
||||
}
|
||||
|
||||
free_info:
|
||||
kfree(info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 rect_size(struct drm_rect *rect)
|
||||
{
|
||||
return drm_rect_width(rect) * drm_rect_height(rect) *
|
||||
(BITS_TO_BYTES(APPLETBDRM_BITS_PER_PIXEL));
|
||||
}
|
||||
|
||||
static int appletbdrm_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct appletbdrm_device *adev = drm_to_adev(connector->dev);
|
||||
|
||||
return drm_connector_helper_get_modes_fixed(connector, &adev->mode);
|
||||
}
|
||||
|
||||
static const u32 appletbdrm_primary_plane_formats[] = {
|
||||
DRM_FORMAT_BGR888,
|
||||
DRM_FORMAT_XRGB8888, /* emulated */
|
||||
};
|
||||
|
||||
static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
|
||||
struct drm_crtc *new_crtc = new_plane_state->crtc;
|
||||
struct drm_crtc_state *new_crtc_state = NULL;
|
||||
struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state);
|
||||
struct drm_atomic_helper_damage_iter iter;
|
||||
struct drm_rect damage;
|
||||
size_t frames_size = 0;
|
||||
size_t request_size;
|
||||
int ret;
|
||||
|
||||
if (new_crtc)
|
||||
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
false, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
else if (!new_plane_state->visible)
|
||||
return 0;
|
||||
|
||||
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state);
|
||||
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
||||
frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage));
|
||||
}
|
||||
|
||||
if (!frames_size)
|
||||
return 0;
|
||||
|
||||
request_size = ALIGN(sizeof(struct appletbdrm_fb_request) +
|
||||
frames_size +
|
||||
sizeof(struct appletbdrm_fb_request_footer), 16);
|
||||
|
||||
appletbdrm_state->request = kzalloc(request_size, GFP_KERNEL);
|
||||
|
||||
if (!appletbdrm_state->request)
|
||||
return -ENOMEM;
|
||||
|
||||
appletbdrm_state->response = kzalloc(sizeof(*appletbdrm_state->response), GFP_KERNEL);
|
||||
|
||||
if (!appletbdrm_state->response)
|
||||
return -ENOMEM;
|
||||
|
||||
appletbdrm_state->request_size = request_size;
|
||||
appletbdrm_state->frames_size = frames_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int appletbdrm_flush_damage(struct appletbdrm_device *adev,
|
||||
struct drm_plane_state *old_state,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(state);
|
||||
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
|
||||
struct appletbdrm_fb_request_response *response = appletbdrm_state->response;
|
||||
struct appletbdrm_fb_request_footer *footer;
|
||||
struct drm_atomic_helper_damage_iter iter;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct appletbdrm_fb_request *request = appletbdrm_state->request;
|
||||
struct drm_device *drm = &adev->drm;
|
||||
struct appletbdrm_frame *frame;
|
||||
u64 timestamp = ktime_get_ns();
|
||||
struct drm_rect damage;
|
||||
size_t frames_size = appletbdrm_state->frames_size;
|
||||
size_t request_size = appletbdrm_state->request_size;
|
||||
int ret;
|
||||
|
||||
if (!frames_size)
|
||||
return 0;
|
||||
|
||||
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to start CPU framebuffer access (%d)\n", ret);
|
||||
goto end_fb_cpu_access;
|
||||
}
|
||||
|
||||
request->header.unk_00 = cpu_to_le16(2);
|
||||
request->header.unk_02 = cpu_to_le16(0x12);
|
||||
request->header.unk_04 = cpu_to_le32(9);
|
||||
request->header.size = cpu_to_le32(request_size - sizeof(request->header));
|
||||
request->unk_10 = cpu_to_le16(1);
|
||||
request->msg_id = timestamp;
|
||||
|
||||
frame = (struct appletbdrm_frame *)request->data;
|
||||
|
||||
drm_atomic_helper_damage_iter_init(&iter, old_state, state);
|
||||
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
||||
struct drm_rect dst_clip = state->dst;
|
||||
struct iosys_map dst = IOSYS_MAP_INIT_VADDR(frame->buf);
|
||||
u32 buf_size = rect_size(&damage);
|
||||
|
||||
if (!drm_rect_intersect(&dst_clip, &damage))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The coordinates need to be translated to the coordinate
|
||||
* system the device expects, see the comment in
|
||||
* appletbdrm_setup_mode_config
|
||||
*/
|
||||
frame->begin_x = cpu_to_le16(damage.y1);
|
||||
frame->begin_y = cpu_to_le16(adev->height - damage.x2);
|
||||
frame->width = cpu_to_le16(drm_rect_height(&damage));
|
||||
frame->height = cpu_to_le16(drm_rect_width(&damage));
|
||||
frame->buf_size = cpu_to_le32(buf_size);
|
||||
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
drm_fb_xrgb8888_to_bgr888(&dst, NULL, &shadow_plane_state->data[0], fb, &damage, &shadow_plane_state->fmtcnv_state);
|
||||
break;
|
||||
default:
|
||||
drm_fb_memcpy(&dst, NULL, &shadow_plane_state->data[0], fb, &damage);
|
||||
break;
|
||||
}
|
||||
|
||||
frame = (void *)frame + struct_size(frame, buf, buf_size);
|
||||
}
|
||||
|
||||
footer = (struct appletbdrm_fb_request_footer *)&request->data[frames_size];
|
||||
|
||||
footer->unk_0c = cpu_to_le32(0xfffe);
|
||||
footer->unk_1c = cpu_to_le32(0x80001);
|
||||
footer->unk_34 = cpu_to_le32(0x80002);
|
||||
footer->unk_4c = cpu_to_le32(0xffff);
|
||||
footer->timestamp = cpu_to_le64(timestamp);
|
||||
|
||||
ret = appletbdrm_send_request(adev, &request->header, request_size);
|
||||
if (ret)
|
||||
goto end_fb_cpu_access;
|
||||
|
||||
ret = appletbdrm_read_response(adev, &response->header, sizeof(*response),
|
||||
APPLETBDRM_MSG_UPDATE_COMPLETE);
|
||||
if (ret)
|
||||
goto end_fb_cpu_access;
|
||||
|
||||
if (response->timestamp != footer->timestamp) {
|
||||
drm_err(drm, "Response timestamp (%llu) doesn't match request timestamp (%llu)\n",
|
||||
le64_to_cpu(response->timestamp), timestamp);
|
||||
goto end_fb_cpu_access;
|
||||
}
|
||||
|
||||
end_fb_cpu_access:
|
||||
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane,
|
||||
struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct appletbdrm_device *adev = drm_to_adev(plane->dev);
|
||||
struct drm_device *drm = plane->dev;
|
||||
struct drm_plane_state *plane_state = plane->state;
|
||||
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane);
|
||||
int idx;
|
||||
|
||||
if (!drm_dev_enter(drm, &idx))
|
||||
return;
|
||||
|
||||
appletbdrm_flush_damage(adev, old_plane_state, plane_state);
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static void appletbdrm_primary_plane_helper_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *dev = plane->dev;
|
||||
struct appletbdrm_device *adev = drm_to_adev(dev);
|
||||
int idx;
|
||||
|
||||
if (!drm_dev_enter(dev, &idx))
|
||||
return;
|
||||
|
||||
appletbdrm_clear_display(adev);
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static void appletbdrm_primary_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct appletbdrm_plane_state *appletbdrm_state;
|
||||
|
||||
WARN_ON(plane->state);
|
||||
|
||||
appletbdrm_state = kzalloc(sizeof(*appletbdrm_state), GFP_KERNEL);
|
||||
if (!appletbdrm_state)
|
||||
return;
|
||||
|
||||
__drm_gem_reset_shadow_plane(plane, &appletbdrm_state->base);
|
||||
}
|
||||
|
||||
static struct drm_plane_state *appletbdrm_primary_plane_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_shadow_plane_state *new_shadow_plane_state;
|
||||
struct appletbdrm_plane_state *appletbdrm_state;
|
||||
|
||||
if (WARN_ON(!plane->state))
|
||||
return NULL;
|
||||
|
||||
appletbdrm_state = kzalloc(sizeof(*appletbdrm_state), GFP_KERNEL);
|
||||
if (!appletbdrm_state)
|
||||
return NULL;
|
||||
|
||||
/* Request and response are not duplicated and are allocated in .atomic_check */
|
||||
appletbdrm_state->request = NULL;
|
||||
appletbdrm_state->response = NULL;
|
||||
|
||||
appletbdrm_state->request_size = 0;
|
||||
appletbdrm_state->frames_size = 0;
|
||||
|
||||
new_shadow_plane_state = &appletbdrm_state->base;
|
||||
|
||||
__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
|
||||
|
||||
return &new_shadow_plane_state->base;
|
||||
}
|
||||
|
||||
static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(state);
|
||||
|
||||
kfree(appletbdrm_state->request);
|
||||
kfree(appletbdrm_state->response);
|
||||
|
||||
__drm_gem_destroy_shadow_plane_state(&appletbdrm_state->base);
|
||||
|
||||
kfree(appletbdrm_state);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = {
|
||||
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
|
||||
.atomic_check = appletbdrm_primary_plane_helper_atomic_check,
|
||||
.atomic_update = appletbdrm_primary_plane_helper_atomic_update,
|
||||
.atomic_disable = appletbdrm_primary_plane_helper_atomic_disable,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs appletbdrm_primary_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.reset = appletbdrm_primary_plane_reset,
|
||||
.atomic_duplicate_state = appletbdrm_primary_plane_duplicate_state,
|
||||
.atomic_destroy_state = appletbdrm_primary_plane_destroy_state,
|
||||
.destroy = drm_plane_cleanup,
|
||||
};
|
||||
|
||||
static enum drm_mode_status appletbdrm_crtc_helper_mode_valid(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct appletbdrm_device *adev = drm_to_adev(crtc->dev);
|
||||
|
||||
return drm_crtc_helper_mode_valid_fixed(crtc, mode, &adev->mode);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs appletbdrm_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create_with_dirty,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs appletbdrm_connector_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs appletbdrm_connector_helper_funcs = {
|
||||
.get_modes = appletbdrm_connector_helper_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_helper_funcs appletbdrm_crtc_helper_funcs = {
|
||||
.mode_valid = appletbdrm_crtc_helper_mode_valid,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs appletbdrm_crtc_funcs = {
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs appletbdrm_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static struct drm_gem_object *appletbdrm_driver_gem_prime_import(struct drm_device *dev,
|
||||
struct dma_buf *dma_buf)
|
||||
{
|
||||
struct appletbdrm_device *adev = drm_to_adev(dev);
|
||||
|
||||
if (!adev->dmadev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return drm_gem_prime_import_dev(dev, dma_buf, adev->dmadev);
|
||||
}
|
||||
|
||||
DEFINE_DRM_GEM_FOPS(appletbdrm_drm_fops);
|
||||
|
||||
static const struct drm_driver appletbdrm_drm_driver = {
|
||||
DRM_GEM_SHMEM_DRIVER_OPS,
|
||||
.gem_prime_import = appletbdrm_driver_gem_prime_import,
|
||||
.name = "appletbdrm",
|
||||
.desc = "Apple Touch Bar DRM Driver",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
|
||||
.fops = &appletbdrm_drm_fops,
|
||||
};
|
||||
|
||||
static int appletbdrm_setup_mode_config(struct appletbdrm_device *adev)
|
||||
{
|
||||
struct drm_connector *connector = &adev->connector;
|
||||
struct drm_plane *primary_plane;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_device *drm = &adev->drm;
|
||||
int ret;
|
||||
|
||||
ret = drmm_mode_config_init(drm);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to initialize mode configuration\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
primary_plane = &adev->primary_plane;
|
||||
ret = drm_universal_plane_init(drm, primary_plane, 0,
|
||||
&appletbdrm_primary_plane_funcs,
|
||||
appletbdrm_primary_plane_formats,
|
||||
ARRAY_SIZE(appletbdrm_primary_plane_formats),
|
||||
NULL,
|
||||
DRM_PLANE_TYPE_PRIMARY, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to initialize universal plane object\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_plane_helper_add(primary_plane, &appletbdrm_primary_plane_helper_funcs);
|
||||
drm_plane_enable_fb_damage_clips(primary_plane);
|
||||
|
||||
crtc = &adev->crtc;
|
||||
ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
|
||||
&appletbdrm_crtc_funcs, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to initialize CRTC object\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(crtc, &appletbdrm_crtc_helper_funcs);
|
||||
|
||||
encoder = &adev->encoder;
|
||||
ret = drm_encoder_init(drm, encoder, &appletbdrm_encoder_funcs,
|
||||
DRM_MODE_ENCODER_DAC, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to initialize encoder\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
|
||||
/*
|
||||
* The coordinate system used by the device is different from the
|
||||
* coordinate system of the framebuffer in that the x and y axes are
|
||||
* swapped, and that the y axis is inverted; so what the device reports
|
||||
* as the height is actually the width of the framebuffer and vice
|
||||
* versa.
|
||||
*/
|
||||
drm->mode_config.max_width = max(adev->height, DRM_SHADOW_PLANE_MAX_WIDTH);
|
||||
drm->mode_config.max_height = max(adev->width, DRM_SHADOW_PLANE_MAX_HEIGHT);
|
||||
drm->mode_config.preferred_depth = APPLETBDRM_BITS_PER_PIXEL;
|
||||
drm->mode_config.funcs = &appletbdrm_mode_config_funcs;
|
||||
|
||||
adev->mode = (struct drm_display_mode) {
|
||||
DRM_MODE_INIT(60, adev->height, adev->width,
|
||||
DRM_MODE_RES_MM(adev->height, 218),
|
||||
DRM_MODE_RES_MM(adev->width, 218))
|
||||
};
|
||||
|
||||
ret = drm_connector_init(drm, connector,
|
||||
&appletbdrm_connector_funcs, DRM_MODE_CONNECTOR_USB);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to initialize connector\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &appletbdrm_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_set_panel_orientation(connector,
|
||||
DRM_MODE_PANEL_ORIENTATION_RIGHT_UP);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to set panel orientation\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
connector->display_info.non_desktop = true;
|
||||
ret = drm_object_property_set_value(&connector->base,
|
||||
drm->mode_config.non_desktop_property, true);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to set non-desktop property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_connector_attach_encoder(connector, encoder);
|
||||
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to initialize simple display pipe\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int appletbdrm_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_endpoint_descriptor *bulk_in, *bulk_out;
|
||||
struct device *dev = &intf->dev;
|
||||
struct appletbdrm_device *adev;
|
||||
struct drm_device *drm = NULL;
|
||||
int ret;
|
||||
|
||||
ret = usb_find_common_endpoints(intf->cur_altsetting, &bulk_in, &bulk_out, NULL, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm, "appletbdrm: Failed to find bulk endpoints\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adev = devm_drm_dev_alloc(dev, &appletbdrm_drm_driver, struct appletbdrm_device, drm);
|
||||
if (IS_ERR(adev))
|
||||
return PTR_ERR(adev);
|
||||
|
||||
adev->in_ep = bulk_in->bEndpointAddress;
|
||||
adev->out_ep = bulk_out->bEndpointAddress;
|
||||
adev->dmadev = dev;
|
||||
|
||||
drm = &adev->drm;
|
||||
|
||||
usb_set_intfdata(intf, adev);
|
||||
|
||||
ret = appletbdrm_get_information(adev);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to get display information\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = appletbdrm_signal_readiness(adev);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to signal readiness\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = appletbdrm_setup_mode_config(adev);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to setup mode config\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to register DRM device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = appletbdrm_clear_display(adev);
|
||||
if (ret) {
|
||||
drm_err(drm, "Failed to clear display\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void appletbdrm_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct appletbdrm_device *adev = usb_get_intfdata(intf);
|
||||
struct drm_device *drm = &adev->drm;
|
||||
|
||||
put_device(adev->dmadev);
|
||||
drm_dev_unplug(drm);
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
}
|
||||
|
||||
static void appletbdrm_shutdown(struct usb_interface *intf)
|
||||
{
|
||||
struct appletbdrm_device *adev = usb_get_intfdata(intf);
|
||||
|
||||
/*
|
||||
* The framebuffer needs to be cleared on shutdown since its content
|
||||
* persists across boots
|
||||
*/
|
||||
drm_atomic_helper_shutdown(&adev->drm);
|
||||
}
|
||||
|
||||
static const struct usb_device_id appletbdrm_usb_id_table[] = {
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x05ac, 0x8302, USB_CLASS_AUDIO_VIDEO) },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, appletbdrm_usb_id_table);
|
||||
|
||||
static struct usb_driver appletbdrm_usb_driver = {
|
||||
.name = "appletbdrm",
|
||||
.probe = appletbdrm_probe,
|
||||
.disconnect = appletbdrm_disconnect,
|
||||
.shutdown = appletbdrm_shutdown,
|
||||
.id_table = appletbdrm_usb_id_table,
|
||||
};
|
||||
module_usb_driver(appletbdrm_usb_driver);
|
||||
|
||||
MODULE_AUTHOR("Kerem Karabay <kekrby@gmail.com>");
|
||||
MODULE_DESCRIPTION("Apple Touch Bar DRM Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
ttm-y := ttm_tt.o ttm_bo.o ttm_bo_util.o ttm_bo_vm.o ttm_module.o \
|
||||
ttm_execbuf_util.o ttm_range_manager.o ttm_resource.o ttm_pool.o \
|
||||
ttm_device.o ttm_sys_manager.o
|
||||
ttm_device.o ttm_sys_manager.o ttm_backup.o
|
||||
ttm-$(CONFIG_AGP) += ttm_agp_backend.o
|
||||
|
||||
obj-$(CONFIG_DRM_TTM) += ttm.o
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright © 2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <drm/ttm/ttm_backup.h>
|
||||
#include <linux/page-flags.h>
|
||||
#include <linux/swap.h>
|
||||
|
||||
/*
|
||||
* Casting from randomized struct file * to struct ttm_backup * is fine since
|
||||
* struct ttm_backup is never defined nor dereferenced.
|
||||
*/
|
||||
static struct file *ttm_backup_to_file(struct ttm_backup *backup)
|
||||
{
|
||||
return (void *)backup;
|
||||
}
|
||||
|
||||
static struct ttm_backup *ttm_file_to_backup(struct file *file)
|
||||
{
|
||||
return (void *)file;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to map shmem indices to handle since a handle value
|
||||
* of 0 means error, following the swp_entry_t convention.
|
||||
*/
|
||||
static unsigned long ttm_backup_shmem_idx_to_handle(pgoff_t idx)
|
||||
{
|
||||
return (unsigned long)idx + 1;
|
||||
}
|
||||
|
||||
static pgoff_t ttm_backup_handle_to_shmem_idx(pgoff_t handle)
|
||||
{
|
||||
return handle - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_backup_drop() - release memory associated with a handle
|
||||
* @backup: The struct backup pointer used to obtain the handle
|
||||
* @handle: The handle obtained from the @backup_page function.
|
||||
*/
|
||||
void ttm_backup_drop(struct ttm_backup *backup, pgoff_t handle)
|
||||
{
|
||||
loff_t start = ttm_backup_handle_to_shmem_idx(handle);
|
||||
|
||||
start <<= PAGE_SHIFT;
|
||||
shmem_truncate_range(file_inode(ttm_backup_to_file(backup)), start,
|
||||
start + PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_backup_copy_page() - Copy the contents of a previously backed
|
||||
* up page
|
||||
* @backup: The struct backup pointer used to back up the page.
|
||||
* @dst: The struct page to copy into.
|
||||
* @handle: The handle returned when the page was backed up.
|
||||
* @intr: Try to perform waits interruptable or at least killable.
|
||||
*
|
||||
* Return: 0 on success, Negative error code on failure, notably
|
||||
* -EINTR if @intr was set to true and a signal is pending.
|
||||
*/
|
||||
int ttm_backup_copy_page(struct ttm_backup *backup, struct page *dst,
|
||||
pgoff_t handle, bool intr)
|
||||
{
|
||||
struct file *filp = ttm_backup_to_file(backup);
|
||||
struct address_space *mapping = filp->f_mapping;
|
||||
struct folio *from_folio;
|
||||
pgoff_t idx = ttm_backup_handle_to_shmem_idx(handle);
|
||||
|
||||
from_folio = shmem_read_folio(mapping, idx);
|
||||
if (IS_ERR(from_folio))
|
||||
return PTR_ERR(from_folio);
|
||||
|
||||
copy_highpage(dst, folio_file_page(from_folio, idx));
|
||||
folio_put(from_folio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_backup_backup_page() - Backup a page
|
||||
* @backup: The struct backup pointer to use.
|
||||
* @page: The page to back up.
|
||||
* @writeback: Whether to perform immediate writeback of the page.
|
||||
* This may have performance implications.
|
||||
* @idx: A unique integer for each page and each struct backup.
|
||||
* This allows the backup implementation to avoid managing
|
||||
* its address space separately.
|
||||
* @page_gfp: The gfp value used when the page was allocated.
|
||||
* This is used for accounting purposes.
|
||||
* @alloc_gfp: The gfp to be used when allocating memory.
|
||||
*
|
||||
* Context: If called from reclaim context, the caller needs to
|
||||
* assert that the shrinker gfp has __GFP_FS set, to avoid
|
||||
* deadlocking on lock_page(). If @writeback is set to true and
|
||||
* called from reclaim context, the caller also needs to assert
|
||||
* that the shrinker gfp has __GFP_IO set, since without it,
|
||||
* we're not allowed to start backup IO.
|
||||
*
|
||||
* Return: A handle on success. Negative error code on failure.
|
||||
*
|
||||
* Note: This function could be extended to back up a folio and
|
||||
* implementations would then split the folio internally if needed.
|
||||
* Drawback is that the caller would then have to keep track of
|
||||
* the folio size- and usage.
|
||||
*/
|
||||
s64
|
||||
ttm_backup_backup_page(struct ttm_backup *backup, struct page *page,
|
||||
bool writeback, pgoff_t idx, gfp_t page_gfp,
|
||||
gfp_t alloc_gfp)
|
||||
{
|
||||
struct file *filp = ttm_backup_to_file(backup);
|
||||
struct address_space *mapping = filp->f_mapping;
|
||||
unsigned long handle = 0;
|
||||
struct folio *to_folio;
|
||||
int ret;
|
||||
|
||||
to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp);
|
||||
if (IS_ERR(to_folio))
|
||||
return PTR_ERR(to_folio);
|
||||
|
||||
folio_mark_accessed(to_folio);
|
||||
folio_lock(to_folio);
|
||||
folio_mark_dirty(to_folio);
|
||||
copy_highpage(folio_file_page(to_folio, idx), page);
|
||||
handle = ttm_backup_shmem_idx_to_handle(idx);
|
||||
|
||||
if (writeback && !folio_mapped(to_folio) &&
|
||||
folio_clear_dirty_for_io(to_folio)) {
|
||||
struct writeback_control wbc = {
|
||||
.sync_mode = WB_SYNC_NONE,
|
||||
.nr_to_write = SWAP_CLUSTER_MAX,
|
||||
.range_start = 0,
|
||||
.range_end = LLONG_MAX,
|
||||
.for_reclaim = 1,
|
||||
};
|
||||
folio_set_reclaim(to_folio);
|
||||
ret = mapping->a_ops->writepage(folio_file_page(to_folio, idx), &wbc);
|
||||
if (!folio_test_writeback(to_folio))
|
||||
folio_clear_reclaim(to_folio);
|
||||
/*
|
||||
* If writepage succeeds, it unlocks the folio.
|
||||
* writepage() errors are otherwise dropped, since writepage()
|
||||
* is only best effort here.
|
||||
*/
|
||||
if (ret)
|
||||
folio_unlock(to_folio);
|
||||
} else {
|
||||
folio_unlock(to_folio);
|
||||
}
|
||||
|
||||
folio_put(to_folio);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_backup_fini() - Free the struct backup resources after last use.
|
||||
* @backup: Pointer to the struct backup whose resources to free.
|
||||
*
|
||||
* After a call to this function, it's illegal to use the @backup pointer.
|
||||
*/
|
||||
void ttm_backup_fini(struct ttm_backup *backup)
|
||||
{
|
||||
fput(ttm_backup_to_file(backup));
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_backup_bytes_avail() - Report the approximate number of bytes of backup space
|
||||
* left for backup.
|
||||
*
|
||||
* This function is intended also for driver use to indicate whether a
|
||||
* backup attempt is meaningful.
|
||||
*
|
||||
* Return: An approximate size of backup space available.
|
||||
*/
|
||||
u64 ttm_backup_bytes_avail(void)
|
||||
{
|
||||
/*
|
||||
* The idea behind backing up to shmem is that shmem objects may
|
||||
* eventually be swapped out. So no point swapping out if there
|
||||
* is no or low swap-space available. But the accuracy of this
|
||||
* number also depends on shmem actually swapping out backed-up
|
||||
* shmem objects without too much buffering.
|
||||
*/
|
||||
return (u64)get_nr_swap_pages() << PAGE_SHIFT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ttm_backup_bytes_avail);
|
||||
|
||||
/**
|
||||
* ttm_backup_shmem_create() - Create a shmem-based struct backup.
|
||||
* @size: The maximum size (in bytes) to back up.
|
||||
*
|
||||
* Create a backup utilizing shmem objects.
|
||||
*
|
||||
* Return: A pointer to a struct ttm_backup on success,
|
||||
* an error pointer on error.
|
||||
*/
|
||||
struct ttm_backup *ttm_backup_shmem_create(loff_t size)
|
||||
{
|
||||
struct file *filp;
|
||||
|
||||
filp = shmem_file_setup("ttm shmem backup", size, 0);
|
||||
|
||||
return ttm_file_to_backup(filp);
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
/*
|
||||
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
||||
*/
|
||||
|
||||
#include <linux/swap.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <drm/ttm/ttm_bo.h>
|
||||
@@ -769,12 +769,10 @@ error_destroy_tt:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool ttm_lru_walk_trylock(struct ttm_lru_walk *walk,
|
||||
static bool ttm_lru_walk_trylock(struct ttm_operation_ctx *ctx,
|
||||
struct ttm_buffer_object *bo,
|
||||
bool *needs_unlock)
|
||||
{
|
||||
struct ttm_operation_ctx *ctx = walk->ctx;
|
||||
|
||||
*needs_unlock = false;
|
||||
|
||||
if (dma_resv_trylock(bo->base.resv)) {
|
||||
@@ -877,7 +875,7 @@ s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev,
|
||||
* since if we do it the other way around, and the trylock fails,
|
||||
* we need to drop the lru lock to put the bo.
|
||||
*/
|
||||
if (ttm_lru_walk_trylock(walk, bo, &bo_needs_unlock))
|
||||
if (ttm_lru_walk_trylock(walk->ctx, bo, &bo_needs_unlock))
|
||||
bo_locked = true;
|
||||
else if (!walk->ticket || walk->ctx->no_wait_gpu ||
|
||||
walk->trylock_only)
|
||||
@@ -920,3 +918,242 @@ s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev,
|
||||
|
||||
return progress;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_lru_walk_for_evict);
|
||||
|
||||
static void ttm_bo_lru_cursor_cleanup_bo(struct ttm_bo_lru_cursor *curs)
|
||||
{
|
||||
struct ttm_buffer_object *bo = curs->bo;
|
||||
|
||||
if (bo) {
|
||||
if (curs->needs_unlock)
|
||||
dma_resv_unlock(bo->base.resv);
|
||||
ttm_bo_put(bo);
|
||||
curs->bo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_bo_lru_cursor_fini() - Stop using a struct ttm_bo_lru_cursor
|
||||
* and clean up any iteration it was used for.
|
||||
* @curs: The cursor.
|
||||
*/
|
||||
void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs)
|
||||
{
|
||||
spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock;
|
||||
|
||||
ttm_bo_lru_cursor_cleanup_bo(curs);
|
||||
spin_lock(lru_lock);
|
||||
ttm_resource_cursor_fini(&curs->res_curs);
|
||||
spin_unlock(lru_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_lru_cursor_fini);
|
||||
|
||||
/**
|
||||
* ttm_bo_lru_cursor_init() - Initialize a struct ttm_bo_lru_cursor
|
||||
* @curs: The ttm_bo_lru_cursor to initialize.
|
||||
* @man: The ttm resource_manager whose LRU lists to iterate over.
|
||||
* @ctx: The ttm_operation_ctx to govern the locking.
|
||||
*
|
||||
* Initialize a struct ttm_bo_lru_cursor. Currently only trylocking
|
||||
* or prelocked buffer objects are available as detailed by
|
||||
* @ctx::resv and @ctx::allow_res_evict. Ticketlocking is not
|
||||
* supported.
|
||||
*
|
||||
* Return: Pointer to @curs. The function does not fail.
|
||||
*/
|
||||
struct ttm_bo_lru_cursor *
|
||||
ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs,
|
||||
struct ttm_resource_manager *man,
|
||||
struct ttm_operation_ctx *ctx)
|
||||
{
|
||||
memset(curs, 0, sizeof(*curs));
|
||||
ttm_resource_cursor_init(&curs->res_curs, man);
|
||||
curs->ctx = ctx;
|
||||
|
||||
return curs;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_lru_cursor_init);
|
||||
|
||||
static struct ttm_buffer_object *
|
||||
ttm_bo_from_res_reserved(struct ttm_resource *res, struct ttm_bo_lru_cursor *curs)
|
||||
{
|
||||
struct ttm_buffer_object *bo = res->bo;
|
||||
|
||||
if (!ttm_lru_walk_trylock(curs->ctx, bo, &curs->needs_unlock))
|
||||
return NULL;
|
||||
|
||||
if (!ttm_bo_get_unless_zero(bo)) {
|
||||
if (curs->needs_unlock)
|
||||
dma_resv_unlock(bo->base.resv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curs->bo = bo;
|
||||
return bo;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_bo_lru_cursor_next() - Continue iterating a manager's LRU lists
|
||||
* to find and lock buffer object.
|
||||
* @curs: The cursor initialized using ttm_bo_lru_cursor_init() and
|
||||
* ttm_bo_lru_cursor_first().
|
||||
*
|
||||
* Return: A pointer to a locked and reference-counted buffer object,
|
||||
* or NULL if none could be found and looping should be terminated.
|
||||
*/
|
||||
struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs)
|
||||
{
|
||||
spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock;
|
||||
struct ttm_resource *res = NULL;
|
||||
struct ttm_buffer_object *bo;
|
||||
|
||||
ttm_bo_lru_cursor_cleanup_bo(curs);
|
||||
|
||||
spin_lock(lru_lock);
|
||||
for (;;) {
|
||||
res = ttm_resource_manager_next(&curs->res_curs);
|
||||
if (!res)
|
||||
break;
|
||||
|
||||
bo = ttm_bo_from_res_reserved(res, curs);
|
||||
if (bo)
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(lru_lock);
|
||||
return res ? bo : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_lru_cursor_next);
|
||||
|
||||
/**
|
||||
* ttm_bo_lru_cursor_first() - Start iterating a manager's LRU lists
|
||||
* to find and lock buffer object.
|
||||
* @curs: The cursor initialized using ttm_bo_lru_cursor_init().
|
||||
*
|
||||
* Return: A pointer to a locked and reference-counted buffer object,
|
||||
* or NULL if none could be found and looping should be terminated.
|
||||
*/
|
||||
struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs)
|
||||
{
|
||||
spinlock_t *lru_lock = &curs->res_curs.man->bdev->lru_lock;
|
||||
struct ttm_buffer_object *bo;
|
||||
struct ttm_resource *res;
|
||||
|
||||
spin_lock(lru_lock);
|
||||
res = ttm_resource_manager_first(&curs->res_curs);
|
||||
if (!res) {
|
||||
spin_unlock(lru_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bo = ttm_bo_from_res_reserved(res, curs);
|
||||
spin_unlock(lru_lock);
|
||||
|
||||
return bo ? bo : ttm_bo_lru_cursor_next(curs);
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_lru_cursor_first);
|
||||
|
||||
/**
|
||||
* ttm_bo_shrink() - Helper to shrink a ttm buffer object.
|
||||
* @ctx: The struct ttm_operation_ctx used for the shrinking operation.
|
||||
* @bo: The buffer object.
|
||||
* @flags: Flags governing the shrinking behaviour.
|
||||
*
|
||||
* The function uses the ttm_tt_back_up functionality to back up or
|
||||
* purge a struct ttm_tt. If the bo is not in system, it's first
|
||||
* moved there.
|
||||
*
|
||||
* Return: The number of pages shrunken or purged, or
|
||||
* negative error code on failure.
|
||||
*/
|
||||
long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
|
||||
const struct ttm_bo_shrink_flags flags)
|
||||
{
|
||||
static const struct ttm_place sys_placement_flags = {
|
||||
.fpfn = 0,
|
||||
.lpfn = 0,
|
||||
.mem_type = TTM_PL_SYSTEM,
|
||||
.flags = 0,
|
||||
};
|
||||
static struct ttm_placement sys_placement = {
|
||||
.num_placement = 1,
|
||||
.placement = &sys_placement_flags,
|
||||
};
|
||||
struct ttm_tt *tt = bo->ttm;
|
||||
long lret;
|
||||
|
||||
dma_resv_assert_held(bo->base.resv);
|
||||
|
||||
if (flags.allow_move && bo->resource->mem_type != TTM_PL_SYSTEM) {
|
||||
int ret = ttm_bo_validate(bo, &sys_placement, ctx);
|
||||
|
||||
/* Consider -ENOMEM and -ENOSPC non-fatal. */
|
||||
if (ret) {
|
||||
if (ret == -ENOMEM || ret == -ENOSPC)
|
||||
ret = -EBUSY;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ttm_bo_unmap_virtual(bo);
|
||||
lret = ttm_bo_wait_ctx(bo, ctx);
|
||||
if (lret < 0)
|
||||
return lret;
|
||||
|
||||
if (bo->bulk_move) {
|
||||
spin_lock(&bo->bdev->lru_lock);
|
||||
ttm_resource_del_bulk_move(bo->resource, bo);
|
||||
spin_unlock(&bo->bdev->lru_lock);
|
||||
}
|
||||
|
||||
lret = ttm_tt_backup(bo->bdev, tt, (struct ttm_backup_flags)
|
||||
{.purge = flags.purge,
|
||||
.writeback = flags.writeback});
|
||||
|
||||
if (lret <= 0 && bo->bulk_move) {
|
||||
spin_lock(&bo->bdev->lru_lock);
|
||||
ttm_resource_add_bulk_move(bo->resource, bo);
|
||||
spin_unlock(&bo->bdev->lru_lock);
|
||||
}
|
||||
|
||||
if (lret < 0 && lret != -EINTR)
|
||||
return -EBUSY;
|
||||
|
||||
return lret;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_shrink);
|
||||
|
||||
/**
|
||||
* ttm_bo_shrink_suitable() - Whether a bo is suitable for shinking
|
||||
* @ctx: The struct ttm_operation_ctx governing the shrinking.
|
||||
* @bo: The candidate for shrinking.
|
||||
*
|
||||
* Check whether the object, given the information available to TTM,
|
||||
* is suitable for shinking, This function can and should be used
|
||||
* before attempting to shrink an object.
|
||||
*
|
||||
* Return: true if suitable. false if not.
|
||||
*/
|
||||
bool ttm_bo_shrink_suitable(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx)
|
||||
{
|
||||
return bo->ttm && ttm_tt_is_populated(bo->ttm) && !bo->pin_count &&
|
||||
(!ctx->no_wait_gpu ||
|
||||
dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP));
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_shrink_suitable);
|
||||
|
||||
/**
|
||||
* ttm_bo_shrink_avoid_wait() - Whether to avoid waiting for GPU
|
||||
* during shrinking
|
||||
*
|
||||
* In some situations, like direct reclaim, waiting (in particular gpu waiting)
|
||||
* should be avoided since it may stall a system that could otherwise make progress
|
||||
* shrinking something else less time consuming.
|
||||
*
|
||||
* Return: true if gpu waiting should be avoided, false if not.
|
||||
*/
|
||||
bool ttm_bo_shrink_avoid_wait(void)
|
||||
{
|
||||
return !current_is_kswapd();
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_bo_shrink_avoid_wait);
|
||||
|
||||
+525
-52
@@ -41,12 +41,20 @@
|
||||
#include <asm/set_memory.h>
|
||||
#endif
|
||||
|
||||
#include <drm/ttm/ttm_backup.h>
|
||||
#include <drm/ttm/ttm_pool.h>
|
||||
#include <drm/ttm/ttm_tt.h>
|
||||
#include <drm/ttm/ttm_bo.h>
|
||||
|
||||
#include "ttm_module.h"
|
||||
|
||||
#ifdef CONFIG_FAULT_INJECTION
|
||||
#include <linux/fault-inject.h>
|
||||
static DECLARE_FAULT_ATTR(backup_fault_inject);
|
||||
#else
|
||||
#define should_fail(...) false
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct ttm_pool_dma - Helper object for coherent DMA mappings
|
||||
*
|
||||
@@ -75,6 +83,35 @@ struct ttm_pool_alloc_state {
|
||||
enum ttm_caching tt_caching;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ttm_pool_tt_restore - State representing restore from backup
|
||||
* @pool: The pool used for page allocation while restoring.
|
||||
* @snapshot_alloc: A snapshot of the most recent struct ttm_pool_alloc_state.
|
||||
* @alloced_page: Pointer to the page most recently allocated from a pool or system.
|
||||
* @first_dma: The dma address corresponding to @alloced_page if dma_mapping
|
||||
* is requested.
|
||||
* @alloced_pages: The number of allocated pages present in the struct ttm_tt
|
||||
* page vector from this restore session.
|
||||
* @restored_pages: The number of 4K pages restored for @alloced_page (which
|
||||
* is typically a multi-order page).
|
||||
* @page_caching: The struct ttm_tt requested caching
|
||||
* @order: The order of @alloced_page.
|
||||
*
|
||||
* Recovery from backup might fail when we've recovered less than the
|
||||
* full ttm_tt. In order not to loose any data (yet), keep information
|
||||
* around that allows us to restart a failed ttm backup recovery.
|
||||
*/
|
||||
struct ttm_pool_tt_restore {
|
||||
struct ttm_pool *pool;
|
||||
struct ttm_pool_alloc_state snapshot_alloc;
|
||||
struct page *alloced_page;
|
||||
dma_addr_t first_dma;
|
||||
pgoff_t alloced_pages;
|
||||
pgoff_t restored_pages;
|
||||
enum ttm_caching page_caching;
|
||||
unsigned int order;
|
||||
};
|
||||
|
||||
static unsigned long page_pool_size;
|
||||
|
||||
MODULE_PARM_DESC(page_pool_size, "Number of pages in the WC/UC/DMA pool");
|
||||
@@ -199,12 +236,11 @@ static int ttm_pool_apply_caching(struct ttm_pool_alloc_state *alloc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Map pages of 1 << order size and fill the DMA address array */
|
||||
/* DMA Map pages of 1 << order size and return the resulting dma_address. */
|
||||
static int ttm_pool_map(struct ttm_pool *pool, unsigned int order,
|
||||
struct page *p, dma_addr_t **dma_addr)
|
||||
struct page *p, dma_addr_t *dma_addr)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
unsigned int i;
|
||||
|
||||
if (pool->use_dma_alloc) {
|
||||
struct ttm_pool_dma *dma = (void *)p->private;
|
||||
@@ -218,10 +254,7 @@ static int ttm_pool_map(struct ttm_pool *pool, unsigned int order,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
for (i = 1 << order; i ; --i) {
|
||||
*(*dma_addr)++ = addr;
|
||||
addr += PAGE_SIZE;
|
||||
}
|
||||
*dma_addr = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -371,6 +404,196 @@ static unsigned int ttm_pool_page_order(struct ttm_pool *pool, struct page *p)
|
||||
return p->private;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split larger pages so that we can free each PAGE_SIZE page as soon
|
||||
* as it has been backed up, in order to avoid memory pressure during
|
||||
* reclaim.
|
||||
*/
|
||||
static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p)
|
||||
{
|
||||
unsigned int order = ttm_pool_page_order(pool, p);
|
||||
pgoff_t nr;
|
||||
|
||||
if (!order)
|
||||
return;
|
||||
|
||||
split_page(p, order);
|
||||
nr = 1UL << order;
|
||||
while (nr--)
|
||||
(p++)->private = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: Partial backup and restoration of a struct ttm_tt.
|
||||
*
|
||||
* Swapout using ttm_backup_backup_page() and swapin using
|
||||
* ttm_backup_copy_page() may fail.
|
||||
* The former most likely due to lack of swap-space or memory, the latter due
|
||||
* to lack of memory or because of signal interruption during waits.
|
||||
*
|
||||
* Backup failure is easily handled by using a ttm_tt pages vector that holds
|
||||
* both backup handles and page pointers. This has to be taken into account when
|
||||
* restoring such a ttm_tt from backup, and when freeing it while backed up.
|
||||
* When restoring, for simplicity, new pages are actually allocated from the
|
||||
* pool and the contents of any old pages are copied in and then the old pages
|
||||
* are released.
|
||||
*
|
||||
* For restoration failures, the struct ttm_pool_tt_restore holds sufficient state
|
||||
* to be able to resume an interrupted restore, and that structure is freed once
|
||||
* the restoration is complete. If the struct ttm_tt is destroyed while there
|
||||
* is a valid struct ttm_pool_tt_restore attached, that is also properly taken
|
||||
* care of.
|
||||
*/
|
||||
|
||||
/* Is restore ongoing for the currently allocated page? */
|
||||
static bool ttm_pool_restore_valid(const struct ttm_pool_tt_restore *restore)
|
||||
{
|
||||
return restore && restore->restored_pages < (1 << restore->order);
|
||||
}
|
||||
|
||||
/* DMA unmap and free a multi-order page, either to the relevant pool or to system. */
|
||||
static pgoff_t ttm_pool_unmap_and_free(struct ttm_pool *pool, struct page *page,
|
||||
const dma_addr_t *dma_addr, enum ttm_caching caching)
|
||||
{
|
||||
struct ttm_pool_type *pt = NULL;
|
||||
unsigned int order;
|
||||
pgoff_t nr;
|
||||
|
||||
if (pool) {
|
||||
order = ttm_pool_page_order(pool, page);
|
||||
nr = (1UL << order);
|
||||
if (dma_addr)
|
||||
ttm_pool_unmap(pool, *dma_addr, nr);
|
||||
|
||||
pt = ttm_pool_select_type(pool, caching, order);
|
||||
} else {
|
||||
order = page->private;
|
||||
nr = (1UL << order);
|
||||
}
|
||||
|
||||
if (pt)
|
||||
ttm_pool_type_give(pt, page);
|
||||
else
|
||||
ttm_pool_free_page(pool, caching, order, page);
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
/* Populate the page-array using the most recent allocated multi-order page. */
|
||||
static void ttm_pool_allocated_page_commit(struct page *allocated,
|
||||
dma_addr_t first_dma,
|
||||
struct ttm_pool_alloc_state *alloc,
|
||||
pgoff_t nr)
|
||||
{
|
||||
pgoff_t i;
|
||||
|
||||
for (i = 0; i < nr; ++i)
|
||||
*alloc->pages++ = allocated++;
|
||||
|
||||
alloc->remaining_pages -= nr;
|
||||
|
||||
if (!alloc->dma_addr)
|
||||
return;
|
||||
|
||||
for (i = 0; i < nr; ++i) {
|
||||
*alloc->dma_addr++ = first_dma;
|
||||
first_dma += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When restoring, restore backed-up content to the newly allocated page and
|
||||
* if successful, populate the page-table and dma-address arrays.
|
||||
*/
|
||||
static int ttm_pool_restore_commit(struct ttm_pool_tt_restore *restore,
|
||||
struct ttm_backup *backup,
|
||||
const struct ttm_operation_ctx *ctx,
|
||||
struct ttm_pool_alloc_state *alloc)
|
||||
|
||||
{
|
||||
pgoff_t i, nr = 1UL << restore->order;
|
||||
struct page **first_page = alloc->pages;
|
||||
struct page *p;
|
||||
int ret = 0;
|
||||
|
||||
for (i = restore->restored_pages; i < nr; ++i) {
|
||||
p = first_page[i];
|
||||
if (ttm_backup_page_ptr_is_handle(p)) {
|
||||
unsigned long handle = ttm_backup_page_ptr_to_handle(p);
|
||||
|
||||
if (IS_ENABLED(CONFIG_FAULT_INJECTION) && ctx->interruptible &&
|
||||
should_fail(&backup_fault_inject, 1)) {
|
||||
ret = -EINTR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (handle == 0) {
|
||||
restore->restored_pages++;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ttm_backup_copy_page(backup, restore->alloced_page + i,
|
||||
handle, ctx->interruptible);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ttm_backup_drop(backup, handle);
|
||||
} else if (p) {
|
||||
/*
|
||||
* We could probably avoid splitting the old page
|
||||
* using clever logic, but ATM we don't care, as
|
||||
* we prioritize releasing memory ASAP. Note that
|
||||
* here, the old retained page is always write-back
|
||||
* cached.
|
||||
*/
|
||||
ttm_pool_split_for_swap(restore->pool, p);
|
||||
copy_highpage(restore->alloced_page + i, p);
|
||||
__free_pages(p, 0);
|
||||
}
|
||||
|
||||
restore->restored_pages++;
|
||||
first_page[i] = ttm_backup_handle_to_page_ptr(0);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (!restore->restored_pages) {
|
||||
dma_addr_t *dma_addr = alloc->dma_addr ? &restore->first_dma : NULL;
|
||||
|
||||
ttm_pool_unmap_and_free(restore->pool, restore->alloced_page,
|
||||
dma_addr, restore->page_caching);
|
||||
restore->restored_pages = nr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ttm_pool_allocated_page_commit(restore->alloced_page, restore->first_dma,
|
||||
alloc, nr);
|
||||
if (restore->page_caching == alloc->tt_caching || PageHighMem(restore->alloced_page))
|
||||
alloc->caching_divide = alloc->pages;
|
||||
restore->snapshot_alloc = *alloc;
|
||||
restore->alloced_pages += nr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If restoring, save information needed for ttm_pool_restore_commit(). */
|
||||
static void
|
||||
ttm_pool_page_allocated_restore(struct ttm_pool *pool, unsigned int order,
|
||||
struct page *p,
|
||||
enum ttm_caching page_caching,
|
||||
dma_addr_t first_dma,
|
||||
struct ttm_pool_tt_restore *restore,
|
||||
const struct ttm_pool_alloc_state *alloc)
|
||||
{
|
||||
restore->pool = pool;
|
||||
restore->order = order;
|
||||
restore->restored_pages = 0;
|
||||
restore->page_caching = page_caching;
|
||||
restore->first_dma = first_dma;
|
||||
restore->alloced_page = p;
|
||||
restore->snapshot_alloc = *alloc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when we got a page, either from a pool or newly allocated.
|
||||
* if needed, dma map the page and populate the dma address array.
|
||||
@@ -380,10 +603,11 @@ static unsigned int ttm_pool_page_order(struct ttm_pool *pool, struct page *p)
|
||||
*/
|
||||
static int ttm_pool_page_allocated(struct ttm_pool *pool, unsigned int order,
|
||||
struct page *p, enum ttm_caching page_caching,
|
||||
struct ttm_pool_alloc_state *alloc)
|
||||
struct ttm_pool_alloc_state *alloc,
|
||||
struct ttm_pool_tt_restore *restore)
|
||||
{
|
||||
pgoff_t i, nr = 1UL << order;
|
||||
bool caching_consistent;
|
||||
dma_addr_t first_dma;
|
||||
int r = 0;
|
||||
|
||||
caching_consistent = (page_caching == alloc->tt_caching) || PageHighMem(p);
|
||||
@@ -395,17 +619,20 @@ static int ttm_pool_page_allocated(struct ttm_pool *pool, unsigned int order,
|
||||
}
|
||||
|
||||
if (alloc->dma_addr) {
|
||||
r = ttm_pool_map(pool, order, p, &alloc->dma_addr);
|
||||
r = ttm_pool_map(pool, order, p, &first_dma);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
alloc->remaining_pages -= nr;
|
||||
for (i = 0; i < nr; ++i)
|
||||
*alloc->pages++ = p++;
|
||||
if (restore) {
|
||||
ttm_pool_page_allocated_restore(pool, order, p, page_caching,
|
||||
first_dma, restore, alloc);
|
||||
} else {
|
||||
ttm_pool_allocated_page_commit(p, first_dma, alloc, 1UL << order);
|
||||
|
||||
if (caching_consistent)
|
||||
alloc->caching_divide = alloc->pages;
|
||||
if (caching_consistent)
|
||||
alloc->caching_divide = alloc->pages;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -428,22 +655,24 @@ static void ttm_pool_free_range(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
pgoff_t start_page, pgoff_t end_page)
|
||||
{
|
||||
struct page **pages = &tt->pages[start_page];
|
||||
unsigned int order;
|
||||
struct ttm_backup *backup = tt->backup;
|
||||
pgoff_t i, nr;
|
||||
|
||||
for (i = start_page; i < end_page; i += nr, pages += nr) {
|
||||
struct ttm_pool_type *pt = NULL;
|
||||
struct page *p = *pages;
|
||||
|
||||
order = ttm_pool_page_order(pool, *pages);
|
||||
nr = (1UL << order);
|
||||
if (tt->dma_address)
|
||||
ttm_pool_unmap(pool, tt->dma_address[i], nr);
|
||||
nr = 1;
|
||||
if (ttm_backup_page_ptr_is_handle(p)) {
|
||||
unsigned long handle = ttm_backup_page_ptr_to_handle(p);
|
||||
|
||||
pt = ttm_pool_select_type(pool, caching, order);
|
||||
if (pt)
|
||||
ttm_pool_type_give(pt, *pages);
|
||||
else
|
||||
ttm_pool_free_page(pool, caching, order, *pages);
|
||||
if (handle != 0)
|
||||
ttm_backup_drop(backup, handle);
|
||||
} else if (p) {
|
||||
dma_addr_t *dma_addr = tt->dma_address ?
|
||||
tt->dma_address + i : NULL;
|
||||
|
||||
nr = ttm_pool_unmap_and_free(pool, p, dma_addr, caching);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,22 +696,11 @@ static unsigned int ttm_pool_alloc_find_order(unsigned int highest,
|
||||
return min_t(unsigned int, highest, __fls(alloc->remaining_pages));
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_pool_alloc - Fill a ttm_tt object
|
||||
*
|
||||
* @pool: ttm_pool to use
|
||||
* @tt: ttm_tt object to fill
|
||||
* @ctx: operation context
|
||||
*
|
||||
* Fill the ttm_tt object with pages and also make sure to DMA map them when
|
||||
* necessary.
|
||||
*
|
||||
* Returns: 0 on successe, negative error code otherwise.
|
||||
*/
|
||||
int ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
struct ttm_operation_ctx *ctx)
|
||||
static int __ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
const struct ttm_operation_ctx *ctx,
|
||||
struct ttm_pool_alloc_state *alloc,
|
||||
struct ttm_pool_tt_restore *restore)
|
||||
{
|
||||
struct ttm_pool_alloc_state alloc;
|
||||
enum ttm_caching page_caching;
|
||||
gfp_t gfp_flags = GFP_USER;
|
||||
pgoff_t caching_divide;
|
||||
@@ -491,10 +709,8 @@ int ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
struct page *p;
|
||||
int r;
|
||||
|
||||
ttm_pool_alloc_state_init(tt, &alloc);
|
||||
|
||||
WARN_ON(!alloc.remaining_pages || ttm_tt_is_populated(tt));
|
||||
WARN_ON(alloc.dma_addr && !pool->dev);
|
||||
WARN_ON(!alloc->remaining_pages || ttm_tt_is_populated(tt));
|
||||
WARN_ON(alloc->dma_addr && !pool->dev);
|
||||
|
||||
if (tt->page_flags & TTM_TT_FLAG_ZERO_ALLOC)
|
||||
gfp_flags |= __GFP_ZERO;
|
||||
@@ -509,9 +725,9 @@ int ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
|
||||
page_caching = tt->caching;
|
||||
allow_pools = true;
|
||||
for (order = ttm_pool_alloc_find_order(MAX_PAGE_ORDER, &alloc);
|
||||
alloc.remaining_pages;
|
||||
order = ttm_pool_alloc_find_order(order, &alloc)) {
|
||||
for (order = ttm_pool_alloc_find_order(MAX_PAGE_ORDER, alloc);
|
||||
alloc->remaining_pages;
|
||||
order = ttm_pool_alloc_find_order(order, alloc)) {
|
||||
struct ttm_pool_type *pt;
|
||||
|
||||
/* First, try to allocate a page from a pool if one exists. */
|
||||
@@ -541,30 +757,120 @@ int ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
r = -ENOMEM;
|
||||
goto error_free_all;
|
||||
}
|
||||
r = ttm_pool_page_allocated(pool, order, p, page_caching, &alloc);
|
||||
r = ttm_pool_page_allocated(pool, order, p, page_caching, alloc,
|
||||
restore);
|
||||
if (r)
|
||||
goto error_free_page;
|
||||
|
||||
if (ttm_pool_restore_valid(restore)) {
|
||||
r = ttm_pool_restore_commit(restore, tt->backup, ctx, alloc);
|
||||
if (r)
|
||||
goto error_free_all;
|
||||
}
|
||||
}
|
||||
|
||||
r = ttm_pool_apply_caching(&alloc);
|
||||
r = ttm_pool_apply_caching(alloc);
|
||||
if (r)
|
||||
goto error_free_all;
|
||||
|
||||
kfree(tt->restore);
|
||||
tt->restore = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_page:
|
||||
ttm_pool_free_page(pool, page_caching, order, p);
|
||||
|
||||
error_free_all:
|
||||
caching_divide = alloc.caching_divide - tt->pages;
|
||||
if (tt->restore)
|
||||
return r;
|
||||
|
||||
caching_divide = alloc->caching_divide - tt->pages;
|
||||
ttm_pool_free_range(pool, tt, tt->caching, 0, caching_divide);
|
||||
ttm_pool_free_range(pool, tt, ttm_cached, caching_divide,
|
||||
tt->num_pages - alloc.remaining_pages);
|
||||
tt->num_pages - alloc->remaining_pages);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_pool_alloc - Fill a ttm_tt object
|
||||
*
|
||||
* @pool: ttm_pool to use
|
||||
* @tt: ttm_tt object to fill
|
||||
* @ctx: operation context
|
||||
*
|
||||
* Fill the ttm_tt object with pages and also make sure to DMA map them when
|
||||
* necessary.
|
||||
*
|
||||
* Returns: 0 on successe, negative error code otherwise.
|
||||
*/
|
||||
int ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
struct ttm_operation_ctx *ctx)
|
||||
{
|
||||
struct ttm_pool_alloc_state alloc;
|
||||
|
||||
if (WARN_ON(ttm_tt_is_backed_up(tt)))
|
||||
return -EINVAL;
|
||||
|
||||
ttm_pool_alloc_state_init(tt, &alloc);
|
||||
|
||||
return __ttm_pool_alloc(pool, tt, ctx, &alloc, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_pool_alloc);
|
||||
|
||||
/**
|
||||
* ttm_pool_restore_and_alloc - Fill a ttm_tt, restoring previously backed-up
|
||||
* content.
|
||||
*
|
||||
* @pool: ttm_pool to use
|
||||
* @tt: ttm_tt object to fill
|
||||
* @ctx: operation context
|
||||
*
|
||||
* Fill the ttm_tt object with pages and also make sure to DMA map them when
|
||||
* necessary. Read in backed-up content.
|
||||
*
|
||||
* Returns: 0 on successe, negative error code otherwise.
|
||||
*/
|
||||
int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
const struct ttm_operation_ctx *ctx)
|
||||
{
|
||||
struct ttm_pool_alloc_state alloc;
|
||||
|
||||
if (WARN_ON(!ttm_tt_is_backed_up(tt)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!tt->restore) {
|
||||
gfp_t gfp = GFP_KERNEL | __GFP_NOWARN;
|
||||
|
||||
ttm_pool_alloc_state_init(tt, &alloc);
|
||||
if (ctx->gfp_retry_mayfail)
|
||||
gfp |= __GFP_RETRY_MAYFAIL;
|
||||
|
||||
tt->restore = kzalloc(sizeof(*tt->restore), gfp);
|
||||
if (!tt->restore)
|
||||
return -ENOMEM;
|
||||
|
||||
tt->restore->snapshot_alloc = alloc;
|
||||
tt->restore->pool = pool;
|
||||
tt->restore->restored_pages = 1;
|
||||
} else {
|
||||
struct ttm_pool_tt_restore *restore = tt->restore;
|
||||
int ret;
|
||||
|
||||
alloc = restore->snapshot_alloc;
|
||||
if (ttm_pool_restore_valid(tt->restore)) {
|
||||
ret = ttm_pool_restore_commit(restore, tt->backup, ctx, &alloc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (!alloc.remaining_pages)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return __ttm_pool_alloc(pool, tt, ctx, &alloc, tt->restore);
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_pool_free - Free the backing pages from a ttm_tt object
|
||||
*
|
||||
@@ -582,6 +888,169 @@ void ttm_pool_free(struct ttm_pool *pool, struct ttm_tt *tt)
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_pool_free);
|
||||
|
||||
/**
|
||||
* ttm_pool_drop_backed_up() - Release content of a swapped-out struct ttm_tt
|
||||
* @tt: The struct ttm_tt.
|
||||
*
|
||||
* Release handles with associated content or any remaining pages of
|
||||
* a backed-up struct ttm_tt.
|
||||
*/
|
||||
void ttm_pool_drop_backed_up(struct ttm_tt *tt)
|
||||
{
|
||||
struct ttm_pool_tt_restore *restore;
|
||||
pgoff_t start_page = 0;
|
||||
|
||||
WARN_ON(!ttm_tt_is_backed_up(tt));
|
||||
|
||||
restore = tt->restore;
|
||||
|
||||
/*
|
||||
* Unmap and free any uncommitted restore page.
|
||||
* any tt page-array backup entries already read back has
|
||||
* been cleared already
|
||||
*/
|
||||
if (ttm_pool_restore_valid(restore)) {
|
||||
dma_addr_t *dma_addr = tt->dma_address ? &restore->first_dma : NULL;
|
||||
|
||||
ttm_pool_unmap_and_free(restore->pool, restore->alloced_page,
|
||||
dma_addr, restore->page_caching);
|
||||
restore->restored_pages = 1UL << restore->order;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a restore is ongoing, part of the tt pages may have a
|
||||
* caching different than writeback.
|
||||
*/
|
||||
if (restore) {
|
||||
pgoff_t mid = restore->snapshot_alloc.caching_divide - tt->pages;
|
||||
|
||||
start_page = restore->alloced_pages;
|
||||
WARN_ON(mid > start_page);
|
||||
/* Pages that might be dma-mapped and non-cached */
|
||||
ttm_pool_free_range(restore->pool, tt, tt->caching,
|
||||
0, mid);
|
||||
/* Pages that might be dma-mapped but cached */
|
||||
ttm_pool_free_range(restore->pool, tt, ttm_cached,
|
||||
mid, restore->alloced_pages);
|
||||
kfree(restore);
|
||||
tt->restore = NULL;
|
||||
}
|
||||
|
||||
ttm_pool_free_range(NULL, tt, ttm_cached, start_page, tt->num_pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_pool_backup() - Back up or purge a struct ttm_tt
|
||||
* @pool: The pool used when allocating the struct ttm_tt.
|
||||
* @tt: The struct ttm_tt.
|
||||
* @flags: Flags to govern the backup behaviour.
|
||||
*
|
||||
* Back up or purge a struct ttm_tt. If @purge is true, then
|
||||
* all pages will be freed directly to the system rather than to the pool
|
||||
* they were allocated from, making the function behave similarly to
|
||||
* ttm_pool_free(). If @purge is false the pages will be backed up instead,
|
||||
* exchanged for handles.
|
||||
* A subsequent call to ttm_pool_restore_and_alloc() will then read back the content and
|
||||
* a subsequent call to ttm_pool_drop_backed_up() will drop it.
|
||||
* If backup of a page fails for whatever reason, @ttm will still be
|
||||
* partially backed up, retaining those pages for which backup fails.
|
||||
* In that case, this function can be retried, possibly after freeing up
|
||||
* memory resources.
|
||||
*
|
||||
* Return: Number of pages actually backed up or freed, or negative
|
||||
* error code on error.
|
||||
*/
|
||||
long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
const struct ttm_backup_flags *flags)
|
||||
{
|
||||
struct ttm_backup *backup = tt->backup;
|
||||
struct page *page;
|
||||
unsigned long handle;
|
||||
gfp_t alloc_gfp;
|
||||
gfp_t gfp;
|
||||
int ret = 0;
|
||||
pgoff_t shrunken = 0;
|
||||
pgoff_t i, num_pages;
|
||||
|
||||
if (WARN_ON(ttm_tt_is_backed_up(tt)))
|
||||
return -EINVAL;
|
||||
|
||||
if ((!ttm_backup_bytes_avail() && !flags->purge) ||
|
||||
pool->use_dma_alloc || ttm_tt_is_backed_up(tt))
|
||||
return -EBUSY;
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
/* Anything returned to the system needs to be cached. */
|
||||
if (tt->caching != ttm_cached)
|
||||
set_pages_array_wb(tt->pages, tt->num_pages);
|
||||
#endif
|
||||
|
||||
if (tt->dma_address || flags->purge) {
|
||||
for (i = 0; i < tt->num_pages; i += num_pages) {
|
||||
unsigned int order;
|
||||
|
||||
page = tt->pages[i];
|
||||
if (unlikely(!page)) {
|
||||
num_pages = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
order = ttm_pool_page_order(pool, page);
|
||||
num_pages = 1UL << order;
|
||||
if (tt->dma_address)
|
||||
ttm_pool_unmap(pool, tt->dma_address[i],
|
||||
num_pages);
|
||||
if (flags->purge) {
|
||||
shrunken += num_pages;
|
||||
page->private = 0;
|
||||
__free_pages(page, order);
|
||||
memset(tt->pages + i, 0,
|
||||
num_pages * sizeof(*tt->pages));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags->purge)
|
||||
return shrunken;
|
||||
|
||||
if (pool->use_dma32)
|
||||
gfp = GFP_DMA32;
|
||||
else
|
||||
gfp = GFP_HIGHUSER;
|
||||
|
||||
alloc_gfp = GFP_KERNEL | __GFP_HIGH | __GFP_NOWARN | __GFP_RETRY_MAYFAIL;
|
||||
|
||||
num_pages = tt->num_pages;
|
||||
|
||||
/* Pretend doing fault injection by shrinking only half of the pages. */
|
||||
if (IS_ENABLED(CONFIG_FAULT_INJECTION) && should_fail(&backup_fault_inject, 1))
|
||||
num_pages = DIV_ROUND_UP(num_pages, 2);
|
||||
|
||||
for (i = 0; i < num_pages; ++i) {
|
||||
s64 shandle;
|
||||
|
||||
page = tt->pages[i];
|
||||
if (unlikely(!page))
|
||||
continue;
|
||||
|
||||
ttm_pool_split_for_swap(pool, page);
|
||||
|
||||
shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,
|
||||
gfp, alloc_gfp);
|
||||
if (shandle < 0) {
|
||||
/* We allow partially shrunken tts */
|
||||
ret = shandle;
|
||||
break;
|
||||
}
|
||||
handle = shandle;
|
||||
tt->pages[i] = ttm_backup_handle_to_page_ptr(handle);
|
||||
put_page(page);
|
||||
shrunken++;
|
||||
}
|
||||
|
||||
return shrunken ? shrunken : ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_pool_init - Initialize a pool
|
||||
*
|
||||
@@ -843,6 +1312,10 @@ int ttm_pool_mgr_init(unsigned long num_pages)
|
||||
&ttm_pool_debugfs_globals_fops);
|
||||
debugfs_create_file("page_pool_shrink", 0400, ttm_debugfs_root, NULL,
|
||||
&ttm_pool_debugfs_shrink_fops);
|
||||
#ifdef CONFIG_FAULT_INJECTION
|
||||
fault_create_debugfs_attr("backup_fault_inject", ttm_debugfs_root,
|
||||
&backup_fault_inject);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mm_shrinker = shrinker_alloc(0, "drm-ttm_pool");
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <drm/drm_cache.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_util.h>
|
||||
#include <drm/ttm/ttm_backup.h>
|
||||
#include <drm/ttm/ttm_bo.h>
|
||||
#include <drm/ttm/ttm_tt.h>
|
||||
|
||||
@@ -158,6 +159,8 @@ static void ttm_tt_init_fields(struct ttm_tt *ttm,
|
||||
ttm->swap_storage = NULL;
|
||||
ttm->sg = bo->sg;
|
||||
ttm->caching = caching;
|
||||
ttm->restore = NULL;
|
||||
ttm->backup = NULL;
|
||||
}
|
||||
|
||||
int ttm_tt_init(struct ttm_tt *ttm, struct ttm_buffer_object *bo,
|
||||
@@ -182,6 +185,13 @@ void ttm_tt_fini(struct ttm_tt *ttm)
|
||||
fput(ttm->swap_storage);
|
||||
ttm->swap_storage = NULL;
|
||||
|
||||
if (ttm_tt_is_backed_up(ttm))
|
||||
ttm_pool_drop_backed_up(ttm);
|
||||
if (ttm->backup) {
|
||||
ttm_backup_fini(ttm->backup);
|
||||
ttm->backup = NULL;
|
||||
}
|
||||
|
||||
if (ttm->pages)
|
||||
kvfree(ttm->pages);
|
||||
else
|
||||
@@ -253,6 +263,49 @@ out_err:
|
||||
}
|
||||
EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_tt_swapin);
|
||||
|
||||
/**
|
||||
* ttm_tt_backup() - Helper to back up a struct ttm_tt.
|
||||
* @bdev: The TTM device.
|
||||
* @tt: The struct ttm_tt.
|
||||
* @flags: Flags that govern the backup behaviour.
|
||||
*
|
||||
* Update the page accounting and call ttm_pool_shrink_tt to free pages
|
||||
* or back them up.
|
||||
*
|
||||
* Return: Number of pages freed or swapped out, or negative error code on
|
||||
* error.
|
||||
*/
|
||||
long ttm_tt_backup(struct ttm_device *bdev, struct ttm_tt *tt,
|
||||
const struct ttm_backup_flags flags)
|
||||
{
|
||||
long ret;
|
||||
|
||||
if (WARN_ON(IS_ERR_OR_NULL(tt->backup)))
|
||||
return 0;
|
||||
|
||||
ret = ttm_pool_backup(&bdev->pool, tt, &flags);
|
||||
if (ret > 0) {
|
||||
tt->page_flags &= ~TTM_TT_FLAG_PRIV_POPULATED;
|
||||
tt->page_flags |= TTM_TT_FLAG_BACKED_UP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ttm_tt_restore(struct ttm_device *bdev, struct ttm_tt *tt,
|
||||
const struct ttm_operation_ctx *ctx)
|
||||
{
|
||||
int ret = ttm_pool_restore_and_alloc(&bdev->pool, tt, ctx);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tt->page_flags &= ~TTM_TT_FLAG_BACKED_UP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_tt_restore);
|
||||
|
||||
/**
|
||||
* ttm_tt_swapout - swap out tt object
|
||||
*
|
||||
@@ -348,6 +401,7 @@ int ttm_tt_populate(struct ttm_device *bdev,
|
||||
goto error;
|
||||
|
||||
ttm->page_flags |= TTM_TT_FLAG_PRIV_POPULATED;
|
||||
ttm->page_flags &= ~TTM_TT_FLAG_BACKED_UP;
|
||||
if (unlikely(ttm->page_flags & TTM_TT_FLAG_SWAPPED)) {
|
||||
ret = ttm_tt_swapin(ttm);
|
||||
if (unlikely(ret != 0)) {
|
||||
@@ -477,3 +531,32 @@ unsigned long ttm_tt_pages_limit(void)
|
||||
return ttm_pages_limit;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_tt_pages_limit);
|
||||
|
||||
/**
|
||||
* ttm_tt_setup_backup() - Allocate and assign a backup structure for a ttm_tt
|
||||
* @tt: The ttm_tt for wich to allocate and assign a backup structure.
|
||||
*
|
||||
* Assign a backup structure to be used for tt backup. This should
|
||||
* typically be done at bo creation, to avoid allocations at shrinking
|
||||
* time.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int ttm_tt_setup_backup(struct ttm_tt *tt)
|
||||
{
|
||||
struct ttm_backup *backup =
|
||||
ttm_backup_shmem_create(((loff_t)tt->num_pages) << PAGE_SHIFT);
|
||||
|
||||
if (WARN_ON_ONCE(!(tt->page_flags & TTM_TT_FLAG_EXTERNAL_MAPPABLE)))
|
||||
return -EINVAL;
|
||||
|
||||
if (IS_ERR(backup))
|
||||
return PTR_ERR(backup);
|
||||
|
||||
if (tt->backup)
|
||||
ttm_backup_fini(tt->backup);
|
||||
|
||||
tt->backup = backup;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ttm_tt_setup_backup);
|
||||
|
||||
@@ -181,40 +181,3 @@ int hgsmi_update_pointer_shape(struct gen_pool *ctx, u32 flags,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* hgsmi_cursor_position - Report the guest cursor position. The host may
|
||||
* wish to use this information to re-position its
|
||||
* own cursor (though this is currently unlikely).
|
||||
* The current host cursor position is returned.
|
||||
* Return: 0 or negative errno value.
|
||||
* @ctx: The context containing the heap used.
|
||||
* @report_position: Are we reporting a position?
|
||||
* @x: Guest cursor X position.
|
||||
* @y: Guest cursor Y position.
|
||||
* @x_host: Host cursor X position is stored here. Optional.
|
||||
* @y_host: Host cursor Y position is stored here. Optional.
|
||||
*/
|
||||
int hgsmi_cursor_position(struct gen_pool *ctx, bool report_position,
|
||||
u32 x, u32 y, u32 *x_host, u32 *y_host)
|
||||
{
|
||||
struct vbva_cursor_position *p;
|
||||
|
||||
p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA,
|
||||
VBVA_CURSOR_POSITION);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->report_position = report_position;
|
||||
p->x = x;
|
||||
p->y = y;
|
||||
|
||||
hgsmi_buffer_submit(ctx, p);
|
||||
|
||||
*x_host = p->x;
|
||||
*y_host = p->y;
|
||||
|
||||
hgsmi_buffer_free(ctx, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ int hgsmi_query_conf(struct gen_pool *ctx, u32 index, u32 *value_ret);
|
||||
int hgsmi_update_pointer_shape(struct gen_pool *ctx, u32 flags,
|
||||
u32 hot_x, u32 hot_y, u32 width, u32 height,
|
||||
u8 *pixels, u32 len);
|
||||
int hgsmi_cursor_position(struct gen_pool *ctx, bool report_position,
|
||||
u32 x, u32 y, u32 *x_host, u32 *y_host);
|
||||
|
||||
bool vbva_enable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx,
|
||||
struct vbva_buffer *vbva, s32 screen);
|
||||
|
||||
@@ -724,7 +724,7 @@ static void drm_vc4_test_pv_muxing_invalid(struct kunit *test)
|
||||
static int vc4_pv_muxing_test_init(struct kunit *test)
|
||||
{
|
||||
const struct pv_muxing_param *params = test->param_value;
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct pv_muxing_priv *priv;
|
||||
struct drm_device *drm;
|
||||
struct vc4_dev *vc4;
|
||||
@@ -737,13 +737,15 @@ static int vc4_pv_muxing_test_init(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
priv->vc4 = vc4;
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
drm = &vc4->base;
|
||||
priv->state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
priv->state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->state);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -782,7 +784,7 @@ static struct kunit_suite vc5_pv_muxing_test_suite = {
|
||||
*/
|
||||
static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct vc4_crtc_state *new_vc4_crtc_state;
|
||||
struct vc4_hvs_state *new_hvs_state;
|
||||
@@ -795,11 +797,10 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes
|
||||
vc4 = vc5_mock_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
drm = &vc4->base;
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
@@ -822,7 +823,7 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes
|
||||
ret = drm_atomic_helper_swap_state(state, false);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
|
||||
@@ -843,6 +844,9 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes
|
||||
KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use);
|
||||
|
||||
KUNIT_EXPECT_NE(test, hdmi0_channel, hdmi1_channel);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -854,7 +858,7 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes
|
||||
*/
|
||||
static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct vc4_crtc_state *new_vc4_crtc_state;
|
||||
struct vc4_hvs_state *new_hvs_state;
|
||||
@@ -867,11 +871,10 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
vc4 = vc5_mock_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
drm = &vc4->base;
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
@@ -905,7 +908,7 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
ret = drm_atomic_helper_swap_state(state, false);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
ret = vc4_mock_atomic_del_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
@@ -929,6 +932,9 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_EQ(test, old_hdmi1_channel, hdmi1_channel);
|
||||
}
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -949,7 +955,7 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
|
||||
static void
|
||||
drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct kunit *test)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *ctx;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct vc4_crtc_state *new_vc4_crtc_state;
|
||||
struct drm_device *drm;
|
||||
@@ -959,11 +965,10 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku
|
||||
vc4 = vc5_mock_device(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
|
||||
|
||||
ctx = drm_kunit_helper_acquire_ctx_alloc(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
drm_modeset_acquire_init(&ctx, 0);
|
||||
|
||||
drm = &vc4->base;
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
|
||||
@@ -975,7 +980,7 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku
|
||||
ret = drm_atomic_helper_swap_state(state, false);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx);
|
||||
state = drm_kunit_helper_atomic_state_alloc(test, drm, &ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
|
||||
@@ -987,6 +992,9 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku
|
||||
new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
|
||||
VC4_ENCODER_TYPE_HDMI0);
|
||||
KUNIT_EXPECT_NULL(test, new_vc4_crtc_state);
|
||||
|
||||
drm_modeset_drop_locks(&ctx);
|
||||
drm_modeset_acquire_fini(&ctx);
|
||||
}
|
||||
|
||||
static struct kunit_case vc5_pv_muxing_bugs_tests[] = {
|
||||
|
||||
@@ -2928,8 +2928,8 @@ static int vc5_hdmi_init_resources(struct drm_device *drm,
|
||||
|
||||
vc4_hdmi->hdmicore_regs = devm_platform_ioremap_resource_byname(pdev,
|
||||
"hdmi");
|
||||
if (!vc4_hdmi->hdmicore_regs)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(vc4_hdmi->hdmicore_regs))
|
||||
return PTR_ERR(vc4_hdmi->hdmicore_regs);
|
||||
|
||||
/* This is shared between both HDMI controllers. Cannot
|
||||
* claim for both instances. Lets not convert to using
|
||||
@@ -2946,33 +2946,33 @@ static int vc5_hdmi_init_resources(struct drm_device *drm,
|
||||
|
||||
vc4_hdmi->cec_regs = devm_platform_ioremap_resource_byname(pdev,
|
||||
"cec");
|
||||
if (!vc4_hdmi->cec_regs)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(vc4_hdmi->cec_regs))
|
||||
return PTR_ERR(vc4_hdmi->cec_regs);
|
||||
|
||||
vc4_hdmi->csc_regs = devm_platform_ioremap_resource_byname(pdev,
|
||||
"csc");
|
||||
if (!vc4_hdmi->csc_regs)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(vc4_hdmi->csc_regs))
|
||||
return PTR_ERR(vc4_hdmi->csc_regs);
|
||||
|
||||
vc4_hdmi->dvp_regs = devm_platform_ioremap_resource_byname(pdev,
|
||||
"dvp");
|
||||
if (!vc4_hdmi->dvp_regs)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(vc4_hdmi->dvp_regs))
|
||||
return PTR_ERR(vc4_hdmi->dvp_regs);
|
||||
|
||||
vc4_hdmi->phy_regs = devm_platform_ioremap_resource_byname(pdev,
|
||||
"phy");
|
||||
|
||||
if (!vc4_hdmi->phy_regs)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(vc4_hdmi->phy_regs))
|
||||
return PTR_ERR(vc4_hdmi->phy_regs);
|
||||
|
||||
vc4_hdmi->ram_regs = devm_platform_ioremap_resource_byname(pdev,
|
||||
"packet");
|
||||
if (!vc4_hdmi->ram_regs)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(vc4_hdmi->ram_regs))
|
||||
return PTR_ERR(vc4_hdmi->ram_regs);
|
||||
|
||||
vc4_hdmi->rm_regs = devm_platform_ioremap_resource_byname(pdev, "rm");
|
||||
if (!vc4_hdmi->rm_regs)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(vc4_hdmi->rm_regs))
|
||||
return PTR_ERR(vc4_hdmi->rm_regs);
|
||||
|
||||
vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi");
|
||||
if (IS_ERR(vc4_hdmi->hsm_clock)) {
|
||||
|
||||
@@ -98,6 +98,7 @@ xe-y += xe_bb.o \
|
||||
xe_rtp.o \
|
||||
xe_sa.o \
|
||||
xe_sched_job.o \
|
||||
xe_shrinker.o \
|
||||
xe_step.o \
|
||||
xe_survivability_mode.o \
|
||||
xe_sync.o \
|
||||
|
||||
@@ -514,8 +514,13 @@ static int shrink_test_run_device(struct xe_device *xe)
|
||||
* other way around, they may not be subject to swapping...
|
||||
*/
|
||||
if (alloced < purgeable) {
|
||||
xe_ttm_tt_account_subtract(&xe_tt->ttm);
|
||||
xe_tt->purgeable = true;
|
||||
xe_ttm_tt_account_add(&xe_tt->ttm);
|
||||
bo->ttm.priority = 0;
|
||||
spin_lock(&bo->ttm.bdev->lru_lock);
|
||||
ttm_bo_move_to_lru_tail(&bo->ttm);
|
||||
spin_unlock(&bo->ttm.bdev->lru_lock);
|
||||
} else {
|
||||
int ret = shrink_test_fill_random(bo, &prng, link);
|
||||
|
||||
@@ -570,7 +575,6 @@ static int shrink_test_run_device(struct xe_device *xe)
|
||||
if (ret == -EINTR)
|
||||
intr = true;
|
||||
} while (ret == -EINTR && !signal_pending(current));
|
||||
|
||||
if (!ret && !purgeable)
|
||||
failed = shrink_test_verify(test, bo, count, &prng, link);
|
||||
|
||||
|
||||
+185
-17
@@ -11,6 +11,7 @@
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_gem_ttm_helper.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/ttm/ttm_backup.h>
|
||||
#include <drm/ttm/ttm_device.h>
|
||||
#include <drm/ttm/ttm_placement.h>
|
||||
#include <drm/ttm/ttm_tt.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include "xe_preempt_fence.h"
|
||||
#include "xe_pxp.h"
|
||||
#include "xe_res_cursor.h"
|
||||
#include "xe_shrinker.h"
|
||||
#include "xe_trace_bo.h"
|
||||
#include "xe_ttm_stolen_mgr.h"
|
||||
#include "xe_vm.h"
|
||||
@@ -307,9 +309,11 @@ static void xe_evict_flags(struct ttm_buffer_object *tbo,
|
||||
}
|
||||
}
|
||||
|
||||
/* struct xe_ttm_tt - Subclassed ttm_tt for xe */
|
||||
struct xe_ttm_tt {
|
||||
struct ttm_tt ttm;
|
||||
struct device *dev;
|
||||
/** @xe - The xe device */
|
||||
struct xe_device *xe;
|
||||
struct sg_table sgt;
|
||||
struct sg_table *sg;
|
||||
/** @purgeable: Whether the content of the pages of @ttm is purgeable. */
|
||||
@@ -322,7 +326,8 @@ static int xe_tt_map_sg(struct ttm_tt *tt)
|
||||
unsigned long num_pages = tt->num_pages;
|
||||
int ret;
|
||||
|
||||
XE_WARN_ON(tt->page_flags & TTM_TT_FLAG_EXTERNAL);
|
||||
XE_WARN_ON((tt->page_flags & TTM_TT_FLAG_EXTERNAL) &&
|
||||
!(tt->page_flags & TTM_TT_FLAG_EXTERNAL_MAPPABLE));
|
||||
|
||||
if (xe_tt->sg)
|
||||
return 0;
|
||||
@@ -330,13 +335,13 @@ static int xe_tt_map_sg(struct ttm_tt *tt)
|
||||
ret = sg_alloc_table_from_pages_segment(&xe_tt->sgt, tt->pages,
|
||||
num_pages, 0,
|
||||
(u64)num_pages << PAGE_SHIFT,
|
||||
xe_sg_segment_size(xe_tt->dev),
|
||||
xe_sg_segment_size(xe_tt->xe->drm.dev),
|
||||
GFP_KERNEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xe_tt->sg = &xe_tt->sgt;
|
||||
ret = dma_map_sgtable(xe_tt->dev, xe_tt->sg, DMA_BIDIRECTIONAL,
|
||||
ret = dma_map_sgtable(xe_tt->xe->drm.dev, xe_tt->sg, DMA_BIDIRECTIONAL,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
if (ret) {
|
||||
sg_free_table(xe_tt->sg);
|
||||
@@ -352,7 +357,7 @@ static void xe_tt_unmap_sg(struct ttm_tt *tt)
|
||||
struct xe_ttm_tt *xe_tt = container_of(tt, struct xe_ttm_tt, ttm);
|
||||
|
||||
if (xe_tt->sg) {
|
||||
dma_unmap_sgtable(xe_tt->dev, xe_tt->sg,
|
||||
dma_unmap_sgtable(xe_tt->xe->drm.dev, xe_tt->sg,
|
||||
DMA_BIDIRECTIONAL, 0);
|
||||
sg_free_table(xe_tt->sg);
|
||||
xe_tt->sg = NULL;
|
||||
@@ -367,21 +372,47 @@ struct sg_table *xe_bo_sg(struct xe_bo *bo)
|
||||
return xe_tt->sg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Account ttm pages against the device shrinker's shrinkable and
|
||||
* purgeable counts.
|
||||
*/
|
||||
static void xe_ttm_tt_account_add(struct ttm_tt *tt)
|
||||
{
|
||||
struct xe_ttm_tt *xe_tt = container_of(tt, struct xe_ttm_tt, ttm);
|
||||
|
||||
if (xe_tt->purgeable)
|
||||
xe_shrinker_mod_pages(xe_tt->xe->mem.shrinker, 0, tt->num_pages);
|
||||
else
|
||||
xe_shrinker_mod_pages(xe_tt->xe->mem.shrinker, tt->num_pages, 0);
|
||||
}
|
||||
|
||||
static void xe_ttm_tt_account_subtract(struct ttm_tt *tt)
|
||||
{
|
||||
struct xe_ttm_tt *xe_tt = container_of(tt, struct xe_ttm_tt, ttm);
|
||||
|
||||
if (xe_tt->purgeable)
|
||||
xe_shrinker_mod_pages(xe_tt->xe->mem.shrinker, 0, -(long)tt->num_pages);
|
||||
else
|
||||
xe_shrinker_mod_pages(xe_tt->xe->mem.shrinker, -(long)tt->num_pages, 0);
|
||||
}
|
||||
|
||||
static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
|
||||
u32 page_flags)
|
||||
{
|
||||
struct xe_bo *bo = ttm_to_xe_bo(ttm_bo);
|
||||
struct xe_device *xe = xe_bo_device(bo);
|
||||
struct xe_ttm_tt *tt;
|
||||
struct xe_ttm_tt *xe_tt;
|
||||
struct ttm_tt *tt;
|
||||
unsigned long extra_pages;
|
||||
enum ttm_caching caching = ttm_cached;
|
||||
int err;
|
||||
|
||||
tt = kzalloc(sizeof(*tt), GFP_KERNEL);
|
||||
if (!tt)
|
||||
xe_tt = kzalloc(sizeof(*xe_tt), GFP_KERNEL);
|
||||
if (!xe_tt)
|
||||
return NULL;
|
||||
|
||||
tt->dev = xe->drm.dev;
|
||||
tt = &xe_tt->ttm;
|
||||
xe_tt->xe = xe;
|
||||
|
||||
extra_pages = 0;
|
||||
if (xe_bo_needs_ccs_pages(bo))
|
||||
@@ -427,42 +458,66 @@ static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
|
||||
caching = ttm_uncached;
|
||||
}
|
||||
|
||||
err = ttm_tt_init(&tt->ttm, &bo->ttm, page_flags, caching, extra_pages);
|
||||
if (ttm_bo->type != ttm_bo_type_sg)
|
||||
page_flags |= TTM_TT_FLAG_EXTERNAL | TTM_TT_FLAG_EXTERNAL_MAPPABLE;
|
||||
|
||||
err = ttm_tt_init(tt, &bo->ttm, page_flags, caching, extra_pages);
|
||||
if (err) {
|
||||
kfree(tt);
|
||||
kfree(xe_tt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &tt->ttm;
|
||||
if (ttm_bo->type != ttm_bo_type_sg) {
|
||||
err = ttm_tt_setup_backup(tt);
|
||||
if (err) {
|
||||
ttm_tt_fini(tt);
|
||||
kfree(xe_tt);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return tt;
|
||||
}
|
||||
|
||||
static int xe_ttm_tt_populate(struct ttm_device *ttm_dev, struct ttm_tt *tt,
|
||||
struct ttm_operation_ctx *ctx)
|
||||
{
|
||||
struct xe_ttm_tt *xe_tt = container_of(tt, struct xe_ttm_tt, ttm);
|
||||
int err;
|
||||
|
||||
/*
|
||||
* dma-bufs are not populated with pages, and the dma-
|
||||
* addresses are set up when moved to XE_PL_TT.
|
||||
*/
|
||||
if (tt->page_flags & TTM_TT_FLAG_EXTERNAL)
|
||||
if ((tt->page_flags & TTM_TT_FLAG_EXTERNAL) &&
|
||||
!(tt->page_flags & TTM_TT_FLAG_EXTERNAL_MAPPABLE))
|
||||
return 0;
|
||||
|
||||
err = ttm_pool_alloc(&ttm_dev->pool, tt, ctx);
|
||||
if (ttm_tt_is_backed_up(tt) && !xe_tt->purgeable) {
|
||||
err = ttm_tt_restore(ttm_dev, tt, ctx);
|
||||
} else {
|
||||
ttm_tt_clear_backed_up(tt);
|
||||
err = ttm_pool_alloc(&ttm_dev->pool, tt, ctx);
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
xe_tt->purgeable = false;
|
||||
xe_ttm_tt_account_add(tt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xe_ttm_tt_unpopulate(struct ttm_device *ttm_dev, struct ttm_tt *tt)
|
||||
{
|
||||
if (tt->page_flags & TTM_TT_FLAG_EXTERNAL)
|
||||
if ((tt->page_flags & TTM_TT_FLAG_EXTERNAL) &&
|
||||
!(tt->page_flags & TTM_TT_FLAG_EXTERNAL_MAPPABLE))
|
||||
return;
|
||||
|
||||
xe_tt_unmap_sg(tt);
|
||||
|
||||
return ttm_pool_free(&ttm_dev->pool, tt);
|
||||
ttm_pool_free(&ttm_dev->pool, tt);
|
||||
xe_ttm_tt_account_subtract(tt);
|
||||
}
|
||||
|
||||
static void xe_ttm_tt_destroy(struct ttm_device *ttm_dev, struct ttm_tt *tt)
|
||||
@@ -912,6 +967,111 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long xe_bo_shrink_purge(struct ttm_operation_ctx *ctx,
|
||||
struct ttm_buffer_object *bo,
|
||||
unsigned long *scanned)
|
||||
{
|
||||
long lret;
|
||||
|
||||
/* Fake move to system, without copying data. */
|
||||
if (bo->resource->mem_type != XE_PL_SYSTEM) {
|
||||
struct ttm_resource *new_resource;
|
||||
|
||||
lret = ttm_bo_wait_ctx(bo, ctx);
|
||||
if (lret)
|
||||
return lret;
|
||||
|
||||
lret = ttm_bo_mem_space(bo, &sys_placement, &new_resource, ctx);
|
||||
if (lret)
|
||||
return lret;
|
||||
|
||||
xe_tt_unmap_sg(bo->ttm);
|
||||
ttm_bo_move_null(bo, new_resource);
|
||||
}
|
||||
|
||||
*scanned += bo->ttm->num_pages;
|
||||
lret = ttm_bo_shrink(ctx, bo, (struct ttm_bo_shrink_flags)
|
||||
{.purge = true,
|
||||
.writeback = false,
|
||||
.allow_move = false});
|
||||
|
||||
if (lret > 0)
|
||||
xe_ttm_tt_account_subtract(bo->ttm);
|
||||
|
||||
return lret;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_shrink() - Try to shrink an xe bo.
|
||||
* @ctx: The struct ttm_operation_ctx used for shrinking.
|
||||
* @bo: The TTM buffer object whose pages to shrink.
|
||||
* @flags: Flags governing the shrink behaviour.
|
||||
* @scanned: Pointer to a counter of the number of pages
|
||||
* attempted to shrink.
|
||||
*
|
||||
* Try to shrink- or purge a bo, and if it succeeds, unmap dma.
|
||||
* Note that we need to be able to handle also non xe bos
|
||||
* (ghost bos), but only if the struct ttm_tt is embedded in
|
||||
* a struct xe_ttm_tt. When the function attempts to shrink
|
||||
* the pages of a buffer object, The value pointed to by @scanned
|
||||
* is updated.
|
||||
*
|
||||
* Return: The number of pages shrunken or purged, or negative error
|
||||
* code on failure.
|
||||
*/
|
||||
long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
|
||||
const struct xe_bo_shrink_flags flags,
|
||||
unsigned long *scanned)
|
||||
{
|
||||
struct ttm_tt *tt = bo->ttm;
|
||||
struct xe_ttm_tt *xe_tt = container_of(tt, struct xe_ttm_tt, ttm);
|
||||
struct ttm_place place = {.mem_type = bo->resource->mem_type};
|
||||
struct xe_bo *xe_bo = ttm_to_xe_bo(bo);
|
||||
struct xe_device *xe = xe_tt->xe;
|
||||
bool needs_rpm;
|
||||
long lret = 0L;
|
||||
|
||||
if (!(tt->page_flags & TTM_TT_FLAG_EXTERNAL_MAPPABLE) ||
|
||||
(flags.purge && !xe_tt->purgeable))
|
||||
return -EBUSY;
|
||||
|
||||
if (!ttm_bo_eviction_valuable(bo, &place))
|
||||
return -EBUSY;
|
||||
|
||||
if (!xe_bo_is_xe_bo(bo) || !xe_bo_get_unless_zero(xe_bo))
|
||||
return xe_bo_shrink_purge(ctx, bo, scanned);
|
||||
|
||||
if (xe_tt->purgeable) {
|
||||
if (bo->resource->mem_type != XE_PL_SYSTEM)
|
||||
lret = xe_bo_move_notify(xe_bo, ctx);
|
||||
if (!lret)
|
||||
lret = xe_bo_shrink_purge(ctx, bo, scanned);
|
||||
goto out_unref;
|
||||
}
|
||||
|
||||
/* System CCS needs gpu copy when moving PL_TT -> PL_SYSTEM */
|
||||
needs_rpm = (!IS_DGFX(xe) && bo->resource->mem_type != XE_PL_SYSTEM &&
|
||||
xe_bo_needs_ccs_pages(xe_bo));
|
||||
if (needs_rpm && !xe_pm_runtime_get_if_active(xe))
|
||||
goto out_unref;
|
||||
|
||||
*scanned += tt->num_pages;
|
||||
lret = ttm_bo_shrink(ctx, bo, (struct ttm_bo_shrink_flags)
|
||||
{.purge = false,
|
||||
.writeback = flags.writeback,
|
||||
.allow_move = true});
|
||||
if (needs_rpm)
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
if (lret > 0)
|
||||
xe_ttm_tt_account_subtract(tt);
|
||||
|
||||
out_unref:
|
||||
xe_bo_put(xe_bo);
|
||||
|
||||
return lret;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_evict_pinned() - Evict a pinned VRAM object to system memory
|
||||
* @bo: The buffer object to move.
|
||||
@@ -1929,6 +2089,8 @@ int xe_bo_pin_external(struct xe_bo *bo)
|
||||
}
|
||||
|
||||
ttm_bo_pin(&bo->ttm);
|
||||
if (bo->ttm.ttm && ttm_tt_is_populated(bo->ttm.ttm))
|
||||
xe_ttm_tt_account_subtract(bo->ttm.ttm);
|
||||
|
||||
/*
|
||||
* FIXME: If we always use the reserve / unreserve functions for locking
|
||||
@@ -1988,6 +2150,8 @@ int xe_bo_pin(struct xe_bo *bo)
|
||||
}
|
||||
|
||||
ttm_bo_pin(&bo->ttm);
|
||||
if (bo->ttm.ttm && ttm_tt_is_populated(bo->ttm.ttm))
|
||||
xe_ttm_tt_account_subtract(bo->ttm.ttm);
|
||||
|
||||
/*
|
||||
* FIXME: If we always use the reserve / unreserve functions for locking
|
||||
@@ -2022,6 +2186,8 @@ void xe_bo_unpin_external(struct xe_bo *bo)
|
||||
spin_unlock(&xe->pinned.lock);
|
||||
|
||||
ttm_bo_unpin(&bo->ttm);
|
||||
if (bo->ttm.ttm && ttm_tt_is_populated(bo->ttm.ttm))
|
||||
xe_ttm_tt_account_add(bo->ttm.ttm);
|
||||
|
||||
/*
|
||||
* FIXME: If we always use the reserve / unreserve functions for locking
|
||||
@@ -2045,6 +2211,8 @@ void xe_bo_unpin(struct xe_bo *bo)
|
||||
spin_unlock(&xe->pinned.lock);
|
||||
}
|
||||
ttm_bo_unpin(&bo->ttm);
|
||||
if (bo->ttm.ttm && ttm_tt_is_populated(bo->ttm.ttm))
|
||||
xe_ttm_tt_account_add(bo->ttm.ttm);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,6 +148,28 @@ static inline struct xe_bo *xe_bo_get(struct xe_bo *bo)
|
||||
|
||||
void xe_bo_put(struct xe_bo *bo);
|
||||
|
||||
/*
|
||||
* xe_bo_get_unless_zero() - Conditionally obtain a GEM object refcount on an
|
||||
* xe bo
|
||||
* @bo: The bo for which we want to obtain a refcount.
|
||||
*
|
||||
* There is a short window between where the bo's GEM object refcount reaches
|
||||
* zero and where we put the final ttm_bo reference. Code in the eviction- and
|
||||
* shrinking path should therefore attempt to grab a gem object reference before
|
||||
* trying to use members outside of the base class ttm object. This function is
|
||||
* intended for that purpose. On successful return, this function must be paired
|
||||
* with an xe_bo_put().
|
||||
*
|
||||
* Return: @bo on success, NULL on failure.
|
||||
*/
|
||||
static inline __must_check struct xe_bo *xe_bo_get_unless_zero(struct xe_bo *bo)
|
||||
{
|
||||
if (!bo || !kref_get_unless_zero(&bo->ttm.base.refcount))
|
||||
return NULL;
|
||||
|
||||
return bo;
|
||||
}
|
||||
|
||||
static inline void __xe_bo_unset_bulk_move(struct xe_bo *bo)
|
||||
{
|
||||
if (bo)
|
||||
@@ -349,6 +371,20 @@ static inline unsigned int xe_sg_segment_size(struct device *dev)
|
||||
return round_down(max / 2, PAGE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct xe_bo_shrink_flags - flags governing the shrink behaviour.
|
||||
* @purge: Only purging allowed. Don't shrink if bo not purgeable.
|
||||
* @writeback: Attempt to immediately move content to swap.
|
||||
*/
|
||||
struct xe_bo_shrink_flags {
|
||||
u32 purge : 1;
|
||||
u32 writeback : 1;
|
||||
};
|
||||
|
||||
long xe_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
|
||||
const struct xe_bo_shrink_flags flags,
|
||||
unsigned long *scanned);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
|
||||
/**
|
||||
* xe_bo_is_mem_type - Whether the bo currently resides in the given
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "xe_pmu.h"
|
||||
#include "xe_pxp.h"
|
||||
#include "xe_query.h"
|
||||
#include "xe_shrinker.h"
|
||||
#include "xe_sriov.h"
|
||||
#include "xe_survivability_mode.h"
|
||||
#include "xe_tile.h"
|
||||
@@ -403,6 +404,9 @@ static void xe_device_destroy(struct drm_device *dev, void *dummy)
|
||||
if (xe->unordered_wq)
|
||||
destroy_workqueue(xe->unordered_wq);
|
||||
|
||||
if (!IS_ERR_OR_NULL(xe->mem.shrinker))
|
||||
xe_shrinker_destroy(xe->mem.shrinker);
|
||||
|
||||
if (xe->destroy_wq)
|
||||
destroy_workqueue(xe->destroy_wq);
|
||||
|
||||
@@ -435,6 +439,10 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
xe->mem.shrinker = xe_shrinker_create(xe);
|
||||
if (IS_ERR(xe->mem.shrinker))
|
||||
return ERR_CAST(xe->mem.shrinker);
|
||||
|
||||
xe->info.devid = pdev->device;
|
||||
xe->info.revid = pdev->revision;
|
||||
xe->info.force_execlist = xe_modparam.force_execlist;
|
||||
|
||||
@@ -372,6 +372,8 @@ struct xe_device {
|
||||
struct xe_vram_region vram;
|
||||
/** @mem.sys_mgr: system TTM manager */
|
||||
struct ttm_resource_manager sys_mgr;
|
||||
/** @mem.sys_mgr: system memory shrinker. */
|
||||
struct xe_shrinker *shrinker;
|
||||
} mem;
|
||||
|
||||
/** @sriov: device level virtualization data */
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright © 2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/shrinker.h>
|
||||
|
||||
#include <drm/ttm/ttm_backup.h>
|
||||
#include <drm/ttm/ttm_bo.h>
|
||||
#include <drm/ttm/ttm_tt.h>
|
||||
|
||||
#include "xe_bo.h"
|
||||
#include "xe_pm.h"
|
||||
#include "xe_shrinker.h"
|
||||
|
||||
/**
|
||||
* struct xe_shrinker - per-device shrinker
|
||||
* @xe: Back pointer to the device.
|
||||
* @lock: Lock protecting accounting.
|
||||
* @shrinkable_pages: Number of pages that are currently shrinkable.
|
||||
* @purgeable_pages: Number of pages that are currently purgeable.
|
||||
* @shrink: Pointer to the mm shrinker.
|
||||
* @pm_worker: Worker to wake up the device if required.
|
||||
*/
|
||||
struct xe_shrinker {
|
||||
struct xe_device *xe;
|
||||
rwlock_t lock;
|
||||
long shrinkable_pages;
|
||||
long purgeable_pages;
|
||||
struct shrinker *shrink;
|
||||
struct work_struct pm_worker;
|
||||
};
|
||||
|
||||
static struct xe_shrinker *to_xe_shrinker(struct shrinker *shrink)
|
||||
{
|
||||
return shrink->private_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_shrinker_mod_pages() - Modify shrinker page accounting
|
||||
* @shrinker: Pointer to the struct xe_shrinker.
|
||||
* @shrinkable: Shrinkable pages delta. May be negative.
|
||||
* @purgeable: Purgeable page delta. May be negative.
|
||||
*
|
||||
* Modifies the shrinkable and purgeable pages accounting.
|
||||
*/
|
||||
void
|
||||
xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgeable)
|
||||
{
|
||||
write_lock(&shrinker->lock);
|
||||
shrinker->shrinkable_pages += shrinkable;
|
||||
shrinker->purgeable_pages += purgeable;
|
||||
write_unlock(&shrinker->lock);
|
||||
}
|
||||
|
||||
static s64 xe_shrinker_walk(struct xe_device *xe,
|
||||
struct ttm_operation_ctx *ctx,
|
||||
const struct xe_bo_shrink_flags flags,
|
||||
unsigned long to_scan, unsigned long *scanned)
|
||||
{
|
||||
unsigned int mem_type;
|
||||
s64 freed = 0, lret;
|
||||
|
||||
for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) {
|
||||
struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type);
|
||||
struct ttm_bo_lru_cursor curs;
|
||||
struct ttm_buffer_object *ttm_bo;
|
||||
|
||||
if (!man || !man->use_tt)
|
||||
continue;
|
||||
|
||||
ttm_bo_lru_for_each_reserved_guarded(&curs, man, ctx, ttm_bo) {
|
||||
if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
|
||||
continue;
|
||||
|
||||
lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned);
|
||||
if (lret < 0)
|
||||
return lret;
|
||||
|
||||
freed += lret;
|
||||
if (*scanned >= to_scan)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return freed;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
xe_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
|
||||
{
|
||||
struct xe_shrinker *shrinker = to_xe_shrinker(shrink);
|
||||
unsigned long num_pages;
|
||||
bool can_backup = !!(sc->gfp_mask & __GFP_FS);
|
||||
|
||||
num_pages = ttm_backup_bytes_avail() >> PAGE_SHIFT;
|
||||
read_lock(&shrinker->lock);
|
||||
|
||||
if (can_backup)
|
||||
num_pages = min_t(unsigned long, num_pages, shrinker->shrinkable_pages);
|
||||
else
|
||||
num_pages = 0;
|
||||
|
||||
num_pages += shrinker->purgeable_pages;
|
||||
read_unlock(&shrinker->lock);
|
||||
|
||||
return num_pages ? num_pages : SHRINK_EMPTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we need runtime pm, and if so try to grab a reference if
|
||||
* already active. If grabbing a reference fails, queue a worker that
|
||||
* does it for us outside of reclaim, but don't wait for it to complete.
|
||||
* If bo shrinking needs an rpm reference and we don't have it (yet),
|
||||
* that bo will be skipped anyway.
|
||||
*/
|
||||
static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force,
|
||||
unsigned long nr_to_scan, bool can_backup)
|
||||
{
|
||||
struct xe_device *xe = shrinker->xe;
|
||||
|
||||
if (IS_DGFX(xe) || !xe_device_has_flat_ccs(xe) ||
|
||||
!ttm_backup_bytes_avail())
|
||||
return false;
|
||||
|
||||
if (!force) {
|
||||
read_lock(&shrinker->lock);
|
||||
force = (nr_to_scan > shrinker->purgeable_pages && can_backup);
|
||||
read_unlock(&shrinker->lock);
|
||||
if (!force)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!xe_pm_runtime_get_if_active(xe)) {
|
||||
if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
|
||||
xe_pm_runtime_get(xe);
|
||||
return true;
|
||||
}
|
||||
queue_work(xe->unordered_wq, &shrinker->pm_worker);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
|
||||
{
|
||||
if (runtime_pm)
|
||||
xe_pm_runtime_put(shrinker->xe);
|
||||
}
|
||||
|
||||
static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
|
||||
{
|
||||
struct xe_shrinker *shrinker = to_xe_shrinker(shrink);
|
||||
struct ttm_operation_ctx ctx = {
|
||||
.interruptible = false,
|
||||
.no_wait_gpu = ttm_bo_shrink_avoid_wait(),
|
||||
};
|
||||
unsigned long nr_to_scan, nr_scanned = 0, freed = 0;
|
||||
struct xe_bo_shrink_flags shrink_flags = {
|
||||
.purge = true,
|
||||
/* Don't request writeback without __GFP_IO. */
|
||||
.writeback = !ctx.no_wait_gpu && (sc->gfp_mask & __GFP_IO),
|
||||
};
|
||||
bool runtime_pm;
|
||||
bool purgeable;
|
||||
bool can_backup = !!(sc->gfp_mask & __GFP_FS);
|
||||
s64 lret;
|
||||
|
||||
nr_to_scan = sc->nr_to_scan;
|
||||
|
||||
read_lock(&shrinker->lock);
|
||||
purgeable = !!shrinker->purgeable_pages;
|
||||
read_unlock(&shrinker->lock);
|
||||
|
||||
/* Might need runtime PM. Try to wake early if it looks like it. */
|
||||
runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup);
|
||||
|
||||
if (purgeable && nr_scanned < nr_to_scan) {
|
||||
lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
|
||||
nr_to_scan, &nr_scanned);
|
||||
if (lret >= 0)
|
||||
freed += lret;
|
||||
}
|
||||
|
||||
sc->nr_scanned = nr_scanned;
|
||||
if (nr_scanned >= nr_to_scan || !can_backup)
|
||||
goto out;
|
||||
|
||||
/* If we didn't wake before, try to do it now if needed. */
|
||||
if (!runtime_pm)
|
||||
runtime_pm = xe_shrinker_runtime_pm_get(shrinker, true, 0, can_backup);
|
||||
|
||||
shrink_flags.purge = false;
|
||||
lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
|
||||
nr_to_scan, &nr_scanned);
|
||||
if (lret >= 0)
|
||||
freed += lret;
|
||||
|
||||
sc->nr_scanned = nr_scanned;
|
||||
out:
|
||||
xe_shrinker_runtime_pm_put(shrinker, runtime_pm);
|
||||
return nr_scanned ? freed : SHRINK_STOP;
|
||||
}
|
||||
|
||||
/* Wake up the device for shrinking. */
|
||||
static void xe_shrinker_pm(struct work_struct *work)
|
||||
{
|
||||
struct xe_shrinker *shrinker =
|
||||
container_of(work, typeof(*shrinker), pm_worker);
|
||||
|
||||
xe_pm_runtime_get(shrinker->xe);
|
||||
xe_pm_runtime_put(shrinker->xe);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_shrinker_create() - Create an xe per-device shrinker
|
||||
* @xe: Pointer to the xe device.
|
||||
*
|
||||
* Returns: A pointer to the created shrinker on success,
|
||||
* Negative error code on failure.
|
||||
*/
|
||||
struct xe_shrinker *xe_shrinker_create(struct xe_device *xe)
|
||||
{
|
||||
struct xe_shrinker *shrinker = kzalloc(sizeof(*shrinker), GFP_KERNEL);
|
||||
|
||||
if (!shrinker)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
shrinker->shrink = shrinker_alloc(0, "xe system shrinker");
|
||||
if (!shrinker->shrink) {
|
||||
kfree(shrinker);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
INIT_WORK(&shrinker->pm_worker, xe_shrinker_pm);
|
||||
shrinker->xe = xe;
|
||||
rwlock_init(&shrinker->lock);
|
||||
shrinker->shrink->count_objects = xe_shrinker_count;
|
||||
shrinker->shrink->scan_objects = xe_shrinker_scan;
|
||||
shrinker->shrink->private_data = shrinker;
|
||||
shrinker_register(shrinker->shrink);
|
||||
|
||||
return shrinker;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_shrinker_destroy() - Destroy an xe per-device shrinker
|
||||
* @shrinker: Pointer to the shrinker to destroy.
|
||||
*/
|
||||
void xe_shrinker_destroy(struct xe_shrinker *shrinker)
|
||||
{
|
||||
xe_assert(shrinker->xe, !shrinker->shrinkable_pages);
|
||||
xe_assert(shrinker->xe, !shrinker->purgeable_pages);
|
||||
shrinker_free(shrinker->shrink);
|
||||
flush_work(&shrinker->pm_worker);
|
||||
kfree(shrinker);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _XE_SHRINKER_H_
|
||||
#define _XE_SHRINKER_H_
|
||||
|
||||
struct xe_shrinker;
|
||||
struct xe_device;
|
||||
|
||||
void xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgeable);
|
||||
|
||||
struct xe_shrinker *xe_shrinker_create(struct xe_device *xe);
|
||||
|
||||
void xe_shrinker_destroy(struct xe_shrinker *shrinker);
|
||||
|
||||
#endif
|
||||
@@ -108,9 +108,8 @@ int xe_ttm_sys_mgr_init(struct xe_device *xe)
|
||||
u64 gtt_size;
|
||||
|
||||
si_meminfo(&si);
|
||||
/* Potentially restrict amount of TT memory here. */
|
||||
gtt_size = (u64)si.totalram * si.mem_unit;
|
||||
/* TTM limits allocation of all TTM devices by 50% of system memory */
|
||||
gtt_size /= 2;
|
||||
|
||||
man->use_tt = true;
|
||||
man->func = &xe_ttm_sys_mgr_func;
|
||||
|
||||
@@ -216,12 +216,3 @@ void host1x_debug_dump(struct host1x *host1x)
|
||||
|
||||
show_all(host1x, &o, true);
|
||||
}
|
||||
|
||||
void host1x_debug_dump_syncpts(struct host1x *host1x)
|
||||
{
|
||||
struct output o = {
|
||||
.fn = write_to_printk
|
||||
};
|
||||
|
||||
show_syncpts(host1x, &o, false);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,5 @@ extern unsigned int host1x_debug_trace_cmdbuf;
|
||||
void host1x_debug_init(struct host1x *host1x);
|
||||
void host1x_debug_deinit(struct host1x *host1x);
|
||||
void host1x_debug_dump(struct host1x *host1x);
|
||||
void host1x_debug_dump_syncpts(struct host1x *host1x);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -165,38 +165,6 @@ int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
|
||||
|
||||
int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
|
||||
bool hflip, bool vflip)
|
||||
{
|
||||
u32 r90, vf, hf;
|
||||
|
||||
r90 = ((u32)mode >> 2) & 0x1;
|
||||
hf = ((u32)mode >> 1) & 0x1;
|
||||
vf = ((u32)mode >> 0) & 0x1;
|
||||
hf ^= (u32)hflip;
|
||||
vf ^= (u32)vflip;
|
||||
|
||||
switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
|
||||
case IPU_ROTATE_NONE:
|
||||
*degrees = 0;
|
||||
break;
|
||||
case IPU_ROTATE_90_RIGHT:
|
||||
*degrees = 90;
|
||||
break;
|
||||
case IPU_ROTATE_180:
|
||||
*degrees = 180;
|
||||
break;
|
||||
case IPU_ROTATE_90_LEFT:
|
||||
*degrees = 270;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
|
||||
|
||||
struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
|
||||
{
|
||||
struct ipuv3_channel *channel;
|
||||
@@ -516,12 +484,6 @@ int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
|
||||
|
||||
bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
|
||||
{
|
||||
return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
|
||||
|
||||
int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
|
||||
{
|
||||
struct ipu_soc *ipu = channel->ipu;
|
||||
|
||||
@@ -337,12 +337,6 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
|
||||
|
||||
int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch)
|
||||
{
|
||||
return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize);
|
||||
|
||||
void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
|
||||
{
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
|
||||
@@ -452,23 +446,6 @@ int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough);
|
||||
|
||||
void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format)
|
||||
{
|
||||
switch (pixel_format) {
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved);
|
||||
|
||||
void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch,
|
||||
unsigned int uv_stride,
|
||||
unsigned int u_offset, unsigned int v_offset)
|
||||
|
||||
@@ -185,32 +185,6 @@ static inline void ipu_csi_write(struct ipu_csi *csi, u32 value,
|
||||
writel(value, csi->base + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set mclk division ratio for generating test mode mclk. Only used
|
||||
* for test generator.
|
||||
*/
|
||||
static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk,
|
||||
u32 ipu_clk)
|
||||
{
|
||||
u32 temp;
|
||||
int div_ratio;
|
||||
|
||||
div_ratio = (ipu_clk / pixel_clk) - 1;
|
||||
|
||||
if (div_ratio > 0xFF || div_ratio < 0) {
|
||||
dev_err(csi->ipu->dev,
|
||||
"value of pixel_clk extends normal range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
temp = ipu_csi_read(csi, CSI_SENS_CONF);
|
||||
temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
|
||||
ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
|
||||
CSI_SENS_CONF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the CSI data format and data width for the given V4L2 media
|
||||
* bus pixel format code.
|
||||
@@ -538,56 +512,6 @@ out_unlock:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_csi_init_interface);
|
||||
|
||||
bool ipu_csi_is_interlaced(struct ipu_csi *csi)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 sensor_protocol;
|
||||
|
||||
spin_lock_irqsave(&csi->lock, flags);
|
||||
sensor_protocol =
|
||||
(ipu_csi_read(csi, CSI_SENS_CONF) &
|
||||
CSI_SENS_CONF_SENS_PRTCL_MASK) >>
|
||||
CSI_SENS_CONF_SENS_PRTCL_SHIFT;
|
||||
spin_unlock_irqrestore(&csi->lock, flags);
|
||||
|
||||
switch (sensor_protocol) {
|
||||
case IPU_CSI_CLK_MODE_GATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_NONGATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
|
||||
return false;
|
||||
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
|
||||
return true;
|
||||
default:
|
||||
dev_err(csi->ipu->dev,
|
||||
"CSI %d sensor protocol unsupported\n", csi->id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced);
|
||||
|
||||
void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&csi->lock, flags);
|
||||
|
||||
reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE);
|
||||
w->width = (reg & 0xFFFF) + 1;
|
||||
w->height = (reg >> 16 & 0xFFFF) + 1;
|
||||
|
||||
reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
|
||||
w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT;
|
||||
w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT;
|
||||
|
||||
spin_unlock_irqrestore(&csi->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_csi_get_window);
|
||||
|
||||
void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -624,38 +548,6 @@ void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_csi_set_downsize);
|
||||
|
||||
void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active,
|
||||
u32 r_value, u32 g_value, u32 b_value,
|
||||
u32 pix_clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 ipu_clk = clk_get_rate(csi->clk_ipu);
|
||||
u32 temp;
|
||||
|
||||
spin_lock_irqsave(&csi->lock, flags);
|
||||
|
||||
temp = ipu_csi_read(csi, CSI_TST_CTRL);
|
||||
|
||||
if (!active) {
|
||||
temp &= ~CSI_TEST_GEN_MODE_EN;
|
||||
ipu_csi_write(csi, temp, CSI_TST_CTRL);
|
||||
} else {
|
||||
/* Set sensb_mclk div_ratio */
|
||||
ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk);
|
||||
|
||||
temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
|
||||
CSI_TEST_GEN_B_MASK);
|
||||
temp |= CSI_TEST_GEN_MODE_EN;
|
||||
temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
|
||||
(g_value << CSI_TEST_GEN_G_SHIFT) |
|
||||
(b_value << CSI_TEST_GEN_B_SHIFT);
|
||||
ipu_csi_write(csi, temp, CSI_TST_CTRL);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&csi->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator);
|
||||
|
||||
int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
|
||||
struct v4l2_mbus_framefmt *mbus_fmt)
|
||||
{
|
||||
|
||||
@@ -321,79 +321,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
|
||||
|
||||
int ipu_ic_task_graphics_init(struct ipu_ic *ic,
|
||||
const struct ipu_ic_colorspace *g_in_cs,
|
||||
bool galpha_en, u32 galpha,
|
||||
bool colorkey_en, u32 colorkey)
|
||||
{
|
||||
struct ipu_ic_priv *priv = ic->priv;
|
||||
struct ipu_ic_csc csc2;
|
||||
unsigned long flags;
|
||||
u32 reg, ic_conf;
|
||||
int ret = 0;
|
||||
|
||||
if (ic->task == IC_TASK_ENCODER)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
ic_conf = ipu_ic_read(ic, IC_CONF);
|
||||
|
||||
if (!(ic_conf & ic->bit->ic_conf_csc1_en)) {
|
||||
struct ipu_ic_csc csc1;
|
||||
|
||||
ret = ipu_ic_calc_csc(&csc1,
|
||||
V4L2_YCBCR_ENC_601,
|
||||
V4L2_QUANTIZATION_FULL_RANGE,
|
||||
IPUV3_COLORSPACE_RGB,
|
||||
V4L2_YCBCR_ENC_601,
|
||||
V4L2_QUANTIZATION_FULL_RANGE,
|
||||
IPUV3_COLORSPACE_RGB);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* need transparent CSC1 conversion */
|
||||
ret = init_csc(ic, &csc1, 0);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ic->g_in_cs = *g_in_cs;
|
||||
csc2.in_cs = ic->g_in_cs;
|
||||
csc2.out_cs = ic->out_cs;
|
||||
|
||||
ret = __ipu_ic_calc_csc(&csc2);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = init_csc(ic, &csc2, 1);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
if (galpha_en) {
|
||||
ic_conf |= IC_CONF_IC_GLB_LOC_A;
|
||||
reg = ipu_ic_read(ic, IC_CMBP_1);
|
||||
reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit);
|
||||
reg |= (galpha << ic->bit->ic_cmb_galpha_bit);
|
||||
ipu_ic_write(ic, reg, IC_CMBP_1);
|
||||
} else
|
||||
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
|
||||
|
||||
if (colorkey_en) {
|
||||
ic_conf |= IC_CONF_KEY_COLOR_EN;
|
||||
ipu_ic_write(ic, colorkey, IC_CMBP_2);
|
||||
} else
|
||||
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
|
||||
|
||||
ipu_ic_write(ic, ic_conf, IC_CONF);
|
||||
|
||||
ic->graphics = true;
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init);
|
||||
|
||||
int ipu_ic_task_init_rsc(struct ipu_ic *ic,
|
||||
const struct ipu_ic_csc *csc,
|
||||
int in_width, int in_height,
|
||||
|
||||
@@ -355,20 +355,6 @@ static void dump_format(struct ipu_image_convert_ctx *ctx,
|
||||
(ic_image->fmt->fourcc >> 24) & 0xff);
|
||||
}
|
||||
|
||||
int ipu_image_convert_enum_format(int index, u32 *fourcc)
|
||||
{
|
||||
const struct ipu_image_pixfmt *fmt;
|
||||
|
||||
if (index >= (int)ARRAY_SIZE(image_convert_formats))
|
||||
return -EINVAL;
|
||||
|
||||
/* Format found */
|
||||
fmt = &image_convert_formats[index];
|
||||
*fourcc = fmt->fourcc;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
|
||||
|
||||
static void free_dma_buf(struct ipu_image_convert_priv *priv,
|
||||
struct ipu_image_convert_dma_buf *buf)
|
||||
{
|
||||
@@ -2437,40 +2423,6 @@ ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_image_convert);
|
||||
|
||||
/* "Canned" synchronous single image conversion */
|
||||
static void image_convert_sync_complete(struct ipu_image_convert_run *run,
|
||||
void *data)
|
||||
{
|
||||
struct completion *comp = data;
|
||||
|
||||
complete(comp);
|
||||
}
|
||||
|
||||
int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
|
||||
struct ipu_image *in, struct ipu_image *out,
|
||||
enum ipu_rotate_mode rot_mode)
|
||||
{
|
||||
struct ipu_image_convert_run *run;
|
||||
struct completion comp;
|
||||
int ret;
|
||||
|
||||
init_completion(&comp);
|
||||
|
||||
run = ipu_image_convert(ipu, ic_task, in, out, rot_mode,
|
||||
image_convert_sync_complete, &comp);
|
||||
if (IS_ERR(run))
|
||||
return PTR_ERR(run);
|
||||
|
||||
ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
|
||||
ret = (ret == 0) ? -ETIMEDOUT : 0;
|
||||
|
||||
ipu_image_convert_unprepare(run->ctx);
|
||||
kfree(run);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
|
||||
|
||||
int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev)
|
||||
{
|
||||
struct ipu_image_convert_priv *priv;
|
||||
|
||||
@@ -216,8 +216,6 @@ void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync);
|
||||
int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
|
||||
int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
|
||||
|
||||
bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno);
|
||||
|
||||
int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id,
|
||||
unsigned long base, u32 module, struct clk *clk_ipu);
|
||||
void ipu_csi_exit(struct ipu_soc *ipu, int id);
|
||||
|
||||
@@ -150,17 +150,6 @@ void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_vdi_setup);
|
||||
|
||||
void ipu_vdi_unsetup(struct ipu_vdi *vdi)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&vdi->lock, flags);
|
||||
ipu_vdi_write(vdi, 0, VDI_FSIZE);
|
||||
ipu_vdi_write(vdi, 0, VDI_C);
|
||||
spin_unlock_irqrestore(&vdi->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
|
||||
|
||||
int ipu_vdi_enable(struct ipu_vdi *vdi)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
@@ -337,9 +337,7 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflis
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
y_low = pageref->offset / info->fix.line_length;
|
||||
y_high = (pageref->offset + PAGE_SIZE - 1) / info->fix.line_length;
|
||||
dev_dbg(info->device,
|
||||
"page->index=%lu y_low=%d y_high=%d\n",
|
||||
pageref->page->index, y_low, y_high);
|
||||
dev_dbg(info->device, "y_low=%d y_high=%d\n", y_low, y_high);
|
||||
if (y_high > info->var.yres - 1)
|
||||
y_high = info->var.yres - 1;
|
||||
if (y_low < dirty_lines_start)
|
||||
|
||||
@@ -96,6 +96,9 @@ void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_
|
||||
void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch,
|
||||
const struct iosys_map *src, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state);
|
||||
void drm_fb_xrgb8888_to_bgr888(struct iosys_map *dst, const unsigned int *dst_pitch,
|
||||
const struct iosys_map *src, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state);
|
||||
void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch,
|
||||
const struct iosys_map *src, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, struct drm_format_conv_state *state);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-resv.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
@@ -575,6 +576,19 @@ static inline bool drm_gem_object_is_shared_for_memory_stats(struct drm_gem_obje
|
||||
return (obj->handle_count > 1) || obj->dma_buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gem_is_imported() - Tests if GEM object's buffer has been imported
|
||||
* @obj: the GEM object
|
||||
*
|
||||
* Returns:
|
||||
* True if the GEM object's buffer has been imported, false otherwise
|
||||
*/
|
||||
static inline bool drm_gem_is_imported(const struct drm_gem_object *obj)
|
||||
{
|
||||
/* The dma-buf's priv field points to the original GEM object. */
|
||||
return obj->dma_buf && (obj->dma_buf->priv != obj);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
/**
|
||||
* drm_gem_gpuva_set_lock() - Set the lock protecting accesses to the gpuva list.
|
||||
|
||||
@@ -120,7 +120,7 @@ static inline bool drm_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem
|
||||
{
|
||||
return (shmem->madv > 0) &&
|
||||
!shmem->vmap_use_count && shmem->sgt &&
|
||||
!shmem->base.dma_buf && !shmem->base.import_attach;
|
||||
!shmem->base.dma_buf && !drm_gem_is_imported(&shmem->base);
|
||||
}
|
||||
|
||||
void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem);
|
||||
|
||||
@@ -95,8 +95,6 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test,
|
||||
sizeof(_type), \
|
||||
offsetof(_type, _member), \
|
||||
_feat))
|
||||
struct drm_modeset_acquire_ctx *
|
||||
drm_kunit_helper_acquire_ctx_alloc(struct kunit *test);
|
||||
|
||||
struct drm_atomic_state *
|
||||
drm_kunit_helper_atomic_state_alloc(struct kunit *test,
|
||||
|
||||
+23
-18
@@ -584,9 +584,15 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
|
||||
* Prefer drm_device based logging over device or prink based logging.
|
||||
*/
|
||||
|
||||
/* Helper to enforce struct drm_device type */
|
||||
static inline struct device *__drm_to_dev(const struct drm_device *drm)
|
||||
{
|
||||
return drm ? drm->dev : NULL;
|
||||
}
|
||||
|
||||
/* Helper for struct drm_device based logging. */
|
||||
#define __drm_printk(drm, level, type, fmt, ...) \
|
||||
dev_##level##type((drm) ? (drm)->dev : NULL, "[drm] " fmt, ##__VA_ARGS__)
|
||||
dev_##level##type(__drm_to_dev(drm), "[drm] " fmt, ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define drm_info(drm, fmt, ...) \
|
||||
@@ -620,25 +626,25 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev,
|
||||
|
||||
|
||||
#define drm_dbg_core(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_CORE, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_driver(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_CORE, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_driver(drm, fmt, ...) \
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_kms(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_KMS, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_KMS, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_prime(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_PRIME, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_PRIME, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_atomic(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_vbl(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_VBL, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_VBL, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_state(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_STATE, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_STATE, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_lease(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_LEASE, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_LEASE, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_dp(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DP, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DP, fmt, ##__VA_ARGS__)
|
||||
#define drm_dbg_drmres(drm, fmt, ...) \
|
||||
drm_dev_dbg((drm) ? (drm)->dev : NULL, DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
|
||||
drm_dev_dbg(__drm_to_dev(drm), DRM_UT_DRMRES, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define drm_dbg(drm, fmt, ...) drm_dbg_driver(drm, fmt, ##__VA_ARGS__)
|
||||
|
||||
@@ -727,10 +733,9 @@ void __drm_err(const char *format, ...);
|
||||
#define __DRM_DEFINE_DBG_RATELIMITED(category, drm, fmt, ...) \
|
||||
({ \
|
||||
static DEFINE_RATELIMIT_STATE(rs_, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);\
|
||||
const struct drm_device *drm_ = (drm); \
|
||||
\
|
||||
if (drm_debug_enabled(DRM_UT_ ## category) && __ratelimit(&rs_)) \
|
||||
drm_dev_printk(drm_ ? drm_->dev : NULL, KERN_DEBUG, fmt, ## __VA_ARGS__); \
|
||||
drm_dev_printk(__drm_to_dev(drm), KERN_DEBUG, fmt, ## __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define drm_dbg_ratelimited(drm, fmt, ...) \
|
||||
@@ -752,13 +757,13 @@ void __drm_err(const char *format, ...);
|
||||
/* Helper for struct drm_device based WARNs */
|
||||
#define drm_WARN(drm, condition, format, arg...) \
|
||||
WARN(condition, "%s %s: [drm] " format, \
|
||||
dev_driver_string((drm)->dev), \
|
||||
dev_name((drm)->dev), ## arg)
|
||||
dev_driver_string(__drm_to_dev(drm)), \
|
||||
dev_name(__drm_to_dev(drm)), ## arg)
|
||||
|
||||
#define drm_WARN_ONCE(drm, condition, format, arg...) \
|
||||
WARN_ONCE(condition, "%s %s: [drm] " format, \
|
||||
dev_driver_string((drm)->dev), \
|
||||
dev_name((drm)->dev), ## arg)
|
||||
dev_driver_string(__drm_to_dev(drm)), \
|
||||
dev_name(__drm_to_dev(drm)), ## arg)
|
||||
|
||||
#define drm_WARN_ON(drm, x) \
|
||||
drm_WARN((drm), (x), "%s", \
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _TTM_BACKUP_H_
|
||||
#define _TTM_BACKUP_H_
|
||||
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
|
||||
struct ttm_backup;
|
||||
|
||||
/**
|
||||
* ttm_backup_handle_to_page_ptr() - Convert handle to struct page pointer
|
||||
* @handle: The handle to convert.
|
||||
*
|
||||
* Converts an opaque handle received from the
|
||||
* struct ttm_backoup_ops::backup_page() function to an (invalid)
|
||||
* struct page pointer suitable for a struct page array.
|
||||
*
|
||||
* Return: An (invalid) struct page pointer.
|
||||
*/
|
||||
static inline struct page *
|
||||
ttm_backup_handle_to_page_ptr(unsigned long handle)
|
||||
{
|
||||
return (struct page *)(handle << 1 | 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_backup_page_ptr_is_handle() - Whether a struct page pointer is a handle
|
||||
* @page: The struct page pointer to check.
|
||||
*
|
||||
* Return: true if the struct page pointer is a handld returned from
|
||||
* ttm_backup_handle_to_page_ptr(). False otherwise.
|
||||
*/
|
||||
static inline bool ttm_backup_page_ptr_is_handle(const struct page *page)
|
||||
{
|
||||
return (unsigned long)page & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_backup_page_ptr_to_handle() - Convert a struct page pointer to a handle
|
||||
* @page: The struct page pointer to convert
|
||||
*
|
||||
* Return: The handle that was previously used in
|
||||
* ttm_backup_handle_to_page_ptr() to obtain a struct page pointer, suitable
|
||||
* for use as argument in the struct ttm_backup_ops drop() or
|
||||
* copy_backed_up_page() functions.
|
||||
*/
|
||||
static inline unsigned long
|
||||
ttm_backup_page_ptr_to_handle(const struct page *page)
|
||||
{
|
||||
WARN_ON(!ttm_backup_page_ptr_is_handle(page));
|
||||
return (unsigned long)page >> 1;
|
||||
}
|
||||
|
||||
void ttm_backup_drop(struct ttm_backup *backup, pgoff_t handle);
|
||||
|
||||
int ttm_backup_copy_page(struct ttm_backup *backup, struct page *dst,
|
||||
pgoff_t handle, bool intr);
|
||||
|
||||
s64
|
||||
ttm_backup_backup_page(struct ttm_backup *backup, struct page *page,
|
||||
bool writeback, pgoff_t idx, gfp_t page_gfp,
|
||||
gfp_t alloc_gfp);
|
||||
|
||||
void ttm_backup_fini(struct ttm_backup *backup);
|
||||
|
||||
u64 ttm_backup_bytes_avail(void);
|
||||
|
||||
struct ttm_backup *ttm_backup_shmem_create(loff_t size);
|
||||
|
||||
#endif
|
||||
@@ -225,6 +225,27 @@ struct ttm_lru_walk {
|
||||
s64 ttm_lru_walk_for_evict(struct ttm_lru_walk *walk, struct ttm_device *bdev,
|
||||
struct ttm_resource_manager *man, s64 target);
|
||||
|
||||
/**
|
||||
* struct ttm_bo_shrink_flags - flags to govern the bo shrinking behaviour
|
||||
* @purge: Purge the content rather than backing it up.
|
||||
* @writeback: Attempt to immediately write content to swap space.
|
||||
* @allow_move: Allow moving to system before shrinking. This is typically
|
||||
* not desired for zombie- or ghost objects (with zombie object meaning
|
||||
* objects with a zero gem object refcount)
|
||||
*/
|
||||
struct ttm_bo_shrink_flags {
|
||||
u32 purge : 1;
|
||||
u32 writeback : 1;
|
||||
u32 allow_move : 1;
|
||||
};
|
||||
|
||||
long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo,
|
||||
const struct ttm_bo_shrink_flags flags);
|
||||
|
||||
bool ttm_bo_shrink_suitable(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx);
|
||||
|
||||
bool ttm_bo_shrink_avoid_wait(void);
|
||||
|
||||
/**
|
||||
* ttm_bo_get - reference a struct ttm_buffer_object
|
||||
*
|
||||
@@ -467,4 +488,76 @@ void ttm_bo_tt_destroy(struct ttm_buffer_object *bo);
|
||||
int ttm_bo_populate(struct ttm_buffer_object *bo,
|
||||
struct ttm_operation_ctx *ctx);
|
||||
|
||||
/* Driver LRU walk helpers initially targeted for shrinking. */
|
||||
|
||||
/**
|
||||
* struct ttm_bo_lru_cursor - Iterator cursor for TTM LRU list looping
|
||||
*/
|
||||
struct ttm_bo_lru_cursor {
|
||||
/** @res_curs: Embedded struct ttm_resource_cursor. */
|
||||
struct ttm_resource_cursor res_curs;
|
||||
/**
|
||||
* @ctx: The struct ttm_operation_ctx used while looping.
|
||||
* governs the locking mode.
|
||||
*/
|
||||
struct ttm_operation_ctx *ctx;
|
||||
/**
|
||||
* @bo: Buffer object pointer if a buffer object is refcounted,
|
||||
* NULL otherwise.
|
||||
*/
|
||||
struct ttm_buffer_object *bo;
|
||||
/**
|
||||
* @needs_unlock: Valid iff @bo != NULL. The bo resv needs
|
||||
* unlock before the next iteration or after loop exit.
|
||||
*/
|
||||
bool needs_unlock;
|
||||
};
|
||||
|
||||
void ttm_bo_lru_cursor_fini(struct ttm_bo_lru_cursor *curs);
|
||||
|
||||
struct ttm_bo_lru_cursor *
|
||||
ttm_bo_lru_cursor_init(struct ttm_bo_lru_cursor *curs,
|
||||
struct ttm_resource_manager *man,
|
||||
struct ttm_operation_ctx *ctx);
|
||||
|
||||
struct ttm_buffer_object *ttm_bo_lru_cursor_first(struct ttm_bo_lru_cursor *curs);
|
||||
|
||||
struct ttm_buffer_object *ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs);
|
||||
|
||||
/*
|
||||
* Defines needed to use autocleanup (linux/cleanup.h) with struct ttm_bo_lru_cursor.
|
||||
*/
|
||||
DEFINE_CLASS(ttm_bo_lru_cursor, struct ttm_bo_lru_cursor *,
|
||||
if (_T) {ttm_bo_lru_cursor_fini(_T); },
|
||||
ttm_bo_lru_cursor_init(curs, man, ctx),
|
||||
struct ttm_bo_lru_cursor *curs, struct ttm_resource_manager *man,
|
||||
struct ttm_operation_ctx *ctx);
|
||||
static inline void *
|
||||
class_ttm_bo_lru_cursor_lock_ptr(class_ttm_bo_lru_cursor_t *_T)
|
||||
{ return *_T; }
|
||||
#define class_ttm_bo_lru_cursor_is_conditional false
|
||||
|
||||
/**
|
||||
* ttm_bo_lru_for_each_reserved_guarded() - Iterate over buffer objects owning
|
||||
* resources on LRU lists.
|
||||
* @_cursor: struct ttm_bo_lru_cursor to use for the iteration.
|
||||
* @_man: The resource manager whose LRU lists to iterate over.
|
||||
* @_ctx: The struct ttm_operation_context to govern the @_bo locking.
|
||||
* @_bo: The struct ttm_buffer_object pointer pointing to the buffer object
|
||||
* for the current iteration.
|
||||
*
|
||||
* Iterate over all resources of @_man and for each resource, attempt to
|
||||
* reference and lock (using the locking mode detailed in @_ctx) the buffer
|
||||
* object it points to. If successful, assign @_bo to the address of the
|
||||
* buffer object and update @_cursor. The iteration is guarded in the
|
||||
* sense that @_cursor will be initialized before looping start and cleaned
|
||||
* up at looping termination, even if terminated prematurely by, for
|
||||
* example a return or break statement. Exiting the loop will also unlock
|
||||
* (if needed) and unreference @_bo.
|
||||
*/
|
||||
#define ttm_bo_lru_for_each_reserved_guarded(_cursor, _man, _ctx, _bo) \
|
||||
scoped_guard(ttm_bo_lru_cursor, _cursor, _man, _ctx) \
|
||||
for ((_bo) = ttm_bo_lru_cursor_first(_cursor); (_bo); \
|
||||
(_bo) = ttm_bo_lru_cursor_next(_cursor))
|
||||
|
||||
#endif
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
struct device;
|
||||
struct seq_file;
|
||||
struct ttm_backup_flags;
|
||||
struct ttm_operation_ctx;
|
||||
struct ttm_pool;
|
||||
struct ttm_tt;
|
||||
@@ -89,6 +90,13 @@ void ttm_pool_fini(struct ttm_pool *pool);
|
||||
|
||||
int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m);
|
||||
|
||||
void ttm_pool_drop_backed_up(struct ttm_tt *tt);
|
||||
|
||||
long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *ttm,
|
||||
const struct ttm_backup_flags *flags);
|
||||
int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
|
||||
const struct ttm_operation_ctx *ctx);
|
||||
|
||||
int ttm_pool_mgr_init(unsigned long num_pages);
|
||||
void ttm_pool_mgr_fini(void);
|
||||
|
||||
|
||||
@@ -32,11 +32,13 @@
|
||||
#include <drm/ttm/ttm_caching.h>
|
||||
#include <drm/ttm/ttm_kmap_iter.h>
|
||||
|
||||
struct ttm_backup;
|
||||
struct ttm_device;
|
||||
struct ttm_tt;
|
||||
struct ttm_resource;
|
||||
struct ttm_buffer_object;
|
||||
struct ttm_operation_ctx;
|
||||
struct ttm_pool_tt_restore;
|
||||
|
||||
/**
|
||||
* struct ttm_tt - This is a structure holding the pages, caching- and aperture
|
||||
@@ -85,17 +87,22 @@ struct ttm_tt {
|
||||
* fault handling abuses the DMA api a bit and dma_map_attrs can't be
|
||||
* used to assure pgprot always matches.
|
||||
*
|
||||
* TTM_TT_FLAG_BACKED_UP: TTM internal only. This is set if the
|
||||
* struct ttm_tt has been (possibly partially) backed up.
|
||||
*
|
||||
* TTM_TT_FLAG_PRIV_POPULATED: TTM internal only. DO NOT USE. This is
|
||||
* set by TTM after ttm_tt_populate() has successfully returned, and is
|
||||
* then unset when TTM calls ttm_tt_unpopulate().
|
||||
*
|
||||
*/
|
||||
#define TTM_TT_FLAG_SWAPPED BIT(0)
|
||||
#define TTM_TT_FLAG_ZERO_ALLOC BIT(1)
|
||||
#define TTM_TT_FLAG_EXTERNAL BIT(2)
|
||||
#define TTM_TT_FLAG_EXTERNAL_MAPPABLE BIT(3)
|
||||
#define TTM_TT_FLAG_DECRYPTED BIT(4)
|
||||
#define TTM_TT_FLAG_BACKED_UP BIT(5)
|
||||
|
||||
#define TTM_TT_FLAG_PRIV_POPULATED BIT(5)
|
||||
#define TTM_TT_FLAG_PRIV_POPULATED BIT(6)
|
||||
uint32_t page_flags;
|
||||
/** @num_pages: Number of pages in the page array. */
|
||||
uint32_t num_pages;
|
||||
@@ -105,11 +112,20 @@ struct ttm_tt {
|
||||
dma_addr_t *dma_address;
|
||||
/** @swap_storage: Pointer to shmem struct file for swap storage. */
|
||||
struct file *swap_storage;
|
||||
/**
|
||||
* @backup: Pointer to backup struct for backed up tts.
|
||||
* Could be unified with @swap_storage. Meanwhile, the driver's
|
||||
* ttm_tt_create() callback is responsible for assigning
|
||||
* this field.
|
||||
*/
|
||||
struct ttm_backup *backup;
|
||||
/**
|
||||
* @caching: The current caching state of the pages, see enum
|
||||
* ttm_caching.
|
||||
*/
|
||||
enum ttm_caching caching;
|
||||
/** @restore: Partial restoration from backup state. TTM private */
|
||||
struct ttm_pool_tt_restore *restore;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -129,9 +145,38 @@ static inline bool ttm_tt_is_populated(struct ttm_tt *tt)
|
||||
return tt->page_flags & TTM_TT_FLAG_PRIV_POPULATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_tt_is_swapped() - Whether the ttm_tt is swapped out or backed up
|
||||
* @tt: The struct ttm_tt.
|
||||
*
|
||||
* Return: true if swapped or backed up, false otherwise.
|
||||
*/
|
||||
static inline bool ttm_tt_is_swapped(const struct ttm_tt *tt)
|
||||
{
|
||||
return tt->page_flags & TTM_TT_FLAG_SWAPPED;
|
||||
return tt->page_flags & (TTM_TT_FLAG_SWAPPED | TTM_TT_FLAG_BACKED_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_tt_is_backed_up() - Whether the ttm_tt backed up
|
||||
* @tt: The struct ttm_tt.
|
||||
*
|
||||
* Return: true if swapped or backed up, false otherwise.
|
||||
*/
|
||||
static inline bool ttm_tt_is_backed_up(const struct ttm_tt *tt)
|
||||
{
|
||||
return tt->page_flags & TTM_TT_FLAG_BACKED_UP;
|
||||
}
|
||||
|
||||
/**
|
||||
* ttm_tt_clear_backed_up() - Clear the ttm_tt backed-up status
|
||||
* @tt: The struct ttm_tt.
|
||||
*
|
||||
* Drivers can use this functionto clear the backed-up status,
|
||||
* for example before destroying or re-validating a purged tt.
|
||||
*/
|
||||
static inline void ttm_tt_clear_backed_up(struct ttm_tt *tt)
|
||||
{
|
||||
tt->page_flags &= ~TTM_TT_FLAG_BACKED_UP;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,6 +280,26 @@ void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages);
|
||||
struct ttm_kmap_iter *ttm_kmap_iter_tt_init(struct ttm_kmap_iter_tt *iter_tt,
|
||||
struct ttm_tt *tt);
|
||||
unsigned long ttm_tt_pages_limit(void);
|
||||
|
||||
/**
|
||||
* struct ttm_backup_flags - Flags to govern backup behaviour.
|
||||
* @purge: Free pages without backing up. Bypass pools.
|
||||
* @writeback: Attempt to copy contents directly to swap space, even
|
||||
* if that means blocking on writes to external memory.
|
||||
*/
|
||||
struct ttm_backup_flags {
|
||||
u32 purge : 1;
|
||||
u32 writeback : 1;
|
||||
};
|
||||
|
||||
long ttm_tt_backup(struct ttm_device *bdev, struct ttm_tt *tt,
|
||||
const struct ttm_backup_flags flags);
|
||||
|
||||
int ttm_tt_restore(struct ttm_device *bdev, struct ttm_tt *tt,
|
||||
const struct ttm_operation_ctx *ctx);
|
||||
|
||||
int ttm_tt_setup_backup(struct ttm_tt *tt);
|
||||
|
||||
#if IS_ENABLED(CONFIG_AGP)
|
||||
#include <linux/agp_backend.h>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#define COMPONENT_H
|
||||
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct device;
|
||||
|
||||
@@ -90,6 +90,8 @@ int component_compare_dev_name(struct device *dev, void *data);
|
||||
|
||||
void component_master_del(struct device *,
|
||||
const struct component_master_ops *);
|
||||
bool component_master_is_bound(struct device *parent,
|
||||
const struct component_master_ops *ops);
|
||||
|
||||
struct component_match;
|
||||
|
||||
|
||||
@@ -40,19 +40,6 @@ struct ipu_image_convert_run {
|
||||
typedef void (*ipu_image_convert_cb_t)(struct ipu_image_convert_run *run,
|
||||
void *ctx);
|
||||
|
||||
/**
|
||||
* ipu_image_convert_enum_format() - enumerate the image converter's
|
||||
* supported input and output pixel formats.
|
||||
*
|
||||
* @index: pixel format index
|
||||
* @fourcc: v4l2 fourcc for this index
|
||||
*
|
||||
* Returns 0 with a valid index and fills in v4l2 fourcc, -EINVAL otherwise.
|
||||
*
|
||||
* In V4L2, drivers can call ipu_image_enum_format() in .enum_fmt.
|
||||
*/
|
||||
int ipu_image_convert_enum_format(int index, u32 *fourcc);
|
||||
|
||||
/**
|
||||
* ipu_image_convert_adjust() - adjust input/output images to IPU restrictions.
|
||||
*
|
||||
@@ -176,23 +163,4 @@ ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
|
||||
ipu_image_convert_cb_t complete,
|
||||
void *complete_context);
|
||||
|
||||
/**
|
||||
* ipu_image_convert_sync() - synchronous single image conversion request
|
||||
*
|
||||
* @ipu: the IPU handle to use for the conversion
|
||||
* @ic_task: the IC task to use for the conversion
|
||||
* @in: input image format
|
||||
* @out: output image format
|
||||
* @rot_mode: rotation mode
|
||||
*
|
||||
* Carry out a single image conversion. Returns when the conversion
|
||||
* completes. The input/output formats and rotation mode must already
|
||||
* meet IPU retrictions. The created context is automatically unprepared
|
||||
* and the run freed on return.
|
||||
*/
|
||||
int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
|
||||
struct ipu_image *in, struct ipu_image *out,
|
||||
enum ipu_rotate_mode rot_mode);
|
||||
|
||||
|
||||
#endif /* __IMX_IPU_IMAGE_CONVERT_H__ */
|
||||
|
||||
@@ -262,7 +262,6 @@ void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
|
||||
void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride,
|
||||
u32 pixelformat);
|
||||
void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
|
||||
int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch);
|
||||
void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
|
||||
void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
|
||||
void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
|
||||
@@ -270,7 +269,6 @@ void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
|
||||
int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch,
|
||||
const struct ipu_rgb *rgb);
|
||||
int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width);
|
||||
void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format);
|
||||
void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch,
|
||||
unsigned int uv_stride,
|
||||
unsigned int u_offset,
|
||||
@@ -361,13 +359,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
|
||||
const struct v4l2_mbus_config *mbus_cfg,
|
||||
const struct v4l2_mbus_framefmt *infmt,
|
||||
const struct v4l2_mbus_framefmt *outfmt);
|
||||
bool ipu_csi_is_interlaced(struct ipu_csi *csi);
|
||||
void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w);
|
||||
void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w);
|
||||
void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert);
|
||||
void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active,
|
||||
u32 r_value, u32 g_value, u32 b_value,
|
||||
u32 pix_clk);
|
||||
int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
|
||||
struct v4l2_mbus_framefmt *mbus_fmt);
|
||||
int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
|
||||
@@ -445,10 +438,6 @@ int ipu_ic_task_init_rsc(struct ipu_ic *ic,
|
||||
int in_width, int in_height,
|
||||
int out_width, int out_height,
|
||||
u32 rsc);
|
||||
int ipu_ic_task_graphics_init(struct ipu_ic *ic,
|
||||
const struct ipu_ic_colorspace *g_in_cs,
|
||||
bool galpha_en, u32 galpha,
|
||||
bool colorkey_en, u32 colorkey);
|
||||
void ipu_ic_task_enable(struct ipu_ic *ic);
|
||||
void ipu_ic_task_disable(struct ipu_ic *ic);
|
||||
int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
|
||||
@@ -467,7 +456,6 @@ struct ipu_vdi;
|
||||
void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field);
|
||||
void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel);
|
||||
void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres);
|
||||
void ipu_vdi_unsetup(struct ipu_vdi *vdi);
|
||||
int ipu_vdi_enable(struct ipu_vdi *vdi);
|
||||
int ipu_vdi_disable(struct ipu_vdi *vdi);
|
||||
struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu);
|
||||
@@ -488,8 +476,6 @@ enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc);
|
||||
enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat);
|
||||
int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
|
||||
bool hflip, bool vflip);
|
||||
int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
|
||||
bool hflip, bool vflip);
|
||||
|
||||
struct ipu_client_platformdata {
|
||||
int csi;
|
||||
|
||||
Reference in New Issue
Block a user