drm-misc-next for 5.19:
UAPI Changes: Cross-subsystem Changes: Core Changes: - Add DRM-managed mutex initialisation - edid: Doc improvements - fbdev: deferred io improvements - format-helper: consolidate format conversion helpers - gem: Rework fence handling in drm_gem_plane_helper_prepare_fb Driver Changes: - ast: DisplayPort support, locking improvements - exynos: Revert conversion to devm_drm_of_get_bridge for DSI - mgag200: locking improvements - mxsfb: LCDIF CRC support - nouveau: switch to drm_gem_plane_helper_prepare_fb - rockchip: Refactor IOMMU initialisation, make some structures static, replace drm_detect_hdmi_monitor with drm_display_info.is_hdmi, support swapped YUV formats, clock improvements, rk3568 support, VOP2 support - bridge: - adv7511: Enable CEC for ADV7535 - it6505: Send DPCD SET_POWER to monitor at disable - mcde_dsi: Revert conversion to devm_drm_of_get_bridge - tc358767: Fix for eDP and DP DT endpoint parsing - new bridge: i.MX8MP LDB - panel: - new panel: Startek KD070WVFPA043-C069A -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCYnPM4wAKCRDj7w1vZxhR xQJbAQC8uVNSlkmZlIRp/SHpk8OSl1uzwnGDyo7C5Thf6kI4vQEAyVJ+rqha8dWG +gAAll4fnJP15/UNs2EtyHK1nDWtOgk= =e1SQ -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2022-05-05' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for 5.19: UAPI Changes: Cross-subsystem Changes: Core Changes: - Add DRM-managed mutex initialisation - edid: Doc improvements - fbdev: deferred io improvements - format-helper: consolidate format conversion helpers - gem: Rework fence handling in drm_gem_plane_helper_prepare_fb Driver Changes: - ast: DisplayPort support, locking improvements - exynos: Revert conversion to devm_drm_of_get_bridge for DSI - mgag200: locking improvements - mxsfb: LCDIF CRC support - nouveau: switch to drm_gem_plane_helper_prepare_fb - rockchip: Refactor IOMMU initialisation, make some structures static, replace drm_detect_hdmi_monitor with drm_display_info.is_hdmi, support swapped YUV formats, clock improvements, rk3568 support, VOP2 support - bridge: - adv7511: Enable CEC for ADV7535 - it6505: Send DPCD SET_POWER to monitor at disable - mcde_dsi: Revert conversion to devm_drm_of_get_bridge - tc358767: Fix for eDP and DP DT endpoint parsing - new bridge: i.MX8MP LDB - panel: - new panel: Startek KD070WVFPA043-C069A Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maxime Ripard <maxime@cerno.tech> Link: https://patchwork.freedesktop.org/patch/msgid/20220505131127.lcqvsywo7qt3eywk@houat
This commit is contained in:
commit
c67f84e97b
@ -0,0 +1,92 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/bridge/fsl,ldb.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale i.MX8MP DPI to LVDS bridge chip
|
||||
|
||||
maintainers:
|
||||
- Marek Vasut <marex@denx.de>
|
||||
|
||||
description: |
|
||||
The i.MX8MP mediamix contains two registers which are responsible
|
||||
for configuring the on-SoC DPI-to-LVDS serializer. This describes
|
||||
those registers as bridge within the DT.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: fsl,imx8mp-ldb
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: ldb
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Video port for DPI input.
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Video port for LVDS Channel-A output (panel or bridge).
|
||||
|
||||
port@2:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Video port for LVDS Channel-B output (panel or bridge).
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx8mp-clock.h>
|
||||
|
||||
blk-ctrl {
|
||||
bridge {
|
||||
compatible = "fsl,imx8mp-ldb";
|
||||
clocks = <&clk IMX8MP_CLK_MEDIA_LDB>;
|
||||
clock-names = "ldb";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
ldb_from_lcdif2: endpoint {
|
||||
remote-endpoint = <&lcdif2_to_ldb>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
ldb_lvds_ch0: endpoint {
|
||||
remote-endpoint = <&ldb_to_lvdsx4panel>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
ldb_lvds_ch1: endpoint {
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -296,6 +296,8 @@ properties:
|
||||
- starry,kr070pe2t
|
||||
# Starry 12.2" (1920x1200 pixels) TFT LCD panel
|
||||
- starry,kr122ea0sra
|
||||
# Startek KD070WVFPA043-C069A 7" TFT LCD panel
|
||||
- startek,kd070wvfpa
|
||||
# Team Source Display Technology TST043015CMHX 4.3" WQVGA TFT LCD panel
|
||||
- team-source-display,tst043015cmhx
|
||||
# Tianma Micro-electronics TM070JDHG30 7.0" WXGA TFT LCD panel
|
||||
|
@ -23,10 +23,22 @@ properties:
|
||||
- rockchip,rk3288-dw-hdmi
|
||||
- rockchip,rk3328-dw-hdmi
|
||||
- rockchip,rk3399-dw-hdmi
|
||||
- rockchip,rk3568-dw-hdmi
|
||||
|
||||
reg-io-width:
|
||||
const: 4
|
||||
|
||||
avdd-0v9-supply:
|
||||
description:
|
||||
A 0.9V supply that powers up the SoC internal circuitry. The actual pin name
|
||||
varies between the different SoCs and is usually HDMI_TX_AVDD_0V9 or sometimes
|
||||
HDMI_AVDD_1V0.
|
||||
|
||||
avdd-1v8-supply:
|
||||
description:
|
||||
A 1.8V supply that powers up the SoC internal circuitry. The pin name on the
|
||||
SoC usually is HDMI_TX_AVDD_1V8.
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
items:
|
||||
@ -36,7 +48,8 @@ properties:
|
||||
# order when present.
|
||||
- description: The HDMI CEC controller main clock
|
||||
- description: Power for GRF IO
|
||||
- description: External clock for some HDMI PHY
|
||||
- description: External clock for some HDMI PHY (old clock name, deprecated)
|
||||
- description: External clock for some HDMI PHY (new name)
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
@ -47,10 +60,14 @@ properties:
|
||||
- cec
|
||||
- grf
|
||||
- vpll
|
||||
- ref
|
||||
- enum:
|
||||
- grf
|
||||
- vpll
|
||||
- const: vpll
|
||||
- ref
|
||||
- enum:
|
||||
- vpll
|
||||
- ref
|
||||
|
||||
ddc-i2c-bus:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
@ -72,6 +89,7 @@ properties:
|
||||
The unwedge pinctrl entry shall drive the DDC SDA line low. This is
|
||||
intended to work around a hardware errata that can cause the DDC I2C
|
||||
bus to be wedged.
|
||||
minItems: 1
|
||||
items:
|
||||
- const: default
|
||||
- const: unwedge
|
||||
@ -79,27 +97,21 @@ properties:
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/$defs/port-base
|
||||
unevaluatedProperties: false
|
||||
patternProperties:
|
||||
"^port(@0)?$":
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Input of the DWC HDMI TX
|
||||
|
||||
properties:
|
||||
endpoint:
|
||||
description: Connection to the VOP
|
||||
endpoint@0:
|
||||
$ref: /schemas/graph.yaml#/properties/endpoint
|
||||
description: Connection to the VOPB
|
||||
|
||||
endpoint@1:
|
||||
$ref: /schemas/graph.yaml#/properties/endpoint
|
||||
description: Connection to the VOPL
|
||||
|
||||
required:
|
||||
- endpoint@0
|
||||
- endpoint@1
|
||||
|
||||
required:
|
||||
- port
|
||||
properties:
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Output of the DWC HDMI TX
|
||||
|
||||
rockchip,grf:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
@ -0,0 +1,140 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/rockchip/rockchip-vop2.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Rockchip SoC display controller (VOP2)
|
||||
|
||||
description:
|
||||
VOP2 (Video Output Processor v2) is the display controller for the Rockchip
|
||||
series of SoCs which transfers the image data from a video memory
|
||||
buffer to an external LCD interface.
|
||||
|
||||
maintainers:
|
||||
- Sandy Huang <hjc@rock-chips.com>
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- rockchip,rk3566-vop
|
||||
- rockchip,rk3568-vop
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description:
|
||||
Must contain one entry corresponding to the base address and length
|
||||
of the register space.
|
||||
- description:
|
||||
Can optionally contain a second entry corresponding to
|
||||
the CRTC gamma LUT address.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
The VOP interrupt is shared by several interrupt sources, such as
|
||||
frame start (VSYNC), line flag and other status interrupts.
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Clock for ddr buffer transfer.
|
||||
- description: Clock for the ahb bus to R/W the phy regs.
|
||||
- description: Pixel clock for video port 0.
|
||||
- description: Pixel clock for video port 1.
|
||||
- description: Pixel clock for video port 2.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: aclk
|
||||
- const: hclk
|
||||
- const: dclk_vp0
|
||||
- const: dclk_vp1
|
||||
- const: dclk_vp2
|
||||
|
||||
rockchip,grf:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to GRF regs used for misc control
|
||||
|
||||
ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
properties:
|
||||
port@0:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
Output endpoint of VP0
|
||||
|
||||
port@1:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
Output endpoint of VP1
|
||||
|
||||
port@2:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description:
|
||||
Output endpoint of VP2
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rk3568-cru.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/power/rk3568-power.h>
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
vop: vop@fe040000 {
|
||||
compatible = "rockchip,rk3568-vop";
|
||||
reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru ACLK_VOP>,
|
||||
<&cru HCLK_VOP>,
|
||||
<&cru DCLK_VOP0>,
|
||||
<&cru DCLK_VOP1>,
|
||||
<&cru DCLK_VOP2>;
|
||||
clock-names = "aclk",
|
||||
"hclk",
|
||||
"dclk_vp0",
|
||||
"dclk_vp1",
|
||||
"dclk_vp2";
|
||||
power-domains = <&power RK3568_PD_VO>;
|
||||
iommus = <&vop_mmu>;
|
||||
vop_out: ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
vp0: port@0 {
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
vp1: port@1 {
|
||||
reg = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
vp2: port@2 {
|
||||
reg = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -603,6 +603,20 @@ Level: Advanced
|
||||
Better Testing
|
||||
==============
|
||||
|
||||
Add unit tests using the Kernel Unit Testing (KUnit) framework
|
||||
--------------------------------------------------------------
|
||||
|
||||
The `KUnit <https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html>`_
|
||||
provides a common framework for unit tests within the Linux kernel. Having a
|
||||
test suite would allow to identify regressions earlier.
|
||||
|
||||
A good candidate for the first unit tests are the format-conversion helpers in
|
||||
``drm_format_helper.c``.
|
||||
|
||||
Contact: Javier Martinez Canillas <javierm@redhat.com>
|
||||
|
||||
Level: Intermediate
|
||||
|
||||
Enable trinity for DRM
|
||||
----------------------
|
||||
|
||||
|
@ -157,9 +157,15 @@ static int sync_file_set_fence(struct sync_file *sync_file,
|
||||
* we already own a new reference to the fence. For num_fence > 1
|
||||
* we own the reference of the dma_fence_array creation.
|
||||
*/
|
||||
if (num_fences == 1) {
|
||||
|
||||
if (num_fences == 0) {
|
||||
sync_file->fence = dma_fence_get_stub();
|
||||
kfree(fences);
|
||||
|
||||
} else if (num_fences == 1) {
|
||||
sync_file->fence = fences[0];
|
||||
kfree(fences);
|
||||
|
||||
} else {
|
||||
array = dma_fence_array_create(num_fences, fences,
|
||||
dma_fence_context_alloc(1),
|
||||
@ -261,19 +267,6 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
|
||||
}
|
||||
}
|
||||
|
||||
if (index == 0)
|
||||
fences[index++] = dma_fence_get_stub();
|
||||
|
||||
if (num_fences > index) {
|
||||
struct dma_fence **tmp;
|
||||
|
||||
/* Keep going even when reducing the size failed */
|
||||
tmp = krealloc_array(fences, index, sizeof(*fences),
|
||||
GFP_KERNEL);
|
||||
if (tmp)
|
||||
fences = tmp;
|
||||
}
|
||||
|
||||
if (sync_file_set_fence(sync_file, fences, index) < 0)
|
||||
goto err_put_fences;
|
||||
|
||||
|
@ -344,7 +344,7 @@ static bool malidp_check_pages_threshold(struct malidp_plane_state *ms,
|
||||
else
|
||||
sgt = obj->funcs->get_sg_table(obj);
|
||||
|
||||
if (!sgt)
|
||||
if (IS_ERR(sgt))
|
||||
return false;
|
||||
|
||||
sgl = sgt->sgl;
|
||||
|
@ -3,6 +3,6 @@
|
||||
# Makefile for the drm device driver. This driver provides support for the
|
||||
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
|
||||
|
||||
ast-y := ast_drv.o ast_i2c.o ast_main.o ast_mm.o ast_mode.o ast_post.o ast_dp501.o
|
||||
ast-y := ast_drv.o ast_i2c.o ast_main.o ast_mm.o ast_mode.o ast_post.o ast_dp501.o ast_dp.o
|
||||
|
||||
obj-$(CONFIG_DRM_AST) := ast.o
|
||||
|
282
drivers/gpu/drm/ast/ast_dp.c
Normal file
282
drivers/gpu/drm/ast/ast_dp.c
Normal file
@ -0,0 +1,282 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2021, ASPEED Technology Inc.
|
||||
// Authors: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com>
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/delay.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include "ast_drv.h"
|
||||
|
||||
int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
u8 i = 0, j = 0;
|
||||
|
||||
/*
|
||||
* CRD1[b5]: DP MCU FW is executing
|
||||
* CRDC[b0]: DP link success
|
||||
* CRDF[b0]: DP HPD
|
||||
* CRE5[b0]: Host reading EDID process is done
|
||||
*/
|
||||
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING) &&
|
||||
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) &&
|
||||
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD) &&
|
||||
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
|
||||
ASTDP_HOST_EDID_READ_DONE_MASK))) {
|
||||
goto err_astdp_edid_not_ready;
|
||||
}
|
||||
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
||||
0x00);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
/*
|
||||
* CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64
|
||||
*/
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE4,
|
||||
(u8) ~ASTDP_EDID_READ_POINTER_MASK, (u8) i);
|
||||
j = 0;
|
||||
|
||||
/*
|
||||
* CRD7[b0]: valid flag for EDID
|
||||
* CRD6[b0]: mirror read pointer for EDID
|
||||
*/
|
||||
while ((ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD7,
|
||||
ASTDP_EDID_VALID_FLAG_MASK) != 0x01) ||
|
||||
(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD6,
|
||||
ASTDP_EDID_READ_POINTER_MASK) != i)) {
|
||||
/*
|
||||
* Delay are getting longer with each retry.
|
||||
* 1. The Delays are often 2 loops when users request "Display Settings"
|
||||
* of right-click of mouse.
|
||||
* 2. The Delays are often longer a lot when system resume from S3/S4.
|
||||
*/
|
||||
mdelay(j+1);
|
||||
|
||||
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1,
|
||||
ASTDP_MCU_FW_EXECUTING) &&
|
||||
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC,
|
||||
ASTDP_LINK_SUCCESS) &&
|
||||
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))) {
|
||||
goto err_astdp_jump_out_loop_of_edid;
|
||||
}
|
||||
|
||||
j++;
|
||||
if (j > 200)
|
||||
goto err_astdp_jump_out_loop_of_edid;
|
||||
}
|
||||
|
||||
*(ediddata) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT,
|
||||
0xD8, ASTDP_EDID_READ_DATA_MASK);
|
||||
*(ediddata + 1) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD9,
|
||||
ASTDP_EDID_READ_DATA_MASK);
|
||||
*(ediddata + 2) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDA,
|
||||
ASTDP_EDID_READ_DATA_MASK);
|
||||
*(ediddata + 3) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDB,
|
||||
ASTDP_EDID_READ_DATA_MASK);
|
||||
|
||||
if (i == 31) {
|
||||
/*
|
||||
* For 128-bytes EDID_1.3,
|
||||
* 1. Add the value of Bytes-126 to Bytes-127.
|
||||
* The Bytes-127 is Checksum. Sum of all 128bytes should
|
||||
* equal 0 (mod 256).
|
||||
* 2. Modify Bytes-126 to be 0.
|
||||
* The Bytes-126 indicates the Number of extensions to
|
||||
* follow. 0 represents noextensions.
|
||||
*/
|
||||
*(ediddata + 3) = *(ediddata + 3) + *(ediddata + 2);
|
||||
*(ediddata + 2) = 0;
|
||||
}
|
||||
|
||||
ediddata += 4;
|
||||
}
|
||||
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
||||
ASTDP_HOST_EDID_READ_DONE);
|
||||
|
||||
return 0;
|
||||
|
||||
err_astdp_jump_out_loop_of_edid:
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
|
||||
(u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
||||
ASTDP_HOST_EDID_READ_DONE);
|
||||
return (~(j+256) + 1);
|
||||
|
||||
err_astdp_edid_not_ready:
|
||||
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING)))
|
||||
return (~0xD1 + 1);
|
||||
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS)))
|
||||
return (~0xDC + 1);
|
||||
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)))
|
||||
return (~0xDF + 1);
|
||||
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, ASTDP_HOST_EDID_READ_DONE_MASK)))
|
||||
return (~0xE5 + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Launch Aspeed DP
|
||||
*/
|
||||
void ast_dp_launch(struct drm_device *dev, u8 bPower)
|
||||
{
|
||||
u32 i = 0, j = 0, WaitCount = 1;
|
||||
u8 bDPTX = 0;
|
||||
u8 bDPExecute = 1;
|
||||
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
// S3 come back, need more time to wait BMC ready.
|
||||
if (bPower)
|
||||
WaitCount = 300;
|
||||
|
||||
|
||||
// Wait total count by different condition.
|
||||
for (j = 0; j < WaitCount; j++) {
|
||||
bDPTX = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, TX_TYPE_MASK);
|
||||
|
||||
if (bDPTX)
|
||||
break;
|
||||
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
// 0xE : ASTDP with DPMCU FW handling
|
||||
if (bDPTX == ASTDP_DPMCU_TX) {
|
||||
// Wait one second then timeout.
|
||||
i = 0;
|
||||
|
||||
while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, COPROCESSOR_LAUNCH) !=
|
||||
COPROCESSOR_LAUNCH) {
|
||||
i++;
|
||||
// wait 100 ms
|
||||
msleep(100);
|
||||
|
||||
if (i >= 10) {
|
||||
// DP would not be ready.
|
||||
bDPExecute = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bDPExecute)
|
||||
ast->tx_chip_type = AST_TX_ASTDP;
|
||||
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
|
||||
(u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
||||
ASTDP_HOST_EDID_READ_DONE);
|
||||
} else
|
||||
ast->tx_chip_type = AST_TX_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ast_dp_power_on_off(struct drm_device *dev, bool on)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
// Read and Turn off DP PHY sleep
|
||||
u8 bE3 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, AST_DP_VIDEO_ENABLE);
|
||||
|
||||
// Turn on DP PHY sleep
|
||||
if (!on)
|
||||
bE3 |= AST_DP_PHY_SLEEP;
|
||||
|
||||
// DP Power on/off
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_PHY_SLEEP, bE3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ast_dp_set_on_off(struct drm_device *dev, bool on)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
u8 video_on_off = on;
|
||||
|
||||
// Video On/Off
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_VIDEO_ENABLE, on);
|
||||
|
||||
// If DP plug in and link successful then check video on / off status
|
||||
if (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) &&
|
||||
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)) {
|
||||
video_on_off <<= 4;
|
||||
while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF,
|
||||
ASTDP_MIRROR_VIDEO_ENABLE) != video_on_off) {
|
||||
// wait 1 ms
|
||||
mdelay(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(crtc->dev);
|
||||
|
||||
u32 ulRefreshRateIndex;
|
||||
u8 ModeIdx;
|
||||
|
||||
ulRefreshRateIndex = vbios_mode->enh_table->refresh_rate_index - 1;
|
||||
|
||||
switch (crtc->mode.crtc_hdisplay) {
|
||||
case 320:
|
||||
ModeIdx = ASTDP_320x240_60;
|
||||
break;
|
||||
case 400:
|
||||
ModeIdx = ASTDP_400x300_60;
|
||||
break;
|
||||
case 512:
|
||||
ModeIdx = ASTDP_512x384_60;
|
||||
break;
|
||||
case 640:
|
||||
ModeIdx = (ASTDP_640x480_60 + (u8) ulRefreshRateIndex);
|
||||
break;
|
||||
case 800:
|
||||
ModeIdx = (ASTDP_800x600_56 + (u8) ulRefreshRateIndex);
|
||||
break;
|
||||
case 1024:
|
||||
ModeIdx = (ASTDP_1024x768_60 + (u8) ulRefreshRateIndex);
|
||||
break;
|
||||
case 1152:
|
||||
ModeIdx = ASTDP_1152x864_75;
|
||||
break;
|
||||
case 1280:
|
||||
if (crtc->mode.crtc_vdisplay == 800)
|
||||
ModeIdx = (ASTDP_1280x800_60_RB - (u8) ulRefreshRateIndex);
|
||||
else // 1024
|
||||
ModeIdx = (ASTDP_1280x1024_60 + (u8) ulRefreshRateIndex);
|
||||
break;
|
||||
case 1360:
|
||||
case 1366:
|
||||
ModeIdx = ASTDP_1366x768_60;
|
||||
break;
|
||||
case 1440:
|
||||
ModeIdx = (ASTDP_1440x900_60_RB - (u8) ulRefreshRateIndex);
|
||||
break;
|
||||
case 1600:
|
||||
if (crtc->mode.crtc_vdisplay == 900)
|
||||
ModeIdx = (ASTDP_1600x900_60_RB - (u8) ulRefreshRateIndex);
|
||||
else //1200
|
||||
ModeIdx = ASTDP_1600x1200_60;
|
||||
break;
|
||||
case 1680:
|
||||
ModeIdx = (ASTDP_1680x1050_60_RB - (u8) ulRefreshRateIndex);
|
||||
break;
|
||||
case 1920:
|
||||
if (crtc->mode.crtc_vdisplay == 1080)
|
||||
ModeIdx = ASTDP_1920x1080_60;
|
||||
else //1200
|
||||
ModeIdx = ASTDP_1920x1200_60;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp)
|
||||
* CRE1[7:0]: MISC1 (default: 0x00)
|
||||
* CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50)
|
||||
*/
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE0, (u8) ~ASTDP_CLEAR_MASK,
|
||||
ASTDP_MISC0_24bpp);
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE1, (u8) ~ASTDP_CLEAR_MASK, ASTDP_MISC1);
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE2, (u8) ~ASTDP_CLEAR_MASK, ModeIdx);
|
||||
}
|
@ -159,15 +159,10 @@ static int ast_drm_thaw(struct drm_device *dev)
|
||||
|
||||
static int ast_drm_resume(struct drm_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pci_enable_device(to_pci_dev(dev->dev)))
|
||||
return -EIO;
|
||||
|
||||
ret = ast_drm_thaw(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
return ast_drm_thaw(dev);
|
||||
}
|
||||
|
||||
static int ast_pm_suspend(struct device *dev)
|
||||
|
@ -70,6 +70,7 @@ enum ast_tx_chip {
|
||||
AST_TX_NONE,
|
||||
AST_TX_SIL164,
|
||||
AST_TX_DP501,
|
||||
AST_TX_ASTDP,
|
||||
};
|
||||
|
||||
#define AST_DRAM_512Mx16 0
|
||||
@ -158,6 +159,7 @@ to_ast_sil164_connector(struct drm_connector *connector)
|
||||
struct ast_private {
|
||||
struct drm_device base;
|
||||
|
||||
struct mutex ioregs_lock; /* Protects access to I/O registers in ioregs */
|
||||
void __iomem *regs;
|
||||
void __iomem *ioregs;
|
||||
void __iomem *dp501_fw_buf;
|
||||
@ -184,6 +186,10 @@ struct ast_private {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
} dp501;
|
||||
struct {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
} astdp;
|
||||
} output;
|
||||
|
||||
bool support_wide_screen;
|
||||
@ -357,10 +363,113 @@ int ast_mode_config_init(struct ast_private *ast);
|
||||
#define AST_DP501_EDID_DATA 0xf020
|
||||
|
||||
/* Define for Soc scratched reg */
|
||||
#define COPROCESSOR_LAUNCH BIT(5)
|
||||
|
||||
/*
|
||||
* Display Transmitter Type:
|
||||
*/
|
||||
#define TX_TYPE_MASK GENMASK(3, 1)
|
||||
#define NO_TX (0 << 1)
|
||||
#define ITE66121_VBIOS_TX (1 << 1)
|
||||
#define SI164_VBIOS_TX (2 << 1)
|
||||
#define CH7003_VBIOS_TX (3 << 1)
|
||||
#define DP501_VBIOS_TX (4 << 1)
|
||||
#define ANX9807_VBIOS_TX (5 << 1)
|
||||
#define TX_FW_EMBEDDED_FW_TX (6 << 1)
|
||||
#define ASTDP_DPMCU_TX (7 << 1)
|
||||
|
||||
#define AST_VRAM_INIT_STATUS_MASK GENMASK(7, 6)
|
||||
//#define AST_VRAM_INIT_BY_BMC BIT(7)
|
||||
//#define AST_VRAM_INIT_READY BIT(6)
|
||||
|
||||
/* Define for Soc scratched reg used on ASTDP */
|
||||
#define AST_DP_PHY_SLEEP BIT(4)
|
||||
#define AST_DP_VIDEO_ENABLE BIT(0)
|
||||
|
||||
#define AST_DP_POWER_ON true
|
||||
#define AST_DP_POWER_OFF false
|
||||
|
||||
/*
|
||||
* CRD1[b5]: DP MCU FW is executing
|
||||
* CRDC[b0]: DP link success
|
||||
* CRDF[b0]: DP HPD
|
||||
* CRE5[b0]: Host reading EDID process is done
|
||||
*/
|
||||
#define ASTDP_MCU_FW_EXECUTING BIT(5)
|
||||
#define ASTDP_LINK_SUCCESS BIT(0)
|
||||
#define ASTDP_HPD BIT(0)
|
||||
#define ASTDP_HOST_EDID_READ_DONE BIT(0)
|
||||
#define ASTDP_HOST_EDID_READ_DONE_MASK GENMASK(0, 0)
|
||||
|
||||
/*
|
||||
* CRB8[b1]: Enable VSYNC off
|
||||
* CRB8[b0]: Enable HSYNC off
|
||||
*/
|
||||
#define AST_DPMS_VSYNC_OFF BIT(1)
|
||||
#define AST_DPMS_HSYNC_OFF BIT(0)
|
||||
|
||||
/*
|
||||
* CRDF[b4]: Mirror of AST_DP_VIDEO_ENABLE
|
||||
* Precondition: A. ~AST_DP_PHY_SLEEP &&
|
||||
* B. DP_HPD &&
|
||||
* C. DP_LINK_SUCCESS
|
||||
*/
|
||||
#define ASTDP_MIRROR_VIDEO_ENABLE BIT(4)
|
||||
|
||||
#define ASTDP_EDID_READ_POINTER_MASK GENMASK(7, 0)
|
||||
#define ASTDP_EDID_VALID_FLAG_MASK GENMASK(0, 0)
|
||||
#define ASTDP_EDID_READ_DATA_MASK GENMASK(7, 0)
|
||||
|
||||
/*
|
||||
* ASTDP setmode registers:
|
||||
* CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp)
|
||||
* CRE1[7:0]: MISC1 (default: 0x00)
|
||||
* CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50)
|
||||
*/
|
||||
#define ASTDP_MISC0_24bpp BIT(5)
|
||||
#define ASTDP_MISC1 0
|
||||
#define ASTDP_CLEAR_MASK GENMASK(7, 0)
|
||||
|
||||
/*
|
||||
* ASTDP resoultion table:
|
||||
* EX: ASTDP_A_B_C:
|
||||
* A: Resolution
|
||||
* B: Refresh Rate
|
||||
* C: Misc information, such as CVT, Reduce Blanked
|
||||
*/
|
||||
#define ASTDP_640x480_60 0x00
|
||||
#define ASTDP_640x480_72 0x01
|
||||
#define ASTDP_640x480_75 0x02
|
||||
#define ASTDP_640x480_85 0x03
|
||||
#define ASTDP_800x600_56 0x04
|
||||
#define ASTDP_800x600_60 0x05
|
||||
#define ASTDP_800x600_72 0x06
|
||||
#define ASTDP_800x600_75 0x07
|
||||
#define ASTDP_800x600_85 0x08
|
||||
#define ASTDP_1024x768_60 0x09
|
||||
#define ASTDP_1024x768_70 0x0A
|
||||
#define ASTDP_1024x768_75 0x0B
|
||||
#define ASTDP_1024x768_85 0x0C
|
||||
#define ASTDP_1280x1024_60 0x0D
|
||||
#define ASTDP_1280x1024_75 0x0E
|
||||
#define ASTDP_1280x1024_85 0x0F
|
||||
#define ASTDP_1600x1200_60 0x10
|
||||
#define ASTDP_320x240_60 0x11
|
||||
#define ASTDP_400x300_60 0x12
|
||||
#define ASTDP_512x384_60 0x13
|
||||
#define ASTDP_1920x1200_60 0x14
|
||||
#define ASTDP_1920x1080_60 0x15
|
||||
#define ASTDP_1280x800_60 0x16
|
||||
#define ASTDP_1280x800_60_RB 0x17
|
||||
#define ASTDP_1440x900_60 0x18
|
||||
#define ASTDP_1440x900_60_RB 0x19
|
||||
#define ASTDP_1680x1050_60 0x1A
|
||||
#define ASTDP_1680x1050_60_RB 0x1B
|
||||
#define ASTDP_1600x900_60 0x1C
|
||||
#define ASTDP_1600x900_60_RB 0x1D
|
||||
#define ASTDP_1366x768_60 0x1E
|
||||
#define ASTDP_1152x864_75 0x1F
|
||||
|
||||
int ast_mm_init(struct ast_private *ast);
|
||||
|
||||
/* ast post */
|
||||
@ -381,4 +490,11 @@ void ast_init_3rdtx(struct drm_device *dev);
|
||||
/* ast_i2c.c */
|
||||
struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev);
|
||||
|
||||
/* aspeed DP */
|
||||
int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata);
|
||||
void ast_dp_launch(struct drm_device *dev, u8 bPower);
|
||||
void ast_dp_power_on_off(struct drm_device *dev, bool no);
|
||||
void ast_dp_set_on_off(struct drm_device *dev, bool no);
|
||||
void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode);
|
||||
|
||||
#endif
|
||||
|
@ -232,7 +232,7 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
|
||||
ast->tx_chip_type = AST_TX_SIL164;
|
||||
}
|
||||
|
||||
if ((ast->chip == AST2300) || (ast->chip == AST2400)) {
|
||||
if ((ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST2500)) {
|
||||
/*
|
||||
* On AST2300 and 2400, look the configuration set by the SoC in
|
||||
* the SOC scratch register #1 bits 11:8 (interestingly marked
|
||||
@ -256,7 +256,8 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post)
|
||||
case 0x0c:
|
||||
ast->tx_chip_type = AST_TX_DP501;
|
||||
}
|
||||
}
|
||||
} else if (ast->chip == AST2600)
|
||||
ast_dp_launch(&ast->base, 0);
|
||||
|
||||
/* Print stuff for diagnostic purposes */
|
||||
switch(ast->tx_chip_type) {
|
||||
@ -420,6 +421,10 @@ struct ast_private *ast_device_create(const struct drm_driver *drv,
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
ret = drmm_mutex_init(dev, &ast->ioregs_lock);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ast->regs = pcim_iomap(pdev, 1, 0);
|
||||
if (!ast->regs)
|
||||
return ERR_PTR(-EIO);
|
||||
|
@ -988,21 +988,41 @@ err_drm_gem_vram_put:
|
||||
static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct ast_private *ast = to_ast_private(crtc->dev);
|
||||
u8 ch = AST_DPMS_VSYNC_OFF | AST_DPMS_HSYNC_OFF;
|
||||
|
||||
/* TODO: Maybe control display signal generation with
|
||||
* Sync Enable (bit CR17.7).
|
||||
*/
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x01, 0xdf, 0);
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xfc, 0);
|
||||
if (ast->tx_chip_type == AST_TX_DP501)
|
||||
ast_set_dp501_video_output(crtc->dev, 1);
|
||||
|
||||
if (ast->tx_chip_type == AST_TX_ASTDP) {
|
||||
ast_dp_power_on_off(crtc->dev, AST_DP_POWER_ON);
|
||||
ast_wait_for_vretrace(ast);
|
||||
ast_dp_set_on_off(crtc->dev, 1);
|
||||
}
|
||||
|
||||
ast_crtc_load_lut(ast, crtc);
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
ch = mode;
|
||||
if (ast->tx_chip_type == AST_TX_DP501)
|
||||
ast_set_dp501_video_output(crtc->dev, 0);
|
||||
break;
|
||||
|
||||
if (ast->tx_chip_type == AST_TX_ASTDP) {
|
||||
ast_dp_set_on_off(crtc->dev, 0);
|
||||
ast_dp_power_on_off(crtc->dev, AST_DP_POWER_OFF);
|
||||
}
|
||||
|
||||
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x01, 0xdf, 0x20);
|
||||
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xfc, ch);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1027,7 +1047,7 @@ ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode
|
||||
|
||||
if ((ast->chip == AST2100) || (ast->chip == AST2200) ||
|
||||
(ast->chip == AST2300) || (ast->chip == AST2400) ||
|
||||
(ast->chip == AST2500)) {
|
||||
(ast->chip == AST2500) || (ast->chip == AST2600)) {
|
||||
if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080))
|
||||
return MODE_OK;
|
||||
|
||||
@ -1099,6 +1119,20 @@ static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ast_crtc_helper_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
|
||||
/*
|
||||
* Concurrent operations could possibly trigger a call to
|
||||
* drm_connector_helper_funcs.get_modes by trying to read the
|
||||
* display modes. Protect access to I/O registers by acquiring
|
||||
* the I/O-register lock. Released in atomic_flush().
|
||||
*/
|
||||
mutex_lock(&ast->ioregs_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
@ -1107,9 +1141,11 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
|
||||
crtc);
|
||||
struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
|
||||
crtc);
|
||||
struct ast_private *ast = to_ast_private(crtc->dev);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
|
||||
struct ast_crtc_state *old_ast_crtc_state = to_ast_crtc_state(old_crtc_state);
|
||||
struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info;
|
||||
|
||||
/*
|
||||
* The gamma LUT has to be reloaded after changing the primary
|
||||
@ -1117,6 +1153,12 @@ ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
|
||||
*/
|
||||
if (old_ast_crtc_state->format != ast_crtc_state->format)
|
||||
ast_crtc_load_lut(ast, crtc);
|
||||
|
||||
//Set Aspeed Display-Port
|
||||
if (ast->tx_chip_type == AST_TX_ASTDP)
|
||||
ast_dp_set_mode(crtc, vbios_mode_info);
|
||||
|
||||
mutex_unlock(&ast->ioregs_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1175,6 +1217,7 @@ ast_crtc_helper_atomic_disable(struct drm_crtc *crtc,
|
||||
static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = {
|
||||
.mode_valid = ast_crtc_helper_mode_valid,
|
||||
.atomic_check = ast_crtc_helper_atomic_check,
|
||||
.atomic_begin = ast_crtc_helper_atomic_begin,
|
||||
.atomic_flush = ast_crtc_helper_atomic_flush,
|
||||
.atomic_enable = ast_crtc_helper_atomic_enable,
|
||||
.atomic_disable = ast_crtc_helper_atomic_disable,
|
||||
@ -1260,21 +1303,33 @@ static int ast_crtc_init(struct drm_device *dev)
|
||||
static int ast_vga_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct ast_vga_connector *ast_vga_connector = to_ast_vga_connector(connector);
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
struct edid *edid;
|
||||
int count;
|
||||
|
||||
if (!ast_vga_connector->i2c)
|
||||
goto err_drm_connector_update_edid_property;
|
||||
|
||||
/*
|
||||
* Protect access to I/O registers from concurrent modesetting
|
||||
* by acquiring the I/O-register lock.
|
||||
*/
|
||||
mutex_lock(&ast->ioregs_lock);
|
||||
|
||||
edid = drm_get_edid(connector, &ast_vga_connector->i2c->adapter);
|
||||
if (!edid)
|
||||
goto err_drm_connector_update_edid_property;
|
||||
goto err_mutex_unlock;
|
||||
|
||||
mutex_unlock(&ast->ioregs_lock);
|
||||
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return count;
|
||||
|
||||
err_mutex_unlock:
|
||||
mutex_unlock(&ast->ioregs_lock);
|
||||
err_drm_connector_update_edid_property:
|
||||
drm_connector_update_edid_property(connector, NULL);
|
||||
return 0;
|
||||
@ -1354,21 +1409,33 @@ static int ast_vga_output_init(struct ast_private *ast)
|
||||
static int ast_sil164_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct ast_sil164_connector *ast_sil164_connector = to_ast_sil164_connector(connector);
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct ast_private *ast = to_ast_private(dev);
|
||||
struct edid *edid;
|
||||
int count;
|
||||
|
||||
if (!ast_sil164_connector->i2c)
|
||||
goto err_drm_connector_update_edid_property;
|
||||
|
||||
/*
|
||||
* Protect access to I/O registers from concurrent modesetting
|
||||
* by acquiring the I/O-register lock.
|
||||
*/
|
||||
mutex_lock(&ast->ioregs_lock);
|
||||
|
||||
edid = drm_get_edid(connector, &ast_sil164_connector->i2c->adapter);
|
||||
if (!edid)
|
||||
goto err_drm_connector_update_edid_property;
|
||||
goto err_mutex_unlock;
|
||||
|
||||
mutex_unlock(&ast->ioregs_lock);
|
||||
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return count;
|
||||
|
||||
err_mutex_unlock:
|
||||
mutex_unlock(&ast->ioregs_lock);
|
||||
err_drm_connector_update_edid_property:
|
||||
drm_connector_update_edid_property(connector, NULL);
|
||||
return 0;
|
||||
@ -1527,6 +1594,93 @@ static int ast_dp501_output_init(struct ast_private *ast)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ASPEED Display-Port Connector
|
||||
*/
|
||||
|
||||
static int ast_astdp_connector_helper_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
void *edid;
|
||||
|
||||
int succ;
|
||||
int count;
|
||||
|
||||
edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
||||
if (!edid)
|
||||
goto err_drm_connector_update_edid_property;
|
||||
|
||||
succ = ast_astdp_read_edid(connector->dev, edid);
|
||||
if (succ < 0)
|
||||
goto err_kfree;
|
||||
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return count;
|
||||
|
||||
err_kfree:
|
||||
kfree(edid);
|
||||
err_drm_connector_update_edid_property:
|
||||
drm_connector_update_edid_property(connector, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs ast_astdp_connector_helper_funcs = {
|
||||
.get_modes = ast_astdp_connector_helper_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs ast_astdp_connector_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static int ast_astdp_connector_init(struct drm_device *dev, struct drm_connector *connector)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = drm_connector_init(dev, connector, &ast_astdp_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &ast_astdp_connector_helper_funcs);
|
||||
|
||||
connector->interlace_allowed = 0;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_astdp_output_init(struct ast_private *ast)
|
||||
{
|
||||
struct drm_device *dev = &ast->base;
|
||||
struct drm_crtc *crtc = &ast->crtc;
|
||||
struct drm_encoder *encoder = &ast->output.astdp.encoder;
|
||||
struct drm_connector *connector = &ast->output.astdp.connector;
|
||||
int ret;
|
||||
|
||||
ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_TMDS);
|
||||
if (ret)
|
||||
return ret;
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
|
||||
ret = ast_astdp_connector_init(dev, connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_connector_attach_encoder(connector, encoder);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mode config
|
||||
*/
|
||||
@ -1563,7 +1717,8 @@ int ast_mode_config_init(struct ast_private *ast)
|
||||
ast->chip == AST2200 ||
|
||||
ast->chip == AST2300 ||
|
||||
ast->chip == AST2400 ||
|
||||
ast->chip == AST2500) {
|
||||
ast->chip == AST2500 ||
|
||||
ast->chip == AST2600) {
|
||||
dev->mode_config.max_width = 1920;
|
||||
dev->mode_config.max_height = 2048;
|
||||
} else {
|
||||
@ -1594,6 +1749,9 @@ int ast_mode_config_init(struct ast_private *ast)
|
||||
case AST_TX_DP501:
|
||||
ret = ast_dp501_output_init(ast);
|
||||
break;
|
||||
case AST_TX_ASTDP:
|
||||
ret = ast_astdp_output_init(ast);
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -379,7 +379,9 @@ void ast_post_gpu(struct drm_device *dev)
|
||||
ast_enable_mmio(dev);
|
||||
ast_set_def_ext_reg(dev);
|
||||
|
||||
if (ast->config_mode == ast_use_p2a) {
|
||||
if (ast->chip == AST2600) {
|
||||
ast_dp_launch(dev, 1);
|
||||
} else if (ast->config_mode == ast_use_p2a) {
|
||||
if (ast->chip == AST2500)
|
||||
ast_post_chip_2500(dev);
|
||||
else if (ast->chip == AST2300 || ast->chip == AST2400)
|
||||
|
@ -75,6 +75,14 @@ config DRM_DISPLAY_CONNECTOR
|
||||
on ARM-based platforms. Saying Y here when this driver is not needed
|
||||
will not cause any issue.
|
||||
|
||||
config DRM_FSL_LDB
|
||||
tristate "Freescale i.MX8MP LDB bridge"
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_PANEL_BRIDGE
|
||||
help
|
||||
Support for i.MX8MP DPI-to-LVDS on-SoC encoder.
|
||||
|
||||
config DRM_ITE_IT6505
|
||||
tristate "ITE IT6505 DisplayPort bridge"
|
||||
depends on OF
|
||||
|
@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_CHIPONE_ICN6211) += chipone-icn6211.o
|
||||
obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
|
||||
obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
|
||||
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
|
||||
obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o
|
||||
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
|
||||
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
|
||||
obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
|
||||
|
@ -209,10 +209,16 @@
|
||||
#define ADV7511_REG_CEC_TX_ENABLE 0x11
|
||||
#define ADV7511_REG_CEC_TX_RETRY 0x12
|
||||
#define ADV7511_REG_CEC_TX_LOW_DRV_CNT 0x14
|
||||
#define ADV7511_REG_CEC_RX_FRAME_HDR 0x15
|
||||
#define ADV7511_REG_CEC_RX_FRAME_DATA0 0x16
|
||||
#define ADV7511_REG_CEC_RX_FRAME_LEN 0x25
|
||||
#define ADV7511_REG_CEC_RX_ENABLE 0x26
|
||||
#define ADV7511_REG_CEC_RX1_FRAME_HDR 0x15
|
||||
#define ADV7511_REG_CEC_RX1_FRAME_DATA0 0x16
|
||||
#define ADV7511_REG_CEC_RX1_FRAME_LEN 0x25
|
||||
#define ADV7511_REG_CEC_RX_STATUS 0x26
|
||||
#define ADV7511_REG_CEC_RX2_FRAME_HDR 0x27
|
||||
#define ADV7511_REG_CEC_RX2_FRAME_DATA0 0x28
|
||||
#define ADV7511_REG_CEC_RX2_FRAME_LEN 0x37
|
||||
#define ADV7511_REG_CEC_RX3_FRAME_HDR 0x38
|
||||
#define ADV7511_REG_CEC_RX3_FRAME_DATA0 0x39
|
||||
#define ADV7511_REG_CEC_RX3_FRAME_LEN 0x48
|
||||
#define ADV7511_REG_CEC_RX_BUFFERS 0x4a
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_MASK 0x4b
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_0_1 0x4c
|
||||
@ -220,6 +226,18 @@
|
||||
#define ADV7511_REG_CEC_CLK_DIV 0x4e
|
||||
#define ADV7511_REG_CEC_SOFT_RESET 0x50
|
||||
|
||||
static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = {
|
||||
ADV7511_REG_CEC_RX1_FRAME_HDR,
|
||||
ADV7511_REG_CEC_RX2_FRAME_HDR,
|
||||
ADV7511_REG_CEC_RX3_FRAME_HDR,
|
||||
};
|
||||
|
||||
static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = {
|
||||
ADV7511_REG_CEC_RX1_FRAME_LEN,
|
||||
ADV7511_REG_CEC_RX2_FRAME_LEN,
|
||||
ADV7511_REG_CEC_RX3_FRAME_LEN,
|
||||
};
|
||||
|
||||
#define ADV7533_REG_CEC_OFFSET 0x70
|
||||
|
||||
enum adv7511_input_clock {
|
||||
@ -335,6 +353,7 @@ struct adv7511 {
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regmap *regmap_cec;
|
||||
unsigned int reg_cec_offset;
|
||||
enum drm_connector_status status;
|
||||
bool powered;
|
||||
|
||||
|
@ -17,12 +17,12 @@
|
||||
|
||||
#define ADV7511_INT1_CEC_MASK \
|
||||
(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1)
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1 | \
|
||||
ADV7511_INT1_CEC_RX_READY2 | ADV7511_INT1_CEC_RX_READY3)
|
||||
|
||||
static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
|
||||
{
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int offset = adv7511->reg_cec_offset;
|
||||
unsigned int val;
|
||||
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
@ -71,26 +71,16 @@ static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
|
||||
}
|
||||
}
|
||||
|
||||
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
|
||||
static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf)
|
||||
{
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
|
||||
ADV7511_INT1_CEC_TX_ARBIT_LOST |
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
|
||||
unsigned int offset = adv7511->reg_cec_offset;
|
||||
struct cec_msg msg = {};
|
||||
unsigned int len;
|
||||
unsigned int val;
|
||||
u8 i;
|
||||
|
||||
if (irq1 & irq_tx_mask)
|
||||
adv_cec_tx_raw_status(adv7511, irq1);
|
||||
|
||||
if (!(irq1 & ADV7511_INT1_CEC_RX_READY1))
|
||||
return;
|
||||
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_FRAME_LEN + offset, &len))
|
||||
ADV7511_REG_CEC_RX_FRAME_LEN[rx_buf] + offset, &len))
|
||||
return;
|
||||
|
||||
msg.len = len & 0x1f;
|
||||
@ -103,23 +93,80 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
|
||||
|
||||
for (i = 0; i < msg.len; i++) {
|
||||
regmap_read(adv7511->regmap_cec,
|
||||
i + ADV7511_REG_CEC_RX_FRAME_HDR + offset, &val);
|
||||
i + ADV7511_REG_CEC_RX_FRAME_HDR[rx_buf] + offset,
|
||||
&val);
|
||||
msg.msg[i] = val;
|
||||
}
|
||||
|
||||
/* toggle to re-enable rx 1 */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 1);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
|
||||
/* Toggle RX Ready Clear bit to re-enable this RX buffer */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf),
|
||||
BIT(rx_buf));
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf), 0);
|
||||
|
||||
cec_received_msg(adv7511->cec_adap, &msg);
|
||||
}
|
||||
|
||||
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
|
||||
{
|
||||
unsigned int offset = adv7511->reg_cec_offset;
|
||||
const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
|
||||
ADV7511_INT1_CEC_TX_ARBIT_LOST |
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
|
||||
const u32 irq_rx_mask = ADV7511_INT1_CEC_RX_READY1 |
|
||||
ADV7511_INT1_CEC_RX_READY2 |
|
||||
ADV7511_INT1_CEC_RX_READY3;
|
||||
unsigned int rx_status;
|
||||
int rx_order[3] = { -1, -1, -1 };
|
||||
int i;
|
||||
|
||||
if (irq1 & irq_tx_mask)
|
||||
adv_cec_tx_raw_status(adv7511, irq1);
|
||||
|
||||
if (!(irq1 & irq_rx_mask))
|
||||
return;
|
||||
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_STATUS + offset, &rx_status))
|
||||
return;
|
||||
|
||||
/*
|
||||
* ADV7511_REG_CEC_RX_STATUS[5:0] contains the reception order of RX
|
||||
* buffers 0, 1, and 2 in bits [1:0], [3:2], and [5:4] respectively.
|
||||
* The values are to be interpreted as follows:
|
||||
*
|
||||
* 0 = buffer unused
|
||||
* 1 = buffer contains oldest received frame (if applicable)
|
||||
* 2 = buffer contains second oldest received frame (if applicable)
|
||||
* 3 = buffer contains third oldest received frame (if applicable)
|
||||
*
|
||||
* Fill rx_order with the sequence of RX buffer indices to
|
||||
* read from in order, where -1 indicates that there are no
|
||||
* more buffers to process.
|
||||
*/
|
||||
for (i = 0; i < 3; i++) {
|
||||
unsigned int timestamp = (rx_status >> (2 * i)) & 0x3;
|
||||
|
||||
if (timestamp)
|
||||
rx_order[timestamp - 1] = i;
|
||||
}
|
||||
|
||||
/* Read CEC RX buffers in the appropriate order as prescribed above */
|
||||
for (i = 0; i < 3; i++) {
|
||||
int rx_buf = rx_order[i];
|
||||
|
||||
if (rx_buf < 0)
|
||||
break;
|
||||
|
||||
adv7511_cec_rx(adv7511, rx_buf);
|
||||
}
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int offset = adv7511->reg_cec_offset;
|
||||
|
||||
if (adv7511->i2c_cec == NULL)
|
||||
return -EIO;
|
||||
@ -129,11 +176,11 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
0x03, 0x01);
|
||||
/* legacy mode and clear all rx buffers */
|
||||
/* non-legacy mode and clear all rx buffers */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x07);
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x0f);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);
|
||||
/* initially disable tx */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
|
||||
@ -141,7 +188,7 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready 1 */
|
||||
/* rx: ready 1-3 */
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1), 0x3f,
|
||||
ADV7511_INT1_CEC_MASK);
|
||||
@ -165,8 +212,7 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int offset = adv7511->reg_cec_offset;
|
||||
unsigned int i, free_idx = ADV7511_MAX_ADDRS;
|
||||
|
||||
if (!adv7511->cec_enabled_adap)
|
||||
@ -235,8 +281,7 @@ static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int offset = adv7511->reg_cec_offset;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
@ -289,8 +334,7 @@ static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
|
||||
|
||||
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
|
||||
{
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int offset = adv7511->reg_cec_offset;
|
||||
int ret = adv7511_cec_parse_dt(dev, adv7511);
|
||||
|
||||
if (ret)
|
||||
@ -310,9 +354,9 @@ int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
|
||||
|
||||
/* legacy mode */
|
||||
/* non-legacy mode - use all three RX buffers */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x00);
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);
|
||||
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
|
@ -1027,14 +1027,19 @@ static bool adv7511_cec_register_volatile(struct device *dev, unsigned int reg)
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (adv7511->type == ADV7533 || adv7511->type == ADV7535)
|
||||
reg -= ADV7533_REG_CEC_OFFSET;
|
||||
reg -= adv7511->reg_cec_offset;
|
||||
|
||||
switch (reg) {
|
||||
case ADV7511_REG_CEC_RX_FRAME_HDR:
|
||||
case ADV7511_REG_CEC_RX_FRAME_DATA0...
|
||||
ADV7511_REG_CEC_RX_FRAME_DATA0 + 14:
|
||||
case ADV7511_REG_CEC_RX_FRAME_LEN:
|
||||
case ADV7511_REG_CEC_RX1_FRAME_HDR:
|
||||
case ADV7511_REG_CEC_RX1_FRAME_DATA0 ... ADV7511_REG_CEC_RX1_FRAME_DATA0 + 14:
|
||||
case ADV7511_REG_CEC_RX1_FRAME_LEN:
|
||||
case ADV7511_REG_CEC_RX2_FRAME_HDR:
|
||||
case ADV7511_REG_CEC_RX2_FRAME_DATA0 ... ADV7511_REG_CEC_RX2_FRAME_DATA0 + 14:
|
||||
case ADV7511_REG_CEC_RX2_FRAME_LEN:
|
||||
case ADV7511_REG_CEC_RX3_FRAME_HDR:
|
||||
case ADV7511_REG_CEC_RX3_FRAME_DATA0 ... ADV7511_REG_CEC_RX3_FRAME_DATA0 + 14:
|
||||
case ADV7511_REG_CEC_RX3_FRAME_LEN:
|
||||
case ADV7511_REG_CEC_RX_STATUS:
|
||||
case ADV7511_REG_CEC_RX_BUFFERS:
|
||||
case ADV7511_REG_CEC_TX_LOW_DRV_CNT:
|
||||
return true;
|
||||
@ -1073,6 +1078,8 @@ static int adv7511_init_cec_regmap(struct adv7511 *adv)
|
||||
ret = adv7533_patch_cec_registers(adv);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
adv->reg_cec_offset = ADV7533_REG_CEC_OFFSET;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
342
drivers/gpu/drm/bridge/fsl-ldb.c
Normal file
342
drivers/gpu/drm/bridge/fsl-ldb.c
Normal file
@ -0,0 +1,342 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2022 Marek Vasut <marex@denx.de>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
||||
#define LDB_CTRL 0x5c
|
||||
#define LDB_CTRL_CH0_ENABLE BIT(0)
|
||||
#define LDB_CTRL_CH0_DI_SELECT BIT(1)
|
||||
#define LDB_CTRL_CH1_ENABLE BIT(2)
|
||||
#define LDB_CTRL_CH1_DI_SELECT BIT(3)
|
||||
#define LDB_CTRL_SPLIT_MODE BIT(4)
|
||||
#define LDB_CTRL_CH0_DATA_WIDTH BIT(5)
|
||||
#define LDB_CTRL_CH0_BIT_MAPPING BIT(6)
|
||||
#define LDB_CTRL_CH1_DATA_WIDTH BIT(7)
|
||||
#define LDB_CTRL_CH1_BIT_MAPPING BIT(8)
|
||||
#define LDB_CTRL_DI0_VSYNC_POLARITY BIT(9)
|
||||
#define LDB_CTRL_DI1_VSYNC_POLARITY BIT(10)
|
||||
#define LDB_CTRL_REG_CH0_FIFO_RESET BIT(11)
|
||||
#define LDB_CTRL_REG_CH1_FIFO_RESET BIT(12)
|
||||
#define LDB_CTRL_ASYNC_FIFO_ENABLE BIT(24)
|
||||
#define LDB_CTRL_ASYNC_FIFO_THRESHOLD_MASK GENMASK(27, 25)
|
||||
|
||||
#define LVDS_CTRL 0x128
|
||||
#define LVDS_CTRL_CH0_EN BIT(0)
|
||||
#define LVDS_CTRL_CH1_EN BIT(1)
|
||||
#define LVDS_CTRL_VBG_EN BIT(2)
|
||||
#define LVDS_CTRL_HS_EN BIT(3)
|
||||
#define LVDS_CTRL_PRE_EMPH_EN BIT(4)
|
||||
#define LVDS_CTRL_PRE_EMPH_ADJ(n) (((n) & 0x7) << 5)
|
||||
#define LVDS_CTRL_PRE_EMPH_ADJ_MASK GENMASK(7, 5)
|
||||
#define LVDS_CTRL_CM_ADJ(n) (((n) & 0x7) << 8)
|
||||
#define LVDS_CTRL_CM_ADJ_MASK GENMASK(10, 8)
|
||||
#define LVDS_CTRL_CC_ADJ(n) (((n) & 0x7) << 11)
|
||||
#define LVDS_CTRL_CC_ADJ_MASK GENMASK(13, 11)
|
||||
#define LVDS_CTRL_SLEW_ADJ(n) (((n) & 0x7) << 14)
|
||||
#define LVDS_CTRL_SLEW_ADJ_MASK GENMASK(16, 14)
|
||||
#define LVDS_CTRL_VBG_ADJ(n) (((n) & 0x7) << 17)
|
||||
#define LVDS_CTRL_VBG_ADJ_MASK GENMASK(19, 17)
|
||||
|
||||
struct fsl_ldb {
|
||||
struct device *dev;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *panel_bridge;
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
bool lvds_dual_link;
|
||||
};
|
||||
|
||||
static inline struct fsl_ldb *to_fsl_ldb(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct fsl_ldb, bridge);
|
||||
}
|
||||
|
||||
static int fsl_ldb_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
|
||||
|
||||
return drm_bridge_attach(bridge->encoder, fsl_ldb->panel_bridge,
|
||||
bridge, flags);
|
||||
}
|
||||
|
||||
static int fsl_ldb_atomic_check(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
/* Invert DE signal polarity. */
|
||||
bridge_state->input_bus_cfg.flags &= ~(DRM_BUS_FLAG_DE_LOW |
|
||||
DRM_BUS_FLAG_DE_HIGH);
|
||||
if (bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
|
||||
bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH;
|
||||
else if (bridge_state->output_bus_cfg.flags & DRM_BUS_FLAG_DE_HIGH)
|
||||
bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_LOW;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_ldb_atomic_enable(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *old_bridge_state)
|
||||
{
|
||||
struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
|
||||
struct drm_atomic_state *state = old_bridge_state->base.state;
|
||||
const struct drm_bridge_state *bridge_state;
|
||||
const struct drm_crtc_state *crtc_state;
|
||||
const struct drm_display_mode *mode;
|
||||
struct drm_connector *connector;
|
||||
struct drm_crtc *crtc;
|
||||
bool lvds_format_24bpp;
|
||||
bool lvds_format_jeida;
|
||||
u32 reg;
|
||||
|
||||
/* Get the LVDS format from the bridge state. */
|
||||
bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
|
||||
|
||||
switch (bridge_state->output_bus_cfg.format) {
|
||||
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
|
||||
lvds_format_24bpp = false;
|
||||
lvds_format_jeida = true;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
|
||||
lvds_format_24bpp = true;
|
||||
lvds_format_jeida = true;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
|
||||
lvds_format_24bpp = true;
|
||||
lvds_format_jeida = false;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Some bridges still don't set the correct LVDS bus pixel
|
||||
* format, use SPWG24 default format until those are fixed.
|
||||
*/
|
||||
lvds_format_24bpp = true;
|
||||
lvds_format_jeida = false;
|
||||
dev_warn(fsl_ldb->dev,
|
||||
"Unsupported LVDS bus format 0x%04x, please check output bridge driver. Falling back to SPWG24.\n",
|
||||
bridge_state->output_bus_cfg.format);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the CRTC adjusted mode. This requires a little dance to go
|
||||
* from the bridge to the encoder, to the connector and to the CRTC.
|
||||
*/
|
||||
connector = drm_atomic_get_new_connector_for_encoder(state,
|
||||
bridge->encoder);
|
||||
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
mode = &crtc_state->adjusted_mode;
|
||||
|
||||
if (fsl_ldb->lvds_dual_link)
|
||||
clk_set_rate(fsl_ldb->clk, mode->clock * 3500);
|
||||
else
|
||||
clk_set_rate(fsl_ldb->clk, mode->clock * 7000);
|
||||
clk_prepare_enable(fsl_ldb->clk);
|
||||
|
||||
/* Program LDB_CTRL */
|
||||
reg = LDB_CTRL_CH0_ENABLE;
|
||||
|
||||
if (fsl_ldb->lvds_dual_link)
|
||||
reg |= LDB_CTRL_CH1_ENABLE;
|
||||
|
||||
if (lvds_format_24bpp) {
|
||||
reg |= LDB_CTRL_CH0_DATA_WIDTH;
|
||||
if (fsl_ldb->lvds_dual_link)
|
||||
reg |= LDB_CTRL_CH1_DATA_WIDTH;
|
||||
}
|
||||
|
||||
if (lvds_format_jeida) {
|
||||
reg |= LDB_CTRL_CH0_BIT_MAPPING;
|
||||
if (fsl_ldb->lvds_dual_link)
|
||||
reg |= LDB_CTRL_CH1_BIT_MAPPING;
|
||||
}
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_PVSYNC) {
|
||||
reg |= LDB_CTRL_DI0_VSYNC_POLARITY;
|
||||
if (fsl_ldb->lvds_dual_link)
|
||||
reg |= LDB_CTRL_DI1_VSYNC_POLARITY;
|
||||
}
|
||||
|
||||
regmap_write(fsl_ldb->regmap, LDB_CTRL, reg);
|
||||
|
||||
/* Program LVDS_CTRL */
|
||||
reg = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN |
|
||||
LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN;
|
||||
regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg);
|
||||
|
||||
/* Wait for VBG to stabilize. */
|
||||
usleep_range(15, 20);
|
||||
|
||||
reg |= LVDS_CTRL_CH0_EN;
|
||||
if (fsl_ldb->lvds_dual_link)
|
||||
reg |= LVDS_CTRL_CH1_EN;
|
||||
|
||||
regmap_write(fsl_ldb->regmap, LVDS_CTRL, reg);
|
||||
}
|
||||
|
||||
static void fsl_ldb_atomic_disable(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *old_bridge_state)
|
||||
{
|
||||
struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
|
||||
|
||||
/* Stop both channels. */
|
||||
regmap_write(fsl_ldb->regmap, LVDS_CTRL, 0);
|
||||
regmap_write(fsl_ldb->regmap, LDB_CTRL, 0);
|
||||
|
||||
clk_disable_unprepare(fsl_ldb->clk);
|
||||
}
|
||||
|
||||
#define MAX_INPUT_SEL_FORMATS 1
|
||||
static u32 *
|
||||
fsl_ldb_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state,
|
||||
u32 output_fmt,
|
||||
unsigned int *num_input_fmts)
|
||||
{
|
||||
u32 *input_fmts;
|
||||
|
||||
*num_input_fmts = 0;
|
||||
|
||||
input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
|
||||
GFP_KERNEL);
|
||||
if (!input_fmts)
|
||||
return NULL;
|
||||
|
||||
input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
*num_input_fmts = MAX_INPUT_SEL_FORMATS;
|
||||
|
||||
return input_fmts;
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
fsl_ldb_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_info *info,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge);
|
||||
|
||||
if (mode->clock > (fsl_ldb->lvds_dual_link ? 80000 : 160000))
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs funcs = {
|
||||
.attach = fsl_ldb_attach,
|
||||
.atomic_check = fsl_ldb_atomic_check,
|
||||
.atomic_enable = fsl_ldb_atomic_enable,
|
||||
.atomic_disable = fsl_ldb_atomic_disable,
|
||||
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
|
||||
.atomic_get_input_bus_fmts = fsl_ldb_atomic_get_input_bus_fmts,
|
||||
.atomic_reset = drm_atomic_helper_bridge_reset,
|
||||
.mode_valid = fsl_ldb_mode_valid,
|
||||
};
|
||||
|
||||
static int fsl_ldb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *panel_node;
|
||||
struct device_node *port1, *port2;
|
||||
struct drm_panel *panel;
|
||||
struct fsl_ldb *fsl_ldb;
|
||||
int dual_link;
|
||||
|
||||
fsl_ldb = devm_kzalloc(dev, sizeof(*fsl_ldb), GFP_KERNEL);
|
||||
if (!fsl_ldb)
|
||||
return -ENOMEM;
|
||||
|
||||
fsl_ldb->dev = &pdev->dev;
|
||||
fsl_ldb->bridge.funcs = &funcs;
|
||||
fsl_ldb->bridge.of_node = dev->of_node;
|
||||
|
||||
fsl_ldb->clk = devm_clk_get(dev, "ldb");
|
||||
if (IS_ERR(fsl_ldb->clk))
|
||||
return PTR_ERR(fsl_ldb->clk);
|
||||
|
||||
fsl_ldb->regmap = syscon_node_to_regmap(dev->of_node->parent);
|
||||
if (IS_ERR(fsl_ldb->regmap))
|
||||
return PTR_ERR(fsl_ldb->regmap);
|
||||
|
||||
/* Locate the panel DT node. */
|
||||
panel_node = of_graph_get_remote_node(dev->of_node, 1, 0);
|
||||
if (!panel_node)
|
||||
return -ENXIO;
|
||||
|
||||
panel = of_drm_find_panel(panel_node);
|
||||
of_node_put(panel_node);
|
||||
if (IS_ERR(panel))
|
||||
return PTR_ERR(panel);
|
||||
|
||||
fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
if (IS_ERR(fsl_ldb->panel_bridge))
|
||||
return PTR_ERR(fsl_ldb->panel_bridge);
|
||||
|
||||
/* Determine whether this is dual-link configuration */
|
||||
port1 = of_graph_get_port_by_id(dev->of_node, 1);
|
||||
port2 = of_graph_get_port_by_id(dev->of_node, 2);
|
||||
dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
|
||||
of_node_put(port1);
|
||||
of_node_put(port2);
|
||||
|
||||
if (dual_link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
|
||||
dev_err(dev, "LVDS channel pixel swap not supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dual_link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS)
|
||||
fsl_ldb->lvds_dual_link = true;
|
||||
|
||||
platform_set_drvdata(pdev, fsl_ldb);
|
||||
|
||||
drm_bridge_add(&fsl_ldb->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_ldb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_ldb *fsl_ldb = platform_get_drvdata(pdev);
|
||||
|
||||
drm_bridge_remove(&fsl_ldb->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id fsl_ldb_match[] = {
|
||||
{ .compatible = "fsl,imx8mp-ldb", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_ldb_match);
|
||||
|
||||
static struct platform_driver fsl_ldb_driver = {
|
||||
.probe = fsl_ldb_probe,
|
||||
.remove = fsl_ldb_remove,
|
||||
.driver = {
|
||||
.name = "fsl-ldb",
|
||||
.of_match_table = fsl_ldb_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(fsl_ldb_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX8MP LDB");
|
||||
MODULE_LICENSE("GPL");
|
@ -737,8 +737,9 @@ static int it6505_drm_dp_link_probe(struct drm_dp_aux *aux,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int it6505_drm_dp_link_power_up(struct drm_dp_aux *aux,
|
||||
struct it6505_drm_dp_link *link)
|
||||
static int it6505_drm_dp_link_set_power(struct drm_dp_aux *aux,
|
||||
struct it6505_drm_dp_link *link,
|
||||
u8 mode)
|
||||
{
|
||||
u8 value;
|
||||
int err;
|
||||
@ -752,18 +753,20 @@ static int it6505_drm_dp_link_power_up(struct drm_dp_aux *aux,
|
||||
return err;
|
||||
|
||||
value &= ~DP_SET_POWER_MASK;
|
||||
value |= DP_SET_POWER_D0;
|
||||
value |= mode;
|
||||
|
||||
err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* According to the DP 1.1 specification, a "Sink Device must exit the
|
||||
* power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
|
||||
* Control Field" (register 0x600).
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
if (mode == DP_SET_POWER_D0) {
|
||||
/*
|
||||
* According to the DP 1.1 specification, a "Sink Device must
|
||||
* exit the power saving state within 1 ms" (Section 2.5.3.1,
|
||||
* Table 5-52, "Sink Control Field" (register 0x600).
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2624,7 +2627,8 @@ static enum drm_connector_status it6505_detect(struct it6505 *it6505)
|
||||
if (it6505_get_sink_hpd_status(it6505)) {
|
||||
it6505_aux_on(it6505);
|
||||
it6505_drm_dp_link_probe(&it6505->aux, &it6505->link);
|
||||
it6505_drm_dp_link_power_up(&it6505->aux, &it6505->link);
|
||||
it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link,
|
||||
DP_SET_POWER_D0);
|
||||
it6505->auto_train_retry = AUTO_TRAIN_RETRY;
|
||||
|
||||
if (it6505->dpcd[0] == 0) {
|
||||
@ -2960,8 +2964,11 @@ static void it6505_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
|
||||
DRM_DEV_DEBUG_DRIVER(dev, "start");
|
||||
|
||||
if (it6505->powered)
|
||||
if (it6505->powered) {
|
||||
it6505_video_disable(it6505);
|
||||
it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link,
|
||||
DP_SET_POWER_D3);
|
||||
}
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
|
@ -1955,7 +1955,7 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
|
||||
tc->bridge.ops |= DRM_BRIDGE_OP_DETECT;
|
||||
tc->bridge.ops |= DRM_BRIDGE_OP_EDID;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc_probe_bridge_endpoint(struct tc_data *tc)
|
||||
@ -1964,7 +1964,9 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc)
|
||||
struct of_endpoint endpoint;
|
||||
struct device_node *node = NULL;
|
||||
const u8 mode_dpi_to_edp = BIT(1) | BIT(2);
|
||||
const u8 mode_dpi_to_dp = BIT(1);
|
||||
const u8 mode_dsi_to_edp = BIT(0) | BIT(2);
|
||||
const u8 mode_dsi_to_dp = BIT(0);
|
||||
const u8 mode_dsi_to_dpi = BIT(0) | BIT(1);
|
||||
u8 mode = 0;
|
||||
|
||||
@ -1990,11 +1992,11 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc)
|
||||
mode |= BIT(endpoint.port);
|
||||
}
|
||||
|
||||
if (mode == mode_dpi_to_edp)
|
||||
if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp)
|
||||
return tc_probe_edp_bridge_endpoint(tc);
|
||||
else if (mode == mode_dsi_to_dpi)
|
||||
return tc_probe_dpi_bridge_endpoint(tc);
|
||||
else if (mode == mode_dsi_to_edp)
|
||||
else if (mode == mode_dsi_to_edp || mode == mode_dsi_to_dp)
|
||||
dev_warn(dev, "The mode DSI-to-(e)DP is not supported!\n");
|
||||
else
|
||||
dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode);
|
||||
|
@ -341,13 +341,11 @@ static int tfp410_init(struct device *dev, bool i2c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tfp410_fini(struct device *dev)
|
||||
static void tfp410_fini(struct device *dev)
|
||||
{
|
||||
struct tfp410 *dvi = dev_get_drvdata(dev);
|
||||
|
||||
drm_bridge_remove(&dvi->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tfp410_probe(struct platform_device *pdev)
|
||||
@ -357,7 +355,9 @@ static int tfp410_probe(struct platform_device *pdev)
|
||||
|
||||
static int tfp410_remove(struct platform_device *pdev)
|
||||
{
|
||||
return tfp410_fini(&pdev->dev);
|
||||
tfp410_fini(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tfp410_match[] = {
|
||||
@ -394,7 +394,9 @@ static int tfp410_i2c_probe(struct i2c_client *client,
|
||||
|
||||
static int tfp410_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return tfp410_fini(&client->dev);
|
||||
tfp410_fini(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tfp410_i2c_ids[] = {
|
||||
|
@ -31,7 +31,8 @@ config DRM_DISPLAY_HDMI_HELPER
|
||||
|
||||
config DRM_DP_AUX_CHARDEV
|
||||
bool "DRM DP AUX Interface"
|
||||
depends on DRM
|
||||
depends on DRM && DRM_DISPLAY_HELPER
|
||||
select DRM_DISPLAY_DP_HELPER
|
||||
help
|
||||
Choose this option to enable a /dev/drm_dp_auxN node that allows to
|
||||
read and write values to arbitrary DPCD registers on the DP aux
|
||||
@ -39,7 +40,8 @@ config DRM_DP_AUX_CHARDEV
|
||||
|
||||
config DRM_DP_CEC
|
||||
bool "Enable DisplayPort CEC-Tunneling-over-AUX HDMI support"
|
||||
depends on DRM
|
||||
depends on DRM && DRM_DISPLAY_HELPER
|
||||
select DRM_DISPLAY_DP_HELPER
|
||||
select CEC_CORE
|
||||
help
|
||||
Choose this option if you want to enable HDMI CEC support for
|
||||
|
@ -3557,10 +3557,9 @@ static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
|
||||
if (ret < 0)
|
||||
goto fail_free;
|
||||
|
||||
/* DPCD read should never be NACKed */
|
||||
if (txmsg->reply.reply_type == 1) {
|
||||
drm_err(mgr->dev, "mstb %p port %d: DPCD read on addr 0x%x for %d bytes NAKed\n",
|
||||
mstb, port->port_num, offset, size);
|
||||
drm_dbg_kms(mgr->dev, "mstb %p port %d: DPCD read on addr 0x%x for %d bytes NAKed\n",
|
||||
mstb, port->port_num, offset, size);
|
||||
ret = -EIO;
|
||||
goto fail_free;
|
||||
}
|
||||
|
@ -254,43 +254,6 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_set_fb_for_plane);
|
||||
|
||||
/**
|
||||
* drm_atomic_set_fence_for_plane - set fence for plane
|
||||
* @plane_state: atomic state object for the plane
|
||||
* @fence: dma_fence to use for the plane
|
||||
*
|
||||
* Helper to setup the plane_state fence in case it is not set yet.
|
||||
* By using this drivers doesn't need to worry if the user choose
|
||||
* implicit or explicit fencing.
|
||||
*
|
||||
* This function will not set the fence to the state if it was set
|
||||
* via explicit fencing interfaces on the atomic ioctl. In that case it will
|
||||
* drop the reference to the fence as we are not storing it anywhere.
|
||||
* Otherwise, if &drm_plane_state.fence is not set this function we just set it
|
||||
* with the received implicit fence. In both cases this function consumes a
|
||||
* reference for @fence.
|
||||
*
|
||||
* This way explicit fencing can be used to overrule implicit fencing, which is
|
||||
* important to make explicit fencing use-cases work: One example is using one
|
||||
* buffer for 2 screens with different refresh rates. Implicit fencing will
|
||||
* clamp rendering to the refresh rate of the slower screen, whereas explicit
|
||||
* fence allows 2 independent render and display loops on a single buffer. If a
|
||||
* driver allows obeys both implicit and explicit fences for plane updates, then
|
||||
* it will break all the benefits of explicit fencing.
|
||||
*/
|
||||
void
|
||||
drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state,
|
||||
struct dma_fence *fence)
|
||||
{
|
||||
if (plane_state->fence) {
|
||||
dma_fence_put(fence);
|
||||
return;
|
||||
}
|
||||
|
||||
plane_state->fence = fence;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_set_fence_for_plane);
|
||||
|
||||
/**
|
||||
* drm_atomic_set_crtc_for_connector - set CRTC for connector
|
||||
* @conn_state: atomic state object for the connector
|
||||
@ -1077,10 +1040,10 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
|
||||
*
|
||||
* As a contrast, with implicit fencing the kernel keeps track of any
|
||||
* ongoing rendering, and automatically ensures that the atomic update waits
|
||||
* for any pending rendering to complete. For shared buffers represented with
|
||||
* a &struct dma_buf this is tracked in &struct dma_resv.
|
||||
* Implicit syncing is how Linux traditionally worked (e.g. DRI2/3 on X.org),
|
||||
* whereas explicit fencing is what Android wants.
|
||||
* for any pending rendering to complete. This is usually tracked in &struct
|
||||
* dma_resv which can also contain mandatory kernel fences. Implicit syncing
|
||||
* is how Linux traditionally worked (e.g. DRI2/3 on X.org), whereas explicit
|
||||
* fencing is what Android wants.
|
||||
*
|
||||
* "IN_FENCE_FD”:
|
||||
* Use this property to pass a fence that DRM should wait on before
|
||||
@ -1095,7 +1058,7 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
|
||||
*
|
||||
* On the driver side the fence is stored on the @fence parameter of
|
||||
* &struct drm_plane_state. Drivers which also support implicit fencing
|
||||
* should set the implicit fence using drm_atomic_set_fence_for_plane(),
|
||||
* should extract the implicit fence using drm_gem_plane_helper_prepare_fb(),
|
||||
* to make sure there's consistent behaviour between drivers in precedence
|
||||
* of implicit vs. explicit fencing.
|
||||
*
|
||||
|
@ -259,7 +259,7 @@ static bool drm_crtc_supports_legacy_gamma(struct drm_crtc *crtc)
|
||||
* @crtc: CRTC object
|
||||
* @red: red correction table
|
||||
* @green: green correction table
|
||||
* @blue: green correction table
|
||||
* @blue: blue correction table
|
||||
* @size: size of the tables
|
||||
* @ctx: lock acquire context
|
||||
*
|
||||
|
@ -1610,7 +1610,7 @@ static void edid_header_fix(void *edid)
|
||||
|
||||
/**
|
||||
* drm_edid_header_is_valid - sanity check the header of the base EDID block
|
||||
* @raw_edid: pointer to raw base EDID block
|
||||
* @_edid: pointer to raw base EDID block
|
||||
*
|
||||
* Sanity check the header of the base EDID block.
|
||||
*
|
||||
@ -1827,7 +1827,7 @@ static void edid_block_dump(const char *level, const void *block, int block_num)
|
||||
|
||||
/**
|
||||
* drm_edid_block_valid - Sanity check the EDID block (base or extension)
|
||||
* @raw_edid: pointer to raw EDID block
|
||||
* @_block: pointer to raw EDID block
|
||||
* @block_num: type of block to validate (0 for base, extension otherwise)
|
||||
* @print_bad_edid: if true, dump bad EDID blocks to the console
|
||||
* @edid_corrupt: if true, the header or checksum is invalid
|
||||
@ -2112,8 +2112,8 @@ static enum edid_block_status edid_block_read(void *block, unsigned int block_nu
|
||||
/**
|
||||
* drm_do_get_edid - get EDID data using a custom EDID block read function
|
||||
* @connector: connector we're probing
|
||||
* @get_edid_block: EDID block read function
|
||||
* @data: private data passed to the block read function
|
||||
* @read_block: EDID block read function
|
||||
* @context: private data passed to the block read function
|
||||
*
|
||||
* When the I2C adapter connected to the DDC bus is hidden behind a device that
|
||||
* exposes a different interface to read EDID blocks this function can be used
|
||||
@ -2384,13 +2384,9 @@ static u32 edid_get_quirks(const struct edid *edid)
|
||||
#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
|
||||
#define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
|
||||
|
||||
/**
|
||||
* edid_fixup_preferred - set preferred modes based on quirk list
|
||||
* @connector: has mode list to fix up
|
||||
* @quirks: quirks list
|
||||
*
|
||||
* Walk the mode list for @connector, clearing the preferred status
|
||||
* on existing modes and setting it anew for the right mode ala @quirks.
|
||||
/*
|
||||
* Walk the mode list for connector, clearing the preferred status on existing
|
||||
* modes and setting it anew for the right mode ala quirks.
|
||||
*/
|
||||
static void edid_fixup_preferred(struct drm_connector *connector,
|
||||
u32 quirks)
|
||||
@ -2659,10 +2655,7 @@ drm_gtf2_2j(const struct edid *edid)
|
||||
return descriptor ? descriptor->data.other_data.data.range.formula.gtf2.j : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* standard_timing_level - get std. timing level(CVT/GTF/DMT)
|
||||
* @edid: EDID block to scan
|
||||
*/
|
||||
/* Get standard timing level (CVT/GTF/DMT). */
|
||||
static int standard_timing_level(const struct edid *edid)
|
||||
{
|
||||
if (edid->revision >= 2) {
|
||||
@ -2696,12 +2689,7 @@ static int drm_mode_hsync(const struct drm_display_mode *mode)
|
||||
return DIV_ROUND_CLOSEST(mode->clock, mode->htotal);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_mode_std - convert standard mode info (width, height, refresh) into mode
|
||||
* @connector: connector of for the EDID block
|
||||
* @edid: EDID block to scan
|
||||
* @t: standard timing params
|
||||
*
|
||||
/*
|
||||
* Take the standard timing params (in this case width, aspect, and refresh)
|
||||
* and convert them into a real mode using CVT/GTF/DMT.
|
||||
*/
|
||||
@ -2857,15 +2845,10 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
|
||||
mode->flags |= DRM_MODE_FLAG_INTERLACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_mode_detailed - create a new mode from an EDID detailed timing section
|
||||
* @dev: DRM device (needed to create new mode)
|
||||
* @edid: EDID block
|
||||
* @timing: EDID detailed timing info
|
||||
* @quirks: quirks to apply
|
||||
*
|
||||
* An EDID detailed timing block contains enough info for us to create and
|
||||
* return a new struct drm_display_mode.
|
||||
/*
|
||||
* Create a new mode from an EDID detailed timing section. An EDID detailed
|
||||
* timing block contains enough info for us to create and return a new struct
|
||||
* drm_display_mode.
|
||||
*/
|
||||
static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
|
||||
const struct edid *edid,
|
||||
@ -3247,13 +3230,10 @@ do_established_modes(const struct detailed_timing *timing, void *c)
|
||||
closure->modes += drm_est3_modes(closure->connector, timing);
|
||||
}
|
||||
|
||||
/**
|
||||
* add_established_modes - get est. modes from EDID and add them
|
||||
* @connector: connector to add mode(s) to
|
||||
* @edid: EDID block to scan
|
||||
*
|
||||
* Each EDID block contains a bitmap of the supported "established modes" list
|
||||
* (defined above). Tease them out and add them to the global modes list.
|
||||
/*
|
||||
* Get established modes from EDID and add them. Each EDID block contains a
|
||||
* bitmap of the supported "established modes" list (defined above). Tease them
|
||||
* out and add them to the global modes list.
|
||||
*/
|
||||
static int
|
||||
add_established_modes(struct drm_connector *connector, const struct edid *edid)
|
||||
@ -3311,13 +3291,10 @@ do_standard_modes(const struct detailed_timing *timing, void *c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add_standard_modes - get std. modes from EDID and add them
|
||||
* @connector: connector to add mode(s) to
|
||||
* @edid: EDID block to scan
|
||||
*
|
||||
* Standard modes can be calculated using the appropriate standard (DMT,
|
||||
* GTF or CVT. Grab them from @edid and add them to the list.
|
||||
/*
|
||||
* Get standard modes from EDID and add them. Standard modes can be calculated
|
||||
* using the appropriate standard (DMT, GTF, or CVT). Grab them from EDID and
|
||||
* add them to the list.
|
||||
*/
|
||||
static int
|
||||
add_standard_modes(struct drm_connector *connector, const struct edid *edid)
|
||||
|
@ -708,22 +708,21 @@ static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off,
|
||||
/**
|
||||
* drm_fb_helper_deferred_io() - fbdev deferred_io callback function
|
||||
* @info: fb_info struct pointer
|
||||
* @pagelist: list of mmap framebuffer pages that have to be flushed
|
||||
* @pagereflist: list of mmap framebuffer pages that have to be flushed
|
||||
*
|
||||
* This function is used as the &fb_deferred_io.deferred_io
|
||||
* callback function for flushing the fbdev mmap writes.
|
||||
*/
|
||||
void drm_fb_helper_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
unsigned long start, end, min, max;
|
||||
struct page *page;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
struct drm_rect damage_area;
|
||||
|
||||
min = ULONG_MAX;
|
||||
max = 0;
|
||||
list_for_each_entry(page, pagelist, lru) {
|
||||
start = page->index << PAGE_SHIFT;
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
start = pageref->offset;
|
||||
end = start + PAGE_SIZE;
|
||||
min = min(min, start);
|
||||
max = max(max, end);
|
||||
@ -2118,7 +2117,9 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
|
||||
if (fb_helper->dev->driver->gem_prime_mmap)
|
||||
if (drm_fbdev_use_shadow_fb(fb_helper))
|
||||
return fb_deferred_io_mmap(info, vma);
|
||||
else if (fb_helper->dev->driver->gem_prime_mmap)
|
||||
return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
@ -40,6 +40,94 @@ unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_clip_offset);
|
||||
|
||||
/* TODO: Make this functon work with multi-plane formats. */
|
||||
static int drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
|
||||
const void *vaddr, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, bool vaddr_cached_hint,
|
||||
void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
|
||||
{
|
||||
unsigned long linepixels = drm_rect_width(clip);
|
||||
unsigned long lines = drm_rect_height(clip);
|
||||
size_t sbuf_len = linepixels * fb->format->cpp[0];
|
||||
void *stmp = NULL;
|
||||
unsigned long i;
|
||||
const void *sbuf;
|
||||
|
||||
/*
|
||||
* Some source buffers, such as CMA memory, use write-combine
|
||||
* caching, so reads are uncached. Speed up access by fetching
|
||||
* one line at a time.
|
||||
*/
|
||||
if (!vaddr_cached_hint) {
|
||||
stmp = kmalloc(sbuf_len, GFP_KERNEL);
|
||||
if (!stmp)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = drm_rect_width(clip) * dst_pixsize;
|
||||
vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]);
|
||||
|
||||
for (i = 0; i < lines; ++i) {
|
||||
if (stmp)
|
||||
sbuf = memcpy(stmp, vaddr, sbuf_len);
|
||||
else
|
||||
sbuf = vaddr;
|
||||
xfrm_line(dst, sbuf, linepixels);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(stmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Make this functon work with multi-plane formats. */
|
||||
static int drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
|
||||
const void *vaddr, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, bool vaddr_cached_hint,
|
||||
void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
|
||||
{
|
||||
unsigned long linepixels = drm_rect_width(clip);
|
||||
unsigned long lines = drm_rect_height(clip);
|
||||
size_t dbuf_len = linepixels * dst_pixsize;
|
||||
size_t stmp_off = round_up(dbuf_len, ARCH_KMALLOC_MINALIGN); /* for sbuf alignment */
|
||||
size_t sbuf_len = linepixels * fb->format->cpp[0];
|
||||
void *stmp = NULL;
|
||||
unsigned long i;
|
||||
const void *sbuf;
|
||||
void *dbuf;
|
||||
|
||||
if (vaddr_cached_hint) {
|
||||
dbuf = kmalloc(dbuf_len, GFP_KERNEL);
|
||||
} else {
|
||||
dbuf = kmalloc(stmp_off + sbuf_len, GFP_KERNEL);
|
||||
stmp = dbuf + stmp_off;
|
||||
}
|
||||
if (!dbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = linepixels * dst_pixsize;
|
||||
vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]);
|
||||
|
||||
for (i = 0; i < lines; ++i) {
|
||||
if (stmp)
|
||||
sbuf = memcpy(stmp, vaddr, sbuf_len);
|
||||
else
|
||||
sbuf = vaddr;
|
||||
xfrm_line(dbuf, sbuf, linepixels);
|
||||
memcpy_toio(dst, dbuf, dbuf_len);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(dbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_fb_memcpy - Copy clip buffer
|
||||
* @dst: Destination buffer
|
||||
@ -100,6 +188,26 @@ void drm_fb_memcpy_toio(void __iomem *dst, unsigned int dst_pitch, const void *v
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_memcpy_toio);
|
||||
|
||||
static void drm_fb_swab16_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u16 *dbuf16 = dbuf;
|
||||
const u16 *sbuf16 = sbuf;
|
||||
const u16 *send16 = sbuf16 + pixels;
|
||||
|
||||
while (sbuf16 < send16)
|
||||
*dbuf16++ = swab16(*sbuf16++);
|
||||
}
|
||||
|
||||
static void drm_fb_swab32_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u32 *dbuf32 = dbuf;
|
||||
const u32 *sbuf32 = sbuf;
|
||||
const u32 *send32 = sbuf32 + pixels;
|
||||
|
||||
while (sbuf32 < send32)
|
||||
*dbuf32++ = swab32(*sbuf32++);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_fb_swab - Swap bytes into clip buffer
|
||||
* @dst: Destination buffer
|
||||
@ -120,63 +228,34 @@ void drm_fb_swab(void *dst, unsigned int dst_pitch, const void *src,
|
||||
bool cached)
|
||||
{
|
||||
u8 cpp = fb->format->cpp[0];
|
||||
size_t len = drm_rect_width(clip) * cpp;
|
||||
const u16 *src16;
|
||||
const u32 *src32;
|
||||
u16 *dst16;
|
||||
u32 *dst32;
|
||||
unsigned int x, y;
|
||||
void *buf = NULL;
|
||||
|
||||
if (WARN_ON_ONCE(cpp != 2 && cpp != 4))
|
||||
return;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = len;
|
||||
|
||||
if (!cached)
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
|
||||
src += clip_offset(clip, fb->pitches[0], cpp);
|
||||
|
||||
for (y = clip->y1; y < clip->y2; y++) {
|
||||
if (buf) {
|
||||
memcpy(buf, src, len);
|
||||
src16 = buf;
|
||||
src32 = buf;
|
||||
} else {
|
||||
src16 = src;
|
||||
src32 = src;
|
||||
}
|
||||
|
||||
dst16 = dst;
|
||||
dst32 = dst;
|
||||
|
||||
for (x = clip->x1; x < clip->x2; x++) {
|
||||
if (cpp == 4)
|
||||
*dst32++ = swab32(*src32++);
|
||||
else
|
||||
*dst16++ = swab16(*src16++);
|
||||
}
|
||||
|
||||
src += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
switch (cpp) {
|
||||
case 4:
|
||||
drm_fb_xfrm(dst, dst_pitch, cpp, src, fb, clip, cached, drm_fb_swab32_line);
|
||||
break;
|
||||
case 2:
|
||||
drm_fb_xfrm(dst, dst_pitch, cpp, src, fb, clip, cached, drm_fb_swab16_line);
|
||||
break;
|
||||
default:
|
||||
drm_warn_once(fb->dev, "Format %p4cc has unsupported pixel size.\n",
|
||||
&fb->format->format);
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_swab);
|
||||
|
||||
static void drm_fb_xrgb8888_to_rgb332_line(u8 *dbuf, const __le32 *sbuf, unsigned int pixels)
|
||||
static void drm_fb_xrgb8888_to_rgb332_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(sbuf[x]);
|
||||
dbuf[x] = ((pix & 0x00e00000) >> 16) |
|
||||
((pix & 0x0000e000) >> 11) |
|
||||
((pix & 0x000000c0) >> 6);
|
||||
pix = le32_to_cpu(sbuf32[x]);
|
||||
dbuf8[x] = ((pix & 0x00e00000) >> 16) |
|
||||
((pix & 0x0000e000) >> 11) |
|
||||
((pix & 0x000000c0) >> 6);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,46 +272,38 @@ static void drm_fb_xrgb8888_to_rgb332_line(u8 *dbuf, const __le32 *sbuf, unsigne
|
||||
void drm_fb_xrgb8888_to_rgb332(void *dst, unsigned int dst_pitch, const void *src,
|
||||
const struct drm_framebuffer *fb, const struct drm_rect *clip)
|
||||
{
|
||||
size_t width = drm_rect_width(clip);
|
||||
size_t src_len = width * sizeof(u32);
|
||||
unsigned int y;
|
||||
void *sbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = width;
|
||||
|
||||
/* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */
|
||||
sbuf = kmalloc(src_len, GFP_KERNEL);
|
||||
if (!sbuf)
|
||||
return;
|
||||
|
||||
src += clip_offset(clip, fb->pitches[0], sizeof(u32));
|
||||
for (y = 0; y < drm_rect_height(clip); y++) {
|
||||
memcpy(sbuf, src, src_len);
|
||||
drm_fb_xrgb8888_to_rgb332_line(dst, sbuf, width);
|
||||
src += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(sbuf);
|
||||
drm_fb_xfrm(dst, dst_pitch, 1, src, fb, clip, false, drm_fb_xrgb8888_to_rgb332_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332);
|
||||
|
||||
static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, const u32 *sbuf,
|
||||
unsigned int pixels,
|
||||
bool swab)
|
||||
static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u16 *dbuf16 = dbuf;
|
||||
const u32 *sbuf32 = sbuf;
|
||||
unsigned int x;
|
||||
u16 val16;
|
||||
|
||||
for (x = 0; x < pixels; x++) {
|
||||
val16 = ((sbuf[x] & 0x00F80000) >> 8) |
|
||||
((sbuf[x] & 0x0000FC00) >> 5) |
|
||||
((sbuf[x] & 0x000000F8) >> 3);
|
||||
if (swab)
|
||||
dbuf[x] = swab16(val16);
|
||||
else
|
||||
dbuf[x] = val16;
|
||||
val16 = ((sbuf32[x] & 0x00F80000) >> 8) |
|
||||
((sbuf32[x] & 0x0000FC00) >> 5) |
|
||||
((sbuf32[x] & 0x000000F8) >> 3);
|
||||
dbuf16[x] = val16;
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf,
|
||||
unsigned int pixels)
|
||||
{
|
||||
u16 *dbuf16 = dbuf;
|
||||
const u32 *sbuf32 = sbuf;
|
||||
unsigned int x;
|
||||
u16 val16;
|
||||
|
||||
for (x = 0; x < pixels; x++) {
|
||||
val16 = ((sbuf32[x] & 0x00F80000) >> 8) |
|
||||
((sbuf32[x] & 0x0000FC00) >> 5) |
|
||||
((sbuf32[x] & 0x000000F8) >> 3);
|
||||
dbuf16[x] = swab16(val16);
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,32 +323,12 @@ void drm_fb_xrgb8888_to_rgb565(void *dst, unsigned int dst_pitch, const void *va
|
||||
const struct drm_framebuffer *fb, const struct drm_rect *clip,
|
||||
bool swab)
|
||||
{
|
||||
size_t linepixels = clip->x2 - clip->x1;
|
||||
size_t src_len = linepixels * sizeof(u32);
|
||||
size_t dst_len = linepixels * sizeof(u16);
|
||||
unsigned y, lines = clip->y2 - clip->y1;
|
||||
void *sbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = dst_len;
|
||||
|
||||
/*
|
||||
* The cma memory is write-combined so reads are uncached.
|
||||
* Speed up by fetching one line at a time.
|
||||
*/
|
||||
sbuf = kmalloc(src_len, GFP_KERNEL);
|
||||
if (!sbuf)
|
||||
return;
|
||||
|
||||
vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
|
||||
for (y = 0; y < lines; y++) {
|
||||
memcpy(sbuf, vaddr, src_len);
|
||||
drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(sbuf);
|
||||
if (swab)
|
||||
drm_fb_xfrm(dst, dst_pitch, 2, vaddr, fb, clip, false,
|
||||
drm_fb_xrgb8888_to_rgb565_swab_line);
|
||||
else
|
||||
drm_fb_xfrm(dst, dst_pitch, 2, vaddr, fb, clip, false,
|
||||
drm_fb_xrgb8888_to_rgb565_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
|
||||
|
||||
@ -297,39 +348,25 @@ void drm_fb_xrgb8888_to_rgb565_toio(void __iomem *dst, unsigned int dst_pitch,
|
||||
const void *vaddr, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip, bool swab)
|
||||
{
|
||||
size_t linepixels = clip->x2 - clip->x1;
|
||||
size_t dst_len = linepixels * sizeof(u16);
|
||||
unsigned y, lines = clip->y2 - clip->y1;
|
||||
void *dbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = dst_len;
|
||||
|
||||
dbuf = kmalloc(dst_len, GFP_KERNEL);
|
||||
if (!dbuf)
|
||||
return;
|
||||
|
||||
vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
|
||||
for (y = 0; y < lines; y++) {
|
||||
drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab);
|
||||
memcpy_toio(dst, dbuf, dst_len);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(dbuf);
|
||||
if (swab)
|
||||
drm_fb_xfrm_toio(dst, dst_pitch, 2, vaddr, fb, clip, false,
|
||||
drm_fb_xrgb8888_to_rgb565_swab_line);
|
||||
else
|
||||
drm_fb_xfrm_toio(dst, dst_pitch, 2, vaddr, fb, clip, false,
|
||||
drm_fb_xrgb8888_to_rgb565_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio);
|
||||
|
||||
static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, const u32 *sbuf,
|
||||
unsigned int pixels)
|
||||
static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u8 *dbuf8 = dbuf;
|
||||
const u32 *sbuf32 = sbuf;
|
||||
unsigned int x;
|
||||
|
||||
for (x = 0; x < pixels; x++) {
|
||||
*dbuf++ = (sbuf[x] & 0x000000FF) >> 0;
|
||||
*dbuf++ = (sbuf[x] & 0x0000FF00) >> 8;
|
||||
*dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
|
||||
*dbuf8++ = (sbuf32[x] & 0x000000FF) >> 0;
|
||||
*dbuf8++ = (sbuf32[x] & 0x0000FF00) >> 8;
|
||||
*dbuf8++ = (sbuf32[x] & 0x00FF0000) >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,28 +384,7 @@ static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, const u32 *sbuf,
|
||||
void drm_fb_xrgb8888_to_rgb888(void *dst, unsigned int dst_pitch, const void *src,
|
||||
const struct drm_framebuffer *fb, const struct drm_rect *clip)
|
||||
{
|
||||
size_t width = drm_rect_width(clip);
|
||||
size_t src_len = width * sizeof(u32);
|
||||
unsigned int y;
|
||||
void *sbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = width * 3;
|
||||
|
||||
/* Use a buffer to speed up access on buffers with uncached read mapping (i.e. WC) */
|
||||
sbuf = kmalloc(src_len, GFP_KERNEL);
|
||||
if (!sbuf)
|
||||
return;
|
||||
|
||||
src += clip_offset(clip, fb->pitches[0], sizeof(u32));
|
||||
for (y = 0; y < drm_rect_height(clip); y++) {
|
||||
memcpy(sbuf, src, src_len);
|
||||
drm_fb_xrgb8888_to_rgb888_line(dst, sbuf, width);
|
||||
src += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(sbuf);
|
||||
drm_fb_xfrm(dst, dst_pitch, 3, src, fb, clip, false, drm_fb_xrgb8888_to_rgb888_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888);
|
||||
|
||||
@ -387,27 +403,8 @@ void drm_fb_xrgb8888_to_rgb888_toio(void __iomem *dst, unsigned int dst_pitch,
|
||||
const void *vaddr, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip)
|
||||
{
|
||||
size_t linepixels = clip->x2 - clip->x1;
|
||||
size_t dst_len = linepixels * 3;
|
||||
unsigned y, lines = clip->y2 - clip->y1;
|
||||
void *dbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = dst_len;
|
||||
|
||||
dbuf = kmalloc(dst_len, GFP_KERNEL);
|
||||
if (!dbuf)
|
||||
return;
|
||||
|
||||
vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
|
||||
for (y = 0; y < lines; y++) {
|
||||
drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels);
|
||||
memcpy_toio(dst, dbuf, dst_len);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(dbuf);
|
||||
drm_fb_xfrm_toio(dst, dst_pitch, 3, vaddr, fb, clip, false,
|
||||
drm_fb_xrgb8888_to_rgb888_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio);
|
||||
|
||||
@ -431,27 +428,8 @@ static void drm_fb_rgb565_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_p
|
||||
const void *vaddr, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip)
|
||||
{
|
||||
size_t linepixels = drm_rect_width(clip);
|
||||
size_t dst_len = linepixels * 4;
|
||||
unsigned int y, lines = drm_rect_height(clip);
|
||||
void *dbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = dst_len;
|
||||
|
||||
dbuf = kmalloc(dst_len, GFP_KERNEL);
|
||||
if (!dbuf)
|
||||
return;
|
||||
|
||||
vaddr += clip_offset(clip, fb->pitches[0], 2);
|
||||
for (y = 0; y < lines; y++) {
|
||||
drm_fb_rgb565_to_xrgb8888_line(dbuf, vaddr, linepixels);
|
||||
memcpy_toio(dst, dbuf, dst_len);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(dbuf);
|
||||
drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false,
|
||||
drm_fb_rgb565_to_xrgb8888_line);
|
||||
}
|
||||
|
||||
static void drm_fb_rgb888_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
@ -472,40 +450,22 @@ static void drm_fb_rgb888_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_p
|
||||
const void *vaddr, const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip)
|
||||
{
|
||||
size_t linepixels = drm_rect_width(clip);
|
||||
size_t dst_len = linepixels * 4;
|
||||
unsigned int y, lines = drm_rect_height(clip);
|
||||
void *dbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = dst_len;
|
||||
|
||||
dbuf = kmalloc(dst_len, GFP_KERNEL);
|
||||
if (!dbuf)
|
||||
return;
|
||||
|
||||
vaddr += clip_offset(clip, fb->pitches[0], 3);
|
||||
for (y = 0; y < lines; y++) {
|
||||
drm_fb_rgb888_to_xrgb8888_line(dbuf, vaddr, linepixels);
|
||||
memcpy_toio(dst, dbuf, dst_len);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(dbuf);
|
||||
drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false,
|
||||
drm_fb_rgb888_to_xrgb8888_line);
|
||||
}
|
||||
|
||||
static void drm_fb_xrgb8888_to_xrgb2101010_line(u32 *dbuf, const u32 *sbuf,
|
||||
unsigned int pixels)
|
||||
static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u32 *dbuf32 = dbuf;
|
||||
const u32 *sbuf32 = sbuf;
|
||||
unsigned int x;
|
||||
u32 val32;
|
||||
|
||||
for (x = 0; x < pixels; x++) {
|
||||
val32 = ((sbuf[x] & 0x000000FF) << 2) |
|
||||
((sbuf[x] & 0x0000FF00) << 4) |
|
||||
((sbuf[x] & 0x00FF0000) << 6);
|
||||
*dbuf++ = val32 | ((val32 >> 8) & 0x00300C03);
|
||||
val32 = ((sbuf32[x] & 0x000000FF) << 2) |
|
||||
((sbuf32[x] & 0x0000FF00) << 4) |
|
||||
((sbuf32[x] & 0x00FF0000) << 6);
|
||||
*dbuf32++ = val32 | ((val32 >> 8) & 0x00300C03);
|
||||
}
|
||||
}
|
||||
|
||||
@ -526,42 +486,25 @@ void drm_fb_xrgb8888_to_xrgb2101010_toio(void __iomem *dst,
|
||||
const struct drm_framebuffer *fb,
|
||||
const struct drm_rect *clip)
|
||||
{
|
||||
size_t linepixels = clip->x2 - clip->x1;
|
||||
size_t dst_len = linepixels * sizeof(u32);
|
||||
unsigned int y, lines = clip->y2 - clip->y1;
|
||||
void *dbuf;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = dst_len;
|
||||
|
||||
dbuf = kmalloc(dst_len, GFP_KERNEL);
|
||||
if (!dbuf)
|
||||
return;
|
||||
|
||||
vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
|
||||
for (y = 0; y < lines; y++) {
|
||||
drm_fb_xrgb8888_to_xrgb2101010_line(dbuf, vaddr, linepixels);
|
||||
memcpy_toio(dst, dbuf, dst_len);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(dbuf);
|
||||
drm_fb_xfrm_toio(dst, dst_pitch, 4, vaddr, fb, clip, false,
|
||||
drm_fb_xrgb8888_to_xrgb2101010_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio);
|
||||
|
||||
static void drm_fb_xrgb8888_to_gray8_line(u8 *dst, const u32 *src, unsigned int pixels)
|
||||
static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u8 *dbuf8 = dbuf;
|
||||
const u32 *sbuf32 = sbuf;
|
||||
unsigned int x;
|
||||
|
||||
for (x = 0; x < pixels; x++) {
|
||||
u8 r = (*src & 0x00ff0000) >> 16;
|
||||
u8 g = (*src & 0x0000ff00) >> 8;
|
||||
u8 b = *src & 0x000000ff;
|
||||
u8 r = (*sbuf32 & 0x00ff0000) >> 16;
|
||||
u8 g = (*sbuf32 & 0x0000ff00) >> 8;
|
||||
u8 b = *sbuf32 & 0x000000ff;
|
||||
|
||||
/* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
|
||||
*dst++ = (3 * r + 6 * g + b) / 10;
|
||||
src++;
|
||||
*dbuf8++ = (3 * r + 6 * g + b) / 10;
|
||||
sbuf32++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -585,37 +528,7 @@ static void drm_fb_xrgb8888_to_gray8_line(u8 *dst, const u32 *src, unsigned int
|
||||
void drm_fb_xrgb8888_to_gray8(void *dst, unsigned int dst_pitch, const void *vaddr,
|
||||
const struct drm_framebuffer *fb, const struct drm_rect *clip)
|
||||
{
|
||||
unsigned int linepixels = clip->x2 - clip->x1;
|
||||
unsigned int len = linepixels * sizeof(u32);
|
||||
unsigned int y;
|
||||
void *buf;
|
||||
u8 *dst8;
|
||||
u32 *src32;
|
||||
|
||||
if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
|
||||
return;
|
||||
|
||||
if (!dst_pitch)
|
||||
dst_pitch = drm_rect_width(clip);
|
||||
|
||||
/*
|
||||
* The cma memory is write-combined so reads are uncached.
|
||||
* Speed up by fetching one line at a time.
|
||||
*/
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
|
||||
for (y = clip->y1; y < clip->y2; y++) {
|
||||
dst8 = dst;
|
||||
src32 = memcpy(buf, vaddr, len);
|
||||
drm_fb_xrgb8888_to_gray8_line(dst8, src32, linepixels);
|
||||
vaddr += fb->pitches[0];
|
||||
dst += dst_pitch;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
drm_fb_xfrm(dst, dst_pitch, 1, vaddr, fb, clip, false, drm_fb_xrgb8888_to_gray8_line);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
|
||||
|
||||
@ -690,17 +603,20 @@ int drm_fb_blit_toio(void __iomem *dst, unsigned int dst_pitch, uint32_t dst_for
|
||||
EXPORT_SYMBOL(drm_fb_blit_toio);
|
||||
|
||||
|
||||
static void drm_fb_gray8_to_mono_line(u8 *dst, const u8 *src, unsigned int pixels)
|
||||
static void drm_fb_gray8_to_mono_line(void *dbuf, const void *sbuf, unsigned int pixels)
|
||||
{
|
||||
u8 *dbuf8 = dbuf;
|
||||
const u8 *sbuf8 = sbuf;
|
||||
|
||||
while (pixels) {
|
||||
unsigned int i, bits = min(pixels, 8U);
|
||||
u8 byte = 0;
|
||||
|
||||
for (i = 0; i < bits; i++, pixels--) {
|
||||
if (*src++ >= 128)
|
||||
if (*sbuf8++ >= 128)
|
||||
byte |= BIT(i);
|
||||
}
|
||||
*dst++ = byte;
|
||||
*dbuf8++ = byte;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/dma-resv.h>
|
||||
#include <linux/dma-fence-chain.h>
|
||||
|
||||
#include <drm/drm_atomic_state_helper.h>
|
||||
#include <drm/drm_atomic_uapi.h>
|
||||
@ -137,29 +138,67 @@
|
||||
*
|
||||
* This function is the default implementation for GEM drivers of
|
||||
* &drm_plane_helper_funcs.prepare_fb if no callback is provided.
|
||||
*
|
||||
* See drm_atomic_set_fence_for_plane() for a discussion of implicit and
|
||||
* explicit fencing in atomic modeset updates.
|
||||
*/
|
||||
int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
|
||||
int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
struct dma_fence *fence;
|
||||
struct dma_fence *fence = dma_fence_get(state->fence);
|
||||
enum dma_resv_usage usage;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
if (!state->fb)
|
||||
return 0;
|
||||
|
||||
obj = drm_gem_fb_get_obj(state->fb, 0);
|
||||
ret = dma_resv_get_singleton(obj->resv, DMA_RESV_USAGE_WRITE, &fence);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* TODO: drm_atomic_set_fence_for_plane() should be changed to be able
|
||||
* to handle more fences in general for multiple BOs per fb.
|
||||
/*
|
||||
* Only add the kernel fences here if there is already a fence set via
|
||||
* explicit fencing interfaces on the atomic ioctl.
|
||||
*
|
||||
* This way explicit fencing can be used to overrule implicit fencing,
|
||||
* which is important to make explicit fencing use-cases work: One
|
||||
* example is using one buffer for 2 screens with different refresh
|
||||
* rates. Implicit fencing will clamp rendering to the refresh rate of
|
||||
* the slower screen, whereas explicit fence allows 2 independent
|
||||
* render and display loops on a single buffer. If a driver allows
|
||||
* obeys both implicit and explicit fences for plane updates, then it
|
||||
* will break all the benefits of explicit fencing.
|
||||
*/
|
||||
drm_atomic_set_fence_for_plane(state, fence);
|
||||
usage = fence ? DMA_RESV_USAGE_KERNEL : DMA_RESV_USAGE_WRITE;
|
||||
|
||||
for (i = 0; i < state->fb->format->num_planes; ++i) {
|
||||
struct drm_gem_object *obj = drm_gem_fb_get_obj(state->fb, i);
|
||||
struct dma_fence *new;
|
||||
|
||||
if (WARN_ON_ONCE(!obj))
|
||||
continue;
|
||||
|
||||
ret = dma_resv_get_singleton(obj->resv, usage, &new);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
if (new && fence) {
|
||||
struct dma_fence_chain *chain = dma_fence_chain_alloc();
|
||||
|
||||
if (!chain) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dma_fence_chain_init(chain, fence, new, 1);
|
||||
fence = &chain->base;
|
||||
|
||||
} else if (new) {
|
||||
fence = new;
|
||||
}
|
||||
}
|
||||
|
||||
dma_fence_put(state->fence);
|
||||
state->fence = fence;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dma_fence_put(fence);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb);
|
||||
|
||||
@ -168,13 +207,13 @@ EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb);
|
||||
* @pipe: Simple display pipe
|
||||
* @plane_state: Plane state
|
||||
*
|
||||
* This function uses drm_gem_plane_helper_prepare_fb() to extract the exclusive fence
|
||||
* from &drm_gem_object.resv and attaches it to plane state for the atomic
|
||||
* This function uses drm_gem_plane_helper_prepare_fb() to extract the fences
|
||||
* from &drm_gem_object.resv and attaches them to the plane state for the atomic
|
||||
* helper to wait on. This is necessary to correctly implement implicit
|
||||
* synchronization for any buffers shared as a struct &dma_buf. Drivers can use
|
||||
* this as their &drm_simple_display_pipe_funcs.prepare_fb callback.
|
||||
*
|
||||
* See drm_atomic_set_fence_for_plane() for a discussion of implicit and
|
||||
* See drm_gem_plane_helper_prepare_fb() for a discussion of implicit and
|
||||
* explicit fencing in atomic modeset updates.
|
||||
*/
|
||||
int drm_gem_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
@ -262,3 +263,29 @@ void drmm_kfree(struct drm_device *dev, void *data)
|
||||
free_dr(dr_match);
|
||||
}
|
||||
EXPORT_SYMBOL(drmm_kfree);
|
||||
|
||||
static void drmm_mutex_release(struct drm_device *dev, void *res)
|
||||
{
|
||||
struct mutex *lock = res;
|
||||
|
||||
mutex_destroy(lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* drmm_mutex_init - &drm_device-managed mutex_init()
|
||||
* @dev: DRM device
|
||||
* @lock: lock to be initialized
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, or a negative errno code otherwise.
|
||||
*
|
||||
* This is a &drm_device-managed version of mutex_init(). The initialized
|
||||
* lock is automatically destroyed on the final drm_dev_put().
|
||||
*/
|
||||
int drmm_mutex_init(struct drm_device *dev, struct mutex *lock)
|
||||
{
|
||||
mutex_init(lock);
|
||||
|
||||
return drmm_add_action_or_reset(dev, drmm_mutex_release, lock);
|
||||
}
|
||||
EXPORT_SYMBOL(drmm_mutex_init);
|
||||
|
@ -247,6 +247,13 @@ static int __drm_universal_plane_init(struct drm_device *dev,
|
||||
if (WARN_ON(config->num_total_plane >= 32))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* First driver to need more than 64 formats needs to fix this. Each
|
||||
* format is encoded as a bit and the current code only supports a u64.
|
||||
*/
|
||||
if (WARN_ON(format_count > 64))
|
||||
return -EINVAL;
|
||||
|
||||
WARN_ON(drm_drv_uses_atomic_modeset(dev) &&
|
||||
(!funcs->atomic_destroy_state ||
|
||||
!funcs->atomic_duplicate_state));
|
||||
@ -268,13 +275,6 @@ static int __drm_universal_plane_init(struct drm_device *dev,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* First driver to need more than 64 formats needs to fix this. Each
|
||||
* format is encoded as a bit and the current code only supports a u64.
|
||||
*/
|
||||
if (WARN_ON(format_count > 64))
|
||||
return -EINVAL;
|
||||
|
||||
if (format_modifiers) {
|
||||
const uint64_t *temp_modifiers = format_modifiers;
|
||||
|
||||
|
@ -598,7 +598,12 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
sync_file = sync_file_create(submit->out_fence);
|
||||
if (!sync_file) {
|
||||
ret = -ENOMEM;
|
||||
goto err_submit_job;
|
||||
/*
|
||||
* When this late error is hit, the submit has already
|
||||
* been handed over to the scheduler. At this point
|
||||
* the sched_job must not be cleaned up.
|
||||
*/
|
||||
goto err_submit_put;
|
||||
}
|
||||
fd_install(out_fence_fd, sync_file->file);
|
||||
}
|
||||
@ -607,7 +612,8 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
args->fence = submit->out_fence_id;
|
||||
|
||||
err_submit_job:
|
||||
drm_sched_job_cleanup(&submit->sched_job);
|
||||
if (ret)
|
||||
drm_sched_job_cleanup(&submit->sched_job);
|
||||
err_submit_put:
|
||||
etnaviv_submit_put(submit);
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
@ -1451,9 +1452,18 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
struct device *dev = dsi->dev;
|
||||
struct drm_encoder *encoder = &dsi->encoder;
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct drm_panel *panel;
|
||||
int ret;
|
||||
|
||||
dsi->out_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
|
||||
panel = of_drm_find_panel(device->dev.of_node);
|
||||
if (!IS_ERR(panel)) {
|
||||
dsi->out_bridge = devm_drm_panel_bridge_add(dev, panel);
|
||||
} else {
|
||||
dsi->out_bridge = of_drm_find_bridge(device->dev.of_node);
|
||||
if (!dsi->out_bridge)
|
||||
dsi->out_bridge = ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (IS_ERR(dsi->out_bridge)) {
|
||||
ret = PTR_ERR(dsi->out_bridge);
|
||||
DRM_DEV_ERROR(dev, "failed to find the bridge: %d\n", ret);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
@ -38,6 +39,7 @@ struct mcde_dsi {
|
||||
struct device *dev;
|
||||
struct mcde *mcde;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge_out;
|
||||
struct mipi_dsi_host dsi_host;
|
||||
struct mipi_dsi_device *mdsi;
|
||||
@ -1071,7 +1073,9 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
|
||||
struct drm_device *drm = data;
|
||||
struct mcde *mcde = to_mcde(drm);
|
||||
struct mcde_dsi *d = dev_get_drvdata(dev);
|
||||
struct drm_bridge *bridge;
|
||||
struct device_node *child;
|
||||
struct drm_panel *panel = NULL;
|
||||
struct drm_bridge *bridge = NULL;
|
||||
|
||||
if (!of_get_available_child_count(dev->of_node)) {
|
||||
dev_info(dev, "unused DSI interface\n");
|
||||
@ -1096,10 +1100,37 @@ static int mcde_dsi_bind(struct device *dev, struct device *master,
|
||||
return PTR_ERR(d->lp_clk);
|
||||
}
|
||||
|
||||
bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
|
||||
if (IS_ERR(bridge)) {
|
||||
dev_err(dev, "error to get bridge\n");
|
||||
return PTR_ERR(bridge);
|
||||
/* Look for a panel as a child to this node */
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
panel = of_drm_find_panel(child);
|
||||
if (IS_ERR(panel)) {
|
||||
dev_err(dev, "failed to find panel try bridge (%ld)\n",
|
||||
PTR_ERR(panel));
|
||||
panel = NULL;
|
||||
|
||||
bridge = of_drm_find_bridge(child);
|
||||
if (!bridge) {
|
||||
dev_err(dev, "failed to find bridge\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (panel) {
|
||||
bridge = drm_panel_bridge_add_typed(panel,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
if (IS_ERR(bridge)) {
|
||||
dev_err(dev, "error adding panel bridge\n");
|
||||
return PTR_ERR(bridge);
|
||||
}
|
||||
dev_info(dev, "connected to panel\n");
|
||||
d->panel = panel;
|
||||
} else if (bridge) {
|
||||
/* TODO: AV8100 HDMI encoder goes here for example */
|
||||
dev_info(dev, "connected to non-panel bridge (unsupported)\n");
|
||||
return -ENODEV;
|
||||
} else {
|
||||
dev_err(dev, "no panel or bridge\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
d->bridge_out = bridge;
|
||||
@ -1122,6 +1153,8 @@ static void mcde_dsi_unbind(struct device *dev, struct device *master,
|
||||
{
|
||||
struct mcde_dsi *d = dev_get_drvdata(dev);
|
||||
|
||||
if (d->panel)
|
||||
drm_panel_bridge_remove(d->bridge_out);
|
||||
regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET,
|
||||
PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_ioctl.h>
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_module.h>
|
||||
#include <drm/drm_pciids.h>
|
||||
|
||||
@ -65,6 +66,11 @@ static int mgag200_regs_init(struct mga_device *mdev)
|
||||
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
||||
u32 option, option2;
|
||||
u8 crtcext3;
|
||||
int ret;
|
||||
|
||||
ret = drmm_mutex_init(dev, &mdev->rmmio_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mdev->type) {
|
||||
case G200_PCI:
|
||||
|
@ -213,6 +213,7 @@ struct mga_device {
|
||||
struct drm_device base;
|
||||
unsigned long flags;
|
||||
|
||||
struct mutex rmmio_lock; /* Protects access to rmmio */
|
||||
resource_size_t rmmio_base;
|
||||
resource_size_t rmmio_size;
|
||||
void __iomem *rmmio;
|
||||
|
@ -881,6 +881,14 @@ mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
.y2 = fb->height,
|
||||
};
|
||||
|
||||
/*
|
||||
* Concurrent operations could possibly trigger a call to
|
||||
* drm_connector_helper_funcs.get_modes by trying to read the
|
||||
* display modes. Protect access to I/O registers by acquiring
|
||||
* the I/O-register lock.
|
||||
*/
|
||||
mutex_lock(&mdev->rmmio_lock);
|
||||
|
||||
if (mdev->type == G200_WB || mdev->type == G200_EW3)
|
||||
mgag200_g200wb_hold_bmc(mdev);
|
||||
|
||||
@ -904,6 +912,8 @@ mgag200_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
mgag200_enable_display(mdev);
|
||||
|
||||
mgag200_handle_damage(mdev, fb, &fullscreen, &shadow_plane_state->data[0]);
|
||||
|
||||
mutex_unlock(&mdev->rmmio_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -963,8 +973,12 @@ mgag200_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
|
||||
if (!fb)
|
||||
return;
|
||||
|
||||
mutex_lock(&mdev->rmmio_lock);
|
||||
|
||||
if (drm_atomic_helper_damage_merged(old_state, state, &damage))
|
||||
mgag200_handle_damage(mdev, fb, &damage, &shadow_plane_state->data[0]);
|
||||
|
||||
mutex_unlock(&mdev->rmmio_lock);
|
||||
}
|
||||
|
||||
static struct drm_crtc_state *
|
||||
|
@ -52,6 +52,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
|
||||
.hs_wdth_shift = 24,
|
||||
.has_overlay = false,
|
||||
.has_ctrl2 = false,
|
||||
.has_crc32 = false,
|
||||
},
|
||||
[MXSFB_V4] = {
|
||||
.transfer_count = LCDC_V4_TRANSFER_COUNT,
|
||||
@ -61,6 +62,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
|
||||
.hs_wdth_shift = 18,
|
||||
.has_overlay = false,
|
||||
.has_ctrl2 = true,
|
||||
.has_crc32 = true,
|
||||
},
|
||||
[MXSFB_V6] = {
|
||||
.transfer_count = LCDC_V4_TRANSFER_COUNT,
|
||||
@ -70,6 +72,7 @@ static const struct mxsfb_devdata mxsfb_devdata[] = {
|
||||
.hs_wdth_shift = 18,
|
||||
.has_overlay = true,
|
||||
.has_ctrl2 = true,
|
||||
.has_crc32 = true,
|
||||
},
|
||||
};
|
||||
|
||||
@ -157,12 +160,19 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct drm_device *drm = data;
|
||||
struct mxsfb_drm_private *mxsfb = drm->dev_private;
|
||||
u32 reg;
|
||||
u32 reg, crc;
|
||||
u64 vbc;
|
||||
|
||||
reg = readl(mxsfb->base + LCDC_CTRL1);
|
||||
|
||||
if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
|
||||
if (reg & CTRL1_CUR_FRAME_DONE_IRQ) {
|
||||
drm_crtc_handle_vblank(&mxsfb->crtc);
|
||||
if (mxsfb->crc_active) {
|
||||
crc = readl(mxsfb->base + LCDC_V4_CRC_STAT);
|
||||
vbc = drm_crtc_accurate_vblank_count(&mxsfb->crtc);
|
||||
drm_crtc_add_crc_entry(&mxsfb->crtc, true, vbc, &crc);
|
||||
}
|
||||
}
|
||||
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
|
||||
|
@ -23,6 +23,7 @@ struct mxsfb_devdata {
|
||||
unsigned int hs_wdth_shift;
|
||||
bool has_overlay;
|
||||
bool has_ctrl2;
|
||||
bool has_crc32;
|
||||
};
|
||||
|
||||
struct mxsfb_drm_private {
|
||||
@ -44,6 +45,8 @@ struct mxsfb_drm_private {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector *connector;
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
bool crc_active;
|
||||
};
|
||||
|
||||
static inline struct mxsfb_drm_private *
|
||||
|
@ -439,6 +439,41 @@ static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
|
||||
writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
|
||||
}
|
||||
|
||||
static int mxsfb_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
|
||||
{
|
||||
struct mxsfb_drm_private *mxsfb;
|
||||
|
||||
if (!crtc)
|
||||
return -ENODEV;
|
||||
|
||||
mxsfb = to_mxsfb_drm_private(crtc->dev);
|
||||
|
||||
if (source && strcmp(source, "auto") == 0)
|
||||
mxsfb->crc_active = true;
|
||||
else if (!source)
|
||||
mxsfb->crc_active = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxsfb_crtc_verify_crc_source(struct drm_crtc *crtc,
|
||||
const char *source, size_t *values_cnt)
|
||||
{
|
||||
if (!crtc)
|
||||
return -ENODEV;
|
||||
|
||||
if (source && strcmp(source, "auto") != 0) {
|
||||
DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
|
||||
source, crtc->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*values_cnt = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
|
||||
.atomic_check = mxsfb_crtc_atomic_check,
|
||||
.atomic_flush = mxsfb_crtc_atomic_flush,
|
||||
@ -457,6 +492,19 @@ static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
|
||||
.disable_vblank = mxsfb_crtc_disable_vblank,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs mxsfb_crtc_with_crc_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,
|
||||
.enable_vblank = mxsfb_crtc_enable_vblank,
|
||||
.disable_vblank = mxsfb_crtc_disable_vblank,
|
||||
.set_crc_source = mxsfb_crtc_set_crc_source,
|
||||
.verify_crc_source = mxsfb_crtc_verify_crc_source,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Encoder
|
||||
*/
|
||||
@ -645,9 +693,16 @@ int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
|
||||
ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
|
||||
&mxsfb->planes.primary, NULL,
|
||||
&mxsfb_crtc_funcs, NULL);
|
||||
if (mxsfb->devdata->has_crc32) {
|
||||
ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
|
||||
&mxsfb->planes.primary, NULL,
|
||||
&mxsfb_crtc_with_crc_funcs,
|
||||
NULL);
|
||||
} else {
|
||||
ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
|
||||
&mxsfb->planes.primary, NULL,
|
||||
&mxsfb_crtc_funcs, NULL);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define LCDC_VDCTRL2 0x90
|
||||
#define LCDC_VDCTRL3 0xa0
|
||||
#define LCDC_VDCTRL4 0xb0
|
||||
#define LCDC_V4_CRC_STAT 0x1a0
|
||||
#define LCDC_V4_DEBUG0 0x1d0
|
||||
#define LCDC_V3_DEBUG0 0x1f0
|
||||
#define LCDC_AS_CTRL 0x210
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
|
||||
#include "nouveau_bo.h"
|
||||
@ -558,9 +559,7 @@ nv50_wndw_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state)
|
||||
asyw->image.handle[0] = ctxdma->object.handle;
|
||||
}
|
||||
|
||||
ret = dma_resv_get_singleton(nvbo->bo.base.resv,
|
||||
DMA_RESV_USAGE_WRITE,
|
||||
&asyw->state.fence);
|
||||
ret = drm_gem_plane_helper_prepare_fb(plane, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -62,4 +62,6 @@ void nvkm_subdev_intr(struct nvkm_subdev *);
|
||||
#define nvkm_debug(s,f,a...) nvkm_printk((s), DEBUG, info, f, ##a)
|
||||
#define nvkm_trace(s,f,a...) nvkm_printk((s), TRACE, info, f, ##a)
|
||||
#define nvkm_spam(s,f,a...) nvkm_printk((s), SPAM, dbg, f, ##a)
|
||||
|
||||
#define nvkm_error_ratelimited(s,f,a...) nvkm_printk((s), ERROR, err_ratelimited, f, ##a)
|
||||
#endif
|
||||
|
@ -255,19 +255,13 @@ nouveau_drm_debugfs_init(struct drm_minor *minor)
|
||||
int
|
||||
nouveau_debugfs_init(struct nouveau_drm *drm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
drm->debugfs = kzalloc(sizeof(*drm->debugfs), GFP_KERNEL);
|
||||
if (!drm->debugfs)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = nvif_object_ctor(&drm->client.device.object, "debugfsCtrl", 0,
|
||||
NVIF_CLASS_CONTROL, NULL, 0,
|
||||
&drm->debugfs->ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return nvif_object_ctor(&drm->client.device.object, "debugfsCtrl", 0,
|
||||
NVIF_CLASS_CONTROL, NULL, 0,
|
||||
&drm->debugfs->ctrl);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -224,7 +224,6 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
|
||||
&fctx->lock, fctx->context, ++fctx->sequence);
|
||||
kref_get(&fctx->fence_ref);
|
||||
|
||||
trace_dma_fence_emit(&fence->base);
|
||||
ret = fctx->emit(fence);
|
||||
if (!ret) {
|
||||
dma_fence_get(&fence->base);
|
||||
|
@ -35,13 +35,13 @@ gf100_bus_intr(struct nvkm_bus *bus)
|
||||
u32 addr = nvkm_rd32(device, 0x009084);
|
||||
u32 data = nvkm_rd32(device, 0x009088);
|
||||
|
||||
nvkm_error(subdev,
|
||||
"MMIO %s of %08x FAULT at %06x [ %s%s%s]\n",
|
||||
(addr & 0x00000002) ? "write" : "read", data,
|
||||
(addr & 0x00fffffc),
|
||||
(stat & 0x00000002) ? "!ENGINE " : "",
|
||||
(stat & 0x00000004) ? "PRIVRING " : "",
|
||||
(stat & 0x00000008) ? "TIMEOUT " : "");
|
||||
nvkm_error_ratelimited(subdev,
|
||||
"MMIO %s of %08x FAULT at %06x [ %s%s%s]\n",
|
||||
(addr & 0x00000002) ? "write" : "read", data,
|
||||
(addr & 0x00fffffc),
|
||||
(stat & 0x00000002) ? "!ENGINE " : "",
|
||||
(stat & 0x00000004) ? "PRIVRING " : "",
|
||||
(stat & 0x00000008) ? "TIMEOUT " : "");
|
||||
|
||||
nvkm_wr32(device, 0x009084, 0x00000000);
|
||||
nvkm_wr32(device, 0x001100, (stat & 0x0000000e));
|
||||
|
@ -45,9 +45,9 @@ nv31_bus_intr(struct nvkm_bus *bus)
|
||||
u32 addr = nvkm_rd32(device, 0x009084);
|
||||
u32 data = nvkm_rd32(device, 0x009088);
|
||||
|
||||
nvkm_error(subdev, "MMIO %s of %08x FAULT at %06x\n",
|
||||
(addr & 0x00000002) ? "write" : "read", data,
|
||||
(addr & 0x00fffffc));
|
||||
nvkm_error_ratelimited(subdev, "MMIO %s of %08x FAULT at %06x\n",
|
||||
(addr & 0x00000002) ? "write" : "read", data,
|
||||
(addr & 0x00fffffc));
|
||||
|
||||
stat &= ~0x00000008;
|
||||
nvkm_wr32(device, 0x001100, 0x00000008);
|
||||
|
@ -60,9 +60,9 @@ nv50_bus_intr(struct nvkm_bus *bus)
|
||||
u32 addr = nvkm_rd32(device, 0x009084);
|
||||
u32 data = nvkm_rd32(device, 0x009088);
|
||||
|
||||
nvkm_error(subdev, "MMIO %s of %08x FAULT at %06x\n",
|
||||
(addr & 0x00000002) ? "write" : "read", data,
|
||||
(addr & 0x00fffffc));
|
||||
nvkm_error_ratelimited(subdev, "MMIO %s of %08x FAULT at %06x\n",
|
||||
(addr & 0x00000002) ? "write" : "read", data,
|
||||
(addr & 0x00fffffc));
|
||||
|
||||
stat &= ~0x00000008;
|
||||
nvkm_wr32(device, 0x001100, 0x00000008);
|
||||
|
@ -32,7 +32,6 @@ g98_devinit_disable(struct nvkm_devinit *init)
|
||||
struct nvkm_device *device = init->subdev.device;
|
||||
u32 r001540 = nvkm_rd32(device, 0x001540);
|
||||
u32 r00154c = nvkm_rd32(device, 0x00154c);
|
||||
u64 disable = 0ULL;
|
||||
|
||||
if (!(r001540 & 0x40000000)) {
|
||||
nvkm_subdev_disable(device, NVKM_ENGINE_MSPDEC, 0);
|
||||
@ -47,7 +46,7 @@ g98_devinit_disable(struct nvkm_devinit *init)
|
||||
if (!(r00154c & 0x00000040))
|
||||
nvkm_subdev_disable(device, NVKM_ENGINE_SEC, 0);
|
||||
|
||||
return disable;
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
static const struct nvkm_devinit_func
|
||||
|
@ -68,7 +68,6 @@ gf100_devinit_disable(struct nvkm_devinit *init)
|
||||
{
|
||||
struct nvkm_device *device = init->subdev.device;
|
||||
u32 r022500 = nvkm_rd32(device, 0x022500);
|
||||
u64 disable = 0ULL;
|
||||
|
||||
if (r022500 & 0x00000001)
|
||||
nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0);
|
||||
@ -87,7 +86,7 @@ gf100_devinit_disable(struct nvkm_devinit *init)
|
||||
if (r022500 & 0x00000200)
|
||||
nvkm_subdev_disable(device, NVKM_ENGINE_CE, 1);
|
||||
|
||||
return disable;
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -32,7 +32,6 @@ gm107_devinit_disable(struct nvkm_devinit *init)
|
||||
struct nvkm_device *device = init->subdev.device;
|
||||
u32 r021c00 = nvkm_rd32(device, 0x021c00);
|
||||
u32 r021c04 = nvkm_rd32(device, 0x021c04);
|
||||
u64 disable = 0ULL;
|
||||
|
||||
if (r021c00 & 0x00000001)
|
||||
nvkm_subdev_disable(device, NVKM_ENGINE_CE, 0);
|
||||
@ -41,7 +40,7 @@ gm107_devinit_disable(struct nvkm_devinit *init)
|
||||
if (r021c04 & 0x00000001)
|
||||
nvkm_subdev_disable(device, NVKM_ENGINE_DISP, 0);
|
||||
|
||||
return disable;
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
static const struct nvkm_devinit_func
|
||||
|
@ -720,7 +720,7 @@ static const struct drm_display_mode ampire_am_1280800n3tzqw_t00h_mode = {
|
||||
static const struct panel_desc ampire_am_1280800n3tzqw_t00h = {
|
||||
.modes = &ire_am_1280800n3tzqw_t00h_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 6,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 217,
|
||||
.height = 136,
|
||||
@ -3310,6 +3310,41 @@ static const struct panel_desc starry_kr070pe2t = {
|
||||
.connector_type = DRM_MODE_CONNECTOR_DPI,
|
||||
};
|
||||
|
||||
static const struct display_timing startek_kd070wvfpa_mode = {
|
||||
.pixelclock = { 25200000, 27200000, 30500000 },
|
||||
.hactive = { 800, 800, 800 },
|
||||
.hfront_porch = { 19, 44, 115 },
|
||||
.hback_porch = { 5, 16, 101 },
|
||||
.hsync_len = { 1, 2, 100 },
|
||||
.vactive = { 480, 480, 480 },
|
||||
.vfront_porch = { 5, 43, 67 },
|
||||
.vback_porch = { 5, 5, 67 },
|
||||
.vsync_len = { 1, 2, 66 },
|
||||
.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
|
||||
DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
|
||||
DISPLAY_FLAGS_SYNC_POSEDGE,
|
||||
};
|
||||
|
||||
static const struct panel_desc startek_kd070wvfpa = {
|
||||
.timings = &startek_kd070wvfpa_mode,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 152,
|
||||
.height = 91,
|
||||
},
|
||||
.delay = {
|
||||
.prepare = 20,
|
||||
.enable = 200,
|
||||
.disable = 200,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.connector_type = DRM_MODE_CONNECTOR_DPI,
|
||||
.bus_flags = DRM_BUS_FLAG_DE_HIGH |
|
||||
DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
|
||||
DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE,
|
||||
};
|
||||
|
||||
static const struct display_timing tsd_tst043015cmhx_timing = {
|
||||
.pixelclock = { 5000000, 9000000, 12000000 },
|
||||
.hactive = { 480, 480, 480 },
|
||||
@ -4019,6 +4054,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "starry,kr070pe2t",
|
||||
.data = &starry_kr070pe2t,
|
||||
}, {
|
||||
.compatible = "startek,kd070wvfpa",
|
||||
.data = &startek_kd070wvfpa,
|
||||
}, {
|
||||
.compatible = "team-source-display,tst043015cmhx",
|
||||
.data = &tsd_tst043015cmhx,
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
|
||||
#include "qxl_drv.h"
|
||||
#include "qxl_object.h"
|
||||
@ -829,6 +830,7 @@ static int qxl_plane_prepare_fb(struct drm_plane *plane,
|
||||
struct qxl_device *qdev = to_qxl(plane->dev);
|
||||
struct drm_gem_object *obj;
|
||||
struct qxl_bo *user_bo;
|
||||
int ret;
|
||||
|
||||
if (!new_state->fb)
|
||||
return 0;
|
||||
@ -852,7 +854,11 @@ static int qxl_plane_prepare_fb(struct drm_plane *plane,
|
||||
qxl_free_cursor(old_cursor_bo);
|
||||
}
|
||||
|
||||
return qxl_bo_pin(user_bo);
|
||||
ret = qxl_bo_pin(user_bo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return drm_gem_plane_helper_prepare_fb(plane, new_state);
|
||||
}
|
||||
|
||||
static void qxl_plane_cleanup_fb(struct drm_plane *plane,
|
||||
|
@ -22,8 +22,22 @@ config DRM_ROCKCHIP
|
||||
|
||||
if DRM_ROCKCHIP
|
||||
|
||||
config ROCKCHIP_VOP
|
||||
bool "Rockchip VOP driver"
|
||||
default y
|
||||
help
|
||||
This selects support for the VOP driver. You should enable it
|
||||
on older SoCs.
|
||||
|
||||
config ROCKCHIP_VOP2
|
||||
bool "Rockchip VOP2 driver"
|
||||
help
|
||||
This selects support for the VOP2 driver. The VOP2 hardware is
|
||||
first found on the RK3568.
|
||||
|
||||
config ROCKCHIP_ANALOGIX_DP
|
||||
bool "Rockchip specific extensions for Analogix DP driver"
|
||||
depends on ROCKCHIP_VOP
|
||||
select DRM_DISPLAY_DP_HELPER
|
||||
help
|
||||
This selects support for Rockchip SoC specific extensions
|
||||
|
@ -4,8 +4,10 @@
|
||||
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
|
||||
|
||||
rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
|
||||
rockchip_drm_gem.o rockchip_drm_vop.o rockchip_vop_reg.o
|
||||
rockchip_drm_gem.o
|
||||
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_VOP2) += rockchip_drm_vop2.o rockchip_vop2_reg.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
|
||||
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
|
||||
|
@ -40,8 +40,6 @@
|
||||
|
||||
#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS 100
|
||||
|
||||
#define to_dp(nm) container_of(nm, struct rockchip_dp_device, nm)
|
||||
|
||||
/**
|
||||
* struct rockchip_dp_chip_data - splite the grf setting of kind of chips
|
||||
* @lcdsel_grf_reg: grf register offset of lcdc select
|
||||
@ -59,7 +57,7 @@ struct rockchip_dp_chip_data {
|
||||
struct rockchip_dp_device {
|
||||
struct drm_device *drm_dev;
|
||||
struct device *dev;
|
||||
struct drm_encoder encoder;
|
||||
struct rockchip_encoder encoder;
|
||||
struct drm_display_mode mode;
|
||||
|
||||
struct clk *pclk;
|
||||
@ -73,6 +71,18 @@ struct rockchip_dp_device {
|
||||
struct analogix_dp_plat_data plat_data;
|
||||
};
|
||||
|
||||
static struct rockchip_dp_device *encoder_to_dp(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct rockchip_dp_device, encoder);
|
||||
}
|
||||
|
||||
static struct rockchip_dp_device *pdata_encoder_to_dp(struct analogix_dp_plat_data *plat_data)
|
||||
{
|
||||
return container_of(plat_data, struct rockchip_dp_device, plat_data);
|
||||
}
|
||||
|
||||
static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
|
||||
{
|
||||
reset_control_assert(dp->rst);
|
||||
@ -84,7 +94,7 @@ static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
|
||||
|
||||
static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
|
||||
{
|
||||
struct rockchip_dp_device *dp = to_dp(plat_data);
|
||||
struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(dp->pclk);
|
||||
@ -105,7 +115,7 @@ static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
|
||||
|
||||
static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
|
||||
{
|
||||
struct rockchip_dp_device *dp = to_dp(plat_data);
|
||||
struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
|
||||
|
||||
clk_disable_unprepare(dp->pclk);
|
||||
|
||||
@ -166,7 +176,7 @@ struct drm_crtc *rockchip_dp_drm_get_new_crtc(struct drm_encoder *encoder,
|
||||
static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct rockchip_dp_device *dp = to_dp(encoder);
|
||||
struct rockchip_dp_device *dp = encoder_to_dp(encoder);
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *old_crtc_state;
|
||||
int ret;
|
||||
@ -208,7 +218,7 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
|
||||
static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct rockchip_dp_device *dp = to_dp(encoder);
|
||||
struct rockchip_dp_device *dp = encoder_to_dp(encoder);
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *new_crtc_state = NULL;
|
||||
int ret;
|
||||
@ -297,7 +307,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
|
||||
|
||||
static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
|
||||
{
|
||||
struct drm_encoder *encoder = &dp->encoder;
|
||||
struct drm_encoder *encoder = &dp->encoder.encoder;
|
||||
struct drm_device *drm_dev = dp->drm_dev;
|
||||
struct device *dev = dp->dev;
|
||||
int ret;
|
||||
@ -333,7 +343,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
|
||||
return ret;
|
||||
}
|
||||
|
||||
dp->plat_data.encoder = &dp->encoder;
|
||||
dp->plat_data.encoder = &dp->encoder.encoder;
|
||||
|
||||
ret = analogix_dp_bind(dp->adp, drm_dev);
|
||||
if (ret)
|
||||
@ -341,7 +351,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
|
||||
|
||||
return 0;
|
||||
err_cleanup_encoder:
|
||||
dp->encoder.funcs->destroy(&dp->encoder);
|
||||
dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -351,7 +361,7 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
|
||||
struct rockchip_dp_device *dp = dev_get_drvdata(dev);
|
||||
|
||||
analogix_dp_unbind(dp->adp);
|
||||
dp->encoder.funcs->destroy(&dp->encoder);
|
||||
dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
|
||||
}
|
||||
|
||||
static const struct component_ops rockchip_dp_component_ops = {
|
||||
|
@ -26,11 +26,17 @@
|
||||
#include "cdn-dp-reg.h"
|
||||
#include "rockchip_drm_vop.h"
|
||||
|
||||
#define connector_to_dp(c) \
|
||||
container_of(c, struct cdn_dp_device, connector)
|
||||
static inline struct cdn_dp_device *connector_to_dp(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct cdn_dp_device, connector);
|
||||
}
|
||||
|
||||
#define encoder_to_dp(c) \
|
||||
container_of(c, struct cdn_dp_device, encoder)
|
||||
static inline struct cdn_dp_device *encoder_to_dp(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct cdn_dp_device, encoder);
|
||||
}
|
||||
|
||||
#define GRF_SOC_CON9 0x6224
|
||||
#define DP_SEL_VOP_LIT BIT(12)
|
||||
@ -48,7 +54,7 @@ struct cdn_dp_data {
|
||||
u8 max_phy;
|
||||
};
|
||||
|
||||
struct cdn_dp_data rk3399_cdn_dp = {
|
||||
static struct cdn_dp_data rk3399_cdn_dp = {
|
||||
.max_phy = 2,
|
||||
};
|
||||
|
||||
@ -1050,7 +1056,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
|
||||
|
||||
INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
|
||||
|
||||
encoder = &dp->encoder;
|
||||
encoder = &dp->encoder.encoder;
|
||||
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
|
||||
dev->of_node);
|
||||
@ -1115,7 +1121,7 @@ err_free_encoder:
|
||||
static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct cdn_dp_device *dp = dev_get_drvdata(dev);
|
||||
struct drm_encoder *encoder = &dp->encoder;
|
||||
struct drm_encoder *encoder = &dp->encoder.encoder;
|
||||
struct drm_connector *connector = &dp->connector;
|
||||
|
||||
cancel_work_sync(&dp->event_work);
|
||||
|
@ -66,7 +66,7 @@ struct cdn_dp_device {
|
||||
struct device *dev;
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
struct rockchip_encoder encoder;
|
||||
struct drm_display_mode mode;
|
||||
struct platform_device *audio_pdev;
|
||||
struct work_struct event_work;
|
||||
|
@ -181,8 +181,6 @@
|
||||
|
||||
#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
|
||||
|
||||
#define to_dsi(nm) container_of(nm, struct dw_mipi_dsi_rockchip, nm)
|
||||
|
||||
enum {
|
||||
DW_DSI_USAGE_IDLE,
|
||||
DW_DSI_USAGE_DSI,
|
||||
@ -236,7 +234,7 @@ struct rockchip_dw_dsi_chip_data {
|
||||
|
||||
struct dw_mipi_dsi_rockchip {
|
||||
struct device *dev;
|
||||
struct drm_encoder encoder;
|
||||
struct rockchip_encoder encoder;
|
||||
void __iomem *base;
|
||||
|
||||
struct regmap *grf_regmap;
|
||||
@ -271,6 +269,13 @@ struct dw_mipi_dsi_rockchip {
|
||||
bool dsi_bound;
|
||||
};
|
||||
|
||||
static struct dw_mipi_dsi_rockchip *to_dsi(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct dw_mipi_dsi_rockchip, encoder);
|
||||
}
|
||||
|
||||
struct dphy_pll_parameter_map {
|
||||
unsigned int max_mbps;
|
||||
u8 hsfreqrange;
|
||||
@ -770,7 +775,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
|
||||
int ret, mux;
|
||||
|
||||
mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
|
||||
&dsi->encoder);
|
||||
&dsi->encoder.encoder);
|
||||
if (mux < 0)
|
||||
return;
|
||||
|
||||
@ -801,7 +806,7 @@ dw_mipi_dsi_encoder_helper_funcs = {
|
||||
static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
|
||||
struct drm_device *drm_dev)
|
||||
{
|
||||
struct drm_encoder *encoder = &dsi->encoder;
|
||||
struct drm_encoder *encoder = &dsi->encoder.encoder;
|
||||
int ret;
|
||||
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
|
||||
@ -959,7 +964,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
|
||||
goto out_pll_clk;
|
||||
}
|
||||
|
||||
ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder);
|
||||
ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder.encoder);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret);
|
||||
goto out_pll_clk;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/bridge/dw_hdmi.h>
|
||||
#include <drm/drm_edid.h>
|
||||
@ -50,6 +51,10 @@
|
||||
#define RK3399_GRF_SOC_CON20 0x6250
|
||||
#define RK3399_HDMI_LCDC_SEL BIT(6)
|
||||
|
||||
#define RK3568_GRF_VO_CON1 0x0364
|
||||
#define RK3568_HDMI_SDAIN_MSK BIT(15)
|
||||
#define RK3568_HDMI_SCLIN_MSK BIT(14)
|
||||
|
||||
#define HIWORD_UPDATE(val, mask) (val | (mask) << 16)
|
||||
|
||||
/**
|
||||
@ -67,15 +72,22 @@ struct rockchip_hdmi_chip_data {
|
||||
struct rockchip_hdmi {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct drm_encoder encoder;
|
||||
struct rockchip_encoder encoder;
|
||||
const struct rockchip_hdmi_chip_data *chip_data;
|
||||
struct clk *vpll_clk;
|
||||
struct clk *ref_clk;
|
||||
struct clk *grf_clk;
|
||||
struct dw_hdmi *hdmi;
|
||||
struct regulator *avdd_0v9;
|
||||
struct regulator *avdd_1v8;
|
||||
struct phy *phy;
|
||||
};
|
||||
|
||||
#define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x)
|
||||
static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct rockchip_hdmi, encoder);
|
||||
}
|
||||
|
||||
static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
|
||||
{
|
||||
@ -196,14 +208,15 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
return PTR_ERR(hdmi->regmap);
|
||||
}
|
||||
|
||||
hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
|
||||
if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
|
||||
hdmi->vpll_clk = NULL;
|
||||
} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
|
||||
hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref");
|
||||
if (!hdmi->ref_clk)
|
||||
hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll");
|
||||
|
||||
if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) {
|
||||
return -EPROBE_DEFER;
|
||||
} else if (IS_ERR(hdmi->vpll_clk)) {
|
||||
DRM_DEV_ERROR(hdmi->dev, "failed to get vpll clock\n");
|
||||
return PTR_ERR(hdmi->vpll_clk);
|
||||
} else if (IS_ERR(hdmi->ref_clk)) {
|
||||
DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n");
|
||||
return PTR_ERR(hdmi->ref_clk);
|
||||
}
|
||||
|
||||
hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
|
||||
@ -216,6 +229,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
|
||||
return PTR_ERR(hdmi->grf_clk);
|
||||
}
|
||||
|
||||
hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9");
|
||||
if (IS_ERR(hdmi->avdd_0v9))
|
||||
return PTR_ERR(hdmi->avdd_0v9);
|
||||
|
||||
hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8");
|
||||
if (IS_ERR(hdmi->avdd_1v8))
|
||||
return PTR_ERR(hdmi->avdd_1v8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -257,7 +278,7 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
|
||||
|
||||
clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
|
||||
clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000);
|
||||
}
|
||||
|
||||
static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
|
||||
@ -467,6 +488,19 @@ static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
|
||||
.use_drm_infoframe = true,
|
||||
};
|
||||
|
||||
static struct rockchip_hdmi_chip_data rk3568_chip_data = {
|
||||
.lcdsel_grf_reg = -1,
|
||||
};
|
||||
|
||||
static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
|
||||
.mode_valid = dw_hdmi_rockchip_mode_valid,
|
||||
.mpll_cfg = rockchip_mpll_cfg,
|
||||
.cur_ctr = rockchip_cur_ctr,
|
||||
.phy_config = rockchip_phy_config,
|
||||
.phy_data = &rk3568_chip_data,
|
||||
.use_drm_infoframe = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3228-dw-hdmi",
|
||||
.data = &rk3228_hdmi_drv_data
|
||||
@ -480,6 +514,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3399-dw-hdmi",
|
||||
.data = &rk3399_hdmi_drv_data
|
||||
},
|
||||
{ .compatible = "rockchip,rk3568-dw-hdmi",
|
||||
.data = &rk3568_hdmi_drv_data
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
|
||||
@ -511,9 +548,12 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
hdmi->dev = &pdev->dev;
|
||||
hdmi->chip_data = plat_data->phy_data;
|
||||
plat_data->phy_data = hdmi;
|
||||
encoder = &hdmi->encoder;
|
||||
encoder = &hdmi->encoder.encoder;
|
||||
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
|
||||
rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
|
||||
dev->of_node, 0, 0);
|
||||
|
||||
/*
|
||||
* If we failed to find the CRTC(s) which this encoder is
|
||||
* supposed to be connected to, it's because the CRTC has
|
||||
@ -537,11 +577,31 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->vpll_clk);
|
||||
ret = regulator_enable(hdmi->avdd_0v9);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
|
||||
DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret);
|
||||
goto err_avdd_0v9;
|
||||
}
|
||||
|
||||
ret = regulator_enable(hdmi->avdd_1v8);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret);
|
||||
goto err_avdd_1v8;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hdmi->ref_clk);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
if (hdmi->chip_data == &rk3568_chip_data) {
|
||||
regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
|
||||
HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
|
||||
RK3568_HDMI_SCLIN_MSK,
|
||||
RK3568_HDMI_SDAIN_MSK |
|
||||
RK3568_HDMI_SCLIN_MSK));
|
||||
}
|
||||
|
||||
drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
|
||||
@ -557,10 +617,19 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
*/
|
||||
if (IS_ERR(hdmi->hdmi)) {
|
||||
ret = PTR_ERR(hdmi->hdmi);
|
||||
drm_encoder_cleanup(encoder);
|
||||
clk_disable_unprepare(hdmi->vpll_clk);
|
||||
goto err_bind;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_bind:
|
||||
drm_encoder_cleanup(encoder);
|
||||
clk_disable_unprepare(hdmi->ref_clk);
|
||||
err_clk:
|
||||
regulator_disable(hdmi->avdd_1v8);
|
||||
err_avdd_1v8:
|
||||
regulator_disable(hdmi->avdd_0v9);
|
||||
err_avdd_0v9:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -570,7 +639,10 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
|
||||
struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
|
||||
dw_hdmi_unbind(hdmi->hdmi);
|
||||
clk_disable_unprepare(hdmi->vpll_clk);
|
||||
clk_disable_unprepare(hdmi->ref_clk);
|
||||
|
||||
regulator_disable(hdmi->avdd_1v8);
|
||||
regulator_disable(hdmi->avdd_0v9);
|
||||
}
|
||||
|
||||
static const struct component_ops dw_hdmi_rockchip_ops = {
|
||||
|
@ -26,11 +26,8 @@
|
||||
|
||||
#include "inno_hdmi.h"
|
||||
|
||||
#define to_inno_hdmi(x) container_of(x, struct inno_hdmi, x)
|
||||
|
||||
struct hdmi_data_info {
|
||||
int vic;
|
||||
bool sink_is_hdmi;
|
||||
bool sink_has_audio;
|
||||
unsigned int enc_in_format;
|
||||
unsigned int enc_out_format;
|
||||
@ -56,7 +53,7 @@ struct inno_hdmi {
|
||||
void __iomem *regs;
|
||||
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
struct rockchip_encoder encoder;
|
||||
|
||||
struct inno_hdmi_i2c *i2c;
|
||||
struct i2c_adapter *ddc;
|
||||
@ -67,6 +64,18 @@ struct inno_hdmi {
|
||||
struct drm_display_mode previous_mode;
|
||||
};
|
||||
|
||||
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct inno_hdmi, encoder);
|
||||
}
|
||||
|
||||
static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct inno_hdmi, connector);
|
||||
}
|
||||
|
||||
enum {
|
||||
CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
|
||||
CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
|
||||
@ -433,6 +442,8 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
|
||||
static int inno_hdmi_setup(struct inno_hdmi *hdmi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_display_info *display = &hdmi->connector.display_info;
|
||||
|
||||
hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
|
||||
|
||||
hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
|
||||
@ -452,13 +463,13 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
|
||||
|
||||
/* Set HDMI Mode */
|
||||
hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
|
||||
v_HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
|
||||
v_HDMI_DVI(display->is_hdmi));
|
||||
|
||||
inno_hdmi_config_video_timing(hdmi, mode);
|
||||
|
||||
inno_hdmi_config_video_csc(hdmi);
|
||||
|
||||
if (hdmi->hdmi_data.sink_is_hdmi) {
|
||||
if (display->is_hdmi) {
|
||||
inno_hdmi_config_video_avi(hdmi, mode);
|
||||
inno_hdmi_config_video_vsi(hdmi, mode);
|
||||
}
|
||||
@ -483,7 +494,7 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
{
|
||||
struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
|
||||
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
|
||||
|
||||
inno_hdmi_setup(hdmi, adj_mode);
|
||||
|
||||
@ -493,14 +504,14 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
|
||||
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
|
||||
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
|
||||
|
||||
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
|
||||
}
|
||||
|
||||
static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct inno_hdmi *hdmi = to_inno_hdmi(encoder);
|
||||
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
|
||||
|
||||
inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
|
||||
}
|
||||
@ -536,7 +547,7 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
|
||||
static enum drm_connector_status
|
||||
inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct inno_hdmi *hdmi = to_inno_hdmi(connector);
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
|
||||
return (hdmi_readb(hdmi, HDMI_STATUS) & m_HOTPLUG) ?
|
||||
connector_status_connected : connector_status_disconnected;
|
||||
@ -544,7 +555,7 @@ inno_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct inno_hdmi *hdmi = to_inno_hdmi(connector);
|
||||
struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
|
||||
struct edid *edid;
|
||||
int ret = 0;
|
||||
|
||||
@ -553,7 +564,6 @@ static int inno_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
|
||||
edid = drm_get_edid(connector, hdmi->ddc);
|
||||
if (edid) {
|
||||
hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
|
||||
hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
@ -599,7 +609,7 @@ static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
|
||||
|
||||
static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
|
||||
{
|
||||
struct drm_encoder *encoder = &hdmi->encoder;
|
||||
struct drm_encoder *encoder = &hdmi->encoder.encoder;
|
||||
struct device *dev = hdmi->dev;
|
||||
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
|
||||
@ -879,7 +889,7 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
|
||||
return 0;
|
||||
err_cleanup_hdmi:
|
||||
hdmi->connector.funcs->destroy(&hdmi->connector);
|
||||
hdmi->encoder.funcs->destroy(&hdmi->encoder);
|
||||
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
|
||||
err_put_adapter:
|
||||
i2c_put_adapter(hdmi->ddc);
|
||||
err_disable_clk:
|
||||
@ -893,7 +903,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
|
||||
struct inno_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
|
||||
hdmi->connector.funcs->destroy(&hdmi->connector);
|
||||
hdmi->encoder.funcs->destroy(&hdmi->encoder);
|
||||
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
|
||||
|
||||
i2c_put_adapter(hdmi->ddc);
|
||||
clk_disable_unprepare(hdmi->pclk);
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
struct hdmi_data_info {
|
||||
int vic; /* The CEA Video ID (VIC) of the current drm display mode. */
|
||||
bool sink_is_hdmi;
|
||||
unsigned int enc_out_format;
|
||||
unsigned int colorimetry;
|
||||
};
|
||||
@ -47,7 +46,7 @@ struct rk3066_hdmi {
|
||||
void __iomem *regs;
|
||||
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
struct rockchip_encoder encoder;
|
||||
|
||||
struct rk3066_hdmi_i2c *i2c;
|
||||
struct i2c_adapter *ddc;
|
||||
@ -58,7 +57,17 @@ struct rk3066_hdmi {
|
||||
struct drm_display_mode previous_mode;
|
||||
};
|
||||
|
||||
#define to_rk3066_hdmi(x) container_of(x, struct rk3066_hdmi, x)
|
||||
static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct rk3066_hdmi, encoder);
|
||||
}
|
||||
|
||||
static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct rk3066_hdmi, connector);
|
||||
}
|
||||
|
||||
static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset)
|
||||
{
|
||||
@ -317,6 +326,8 @@ static void rk3066_hdmi_config_phy(struct rk3066_hdmi *hdmi)
|
||||
static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_display_info *display = &hdmi->connector.display_info;
|
||||
|
||||
hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
|
||||
hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
|
||||
|
||||
@ -349,7 +360,7 @@ static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi,
|
||||
|
||||
rk3066_hdmi_config_video_timing(hdmi, mode);
|
||||
|
||||
if (hdmi->hdmi_data.sink_is_hdmi) {
|
||||
if (display->is_hdmi) {
|
||||
hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK,
|
||||
HDMI_VIDEO_MODE_HDMI);
|
||||
rk3066_hdmi_config_avi(hdmi, mode);
|
||||
@ -380,7 +391,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
{
|
||||
struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
|
||||
struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
|
||||
|
||||
/* Store the display mode for plugin/DPMS poweron events. */
|
||||
memcpy(&hdmi->previous_mode, adj_mode, sizeof(hdmi->previous_mode));
|
||||
@ -388,7 +399,7 @@ rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder,
|
||||
|
||||
static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
|
||||
struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
|
||||
int mux, val;
|
||||
|
||||
mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
|
||||
@ -407,7 +418,7 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder)
|
||||
|
||||
static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rk3066_hdmi *hdmi = to_rk3066_hdmi(encoder);
|
||||
struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder);
|
||||
|
||||
DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n");
|
||||
|
||||
@ -455,7 +466,7 @@ struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = {
|
||||
static enum drm_connector_status
|
||||
rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
|
||||
struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
|
||||
|
||||
return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ?
|
||||
connector_status_connected : connector_status_disconnected;
|
||||
@ -463,7 +474,7 @@ rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
|
||||
struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
|
||||
struct edid *edid;
|
||||
int ret = 0;
|
||||
|
||||
@ -472,7 +483,6 @@ static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
|
||||
edid = drm_get_edid(connector, hdmi->ddc);
|
||||
if (edid) {
|
||||
hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
|
||||
drm_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
@ -496,9 +506,9 @@ rk3066_hdmi_connector_mode_valid(struct drm_connector *connector,
|
||||
static struct drm_encoder *
|
||||
rk3066_hdmi_connector_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
struct rk3066_hdmi *hdmi = to_rk3066_hdmi(connector);
|
||||
struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector);
|
||||
|
||||
return &hdmi->encoder;
|
||||
return &hdmi->encoder.encoder;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -538,7 +548,7 @@ struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = {
|
||||
static int
|
||||
rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi)
|
||||
{
|
||||
struct drm_encoder *encoder = &hdmi->encoder;
|
||||
struct drm_encoder *encoder = &hdmi->encoder.encoder;
|
||||
struct device *dev = hdmi->dev;
|
||||
|
||||
encoder->possible_crtcs =
|
||||
@ -816,7 +826,7 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master,
|
||||
|
||||
err_cleanup_hdmi:
|
||||
hdmi->connector.funcs->destroy(&hdmi->connector);
|
||||
hdmi->encoder.funcs->destroy(&hdmi->encoder);
|
||||
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
|
||||
err_disable_i2c:
|
||||
i2c_put_adapter(hdmi->ddc);
|
||||
err_disable_hclk:
|
||||
@ -831,7 +841,7 @@ static void rk3066_hdmi_unbind(struct device *dev, struct device *master,
|
||||
struct rk3066_hdmi *hdmi = dev_get_drvdata(dev);
|
||||
|
||||
hdmi->connector.funcs->destroy(&hdmi->connector);
|
||||
hdmi->encoder.funcs->destroy(&hdmi->encoder);
|
||||
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);
|
||||
|
||||
i2c_put_adapter(hdmi->ddc);
|
||||
clk_disable_unprepare(hdmi->hclk);
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-iommu.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_graph.h>
|
||||
@ -34,7 +33,6 @@
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
static bool is_support_iommu = true;
|
||||
static const struct drm_driver rockchip_drm_driver;
|
||||
|
||||
/*
|
||||
@ -48,7 +46,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
|
||||
struct rockchip_drm_private *private = drm_dev->dev_private;
|
||||
int ret;
|
||||
|
||||
if (!is_support_iommu)
|
||||
if (!private->domain)
|
||||
return 0;
|
||||
|
||||
ret = iommu_attach_device(private->domain, dev);
|
||||
@ -64,12 +62,22 @@ void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
|
||||
struct device *dev)
|
||||
{
|
||||
struct rockchip_drm_private *private = drm_dev->dev_private;
|
||||
struct iommu_domain *domain = private->domain;
|
||||
|
||||
if (!is_support_iommu)
|
||||
if (!private->domain)
|
||||
return;
|
||||
|
||||
iommu_detach_device(domain, dev);
|
||||
iommu_detach_device(private->domain, dev);
|
||||
}
|
||||
|
||||
void rockchip_drm_dma_init_device(struct drm_device *drm_dev,
|
||||
struct device *dev)
|
||||
{
|
||||
struct rockchip_drm_private *private = drm_dev->dev_private;
|
||||
|
||||
if (!device_iommu_mapped(dev))
|
||||
private->iommu_dev = ERR_PTR(-ENODEV);
|
||||
else if (!private->iommu_dev)
|
||||
private->iommu_dev = dev;
|
||||
}
|
||||
|
||||
static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
|
||||
@ -78,10 +86,10 @@ static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
|
||||
struct iommu_domain_geometry *geometry;
|
||||
u64 start, end;
|
||||
|
||||
if (!is_support_iommu)
|
||||
if (IS_ERR_OR_NULL(private->iommu_dev))
|
||||
return 0;
|
||||
|
||||
private->domain = iommu_domain_alloc(&platform_bus_type);
|
||||
private->domain = iommu_domain_alloc(private->iommu_dev->bus);
|
||||
if (!private->domain)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -101,7 +109,7 @@ static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
|
||||
{
|
||||
struct rockchip_drm_private *private = drm_dev->dev_private;
|
||||
|
||||
if (!is_support_iommu)
|
||||
if (!private->domain)
|
||||
return;
|
||||
|
||||
drm_mm_takedown(&private->mm);
|
||||
@ -137,24 +145,24 @@ static int rockchip_drm_bind(struct device *dev)
|
||||
|
||||
drm_dev->dev_private = private;
|
||||
|
||||
ret = rockchip_drm_init_iommu(drm_dev);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
ret = drmm_mode_config_init(drm_dev);
|
||||
if (ret)
|
||||
goto err_iommu_cleanup;
|
||||
goto err_free;
|
||||
|
||||
rockchip_drm_mode_config_init(drm_dev);
|
||||
|
||||
/* Try to bind all sub drivers. */
|
||||
ret = component_bind_all(dev, drm_dev);
|
||||
if (ret)
|
||||
goto err_iommu_cleanup;
|
||||
goto err_free;
|
||||
|
||||
ret = rockchip_drm_init_iommu(drm_dev);
|
||||
if (ret)
|
||||
goto err_unbind_all;
|
||||
|
||||
ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
|
||||
if (ret)
|
||||
goto err_unbind_all;
|
||||
goto err_iommu_cleanup;
|
||||
|
||||
drm_mode_config_reset(drm_dev);
|
||||
|
||||
@ -170,10 +178,10 @@ static int rockchip_drm_bind(struct device *dev)
|
||||
return 0;
|
||||
err_kms_helper_poll_fini:
|
||||
drm_kms_helper_poll_fini(drm_dev);
|
||||
err_unbind_all:
|
||||
component_unbind_all(dev, drm_dev);
|
||||
err_iommu_cleanup:
|
||||
rockchip_iommu_cleanup(drm_dev);
|
||||
err_unbind_all:
|
||||
component_unbind_all(dev, drm_dev);
|
||||
err_free:
|
||||
drm_dev_put(drm_dev);
|
||||
return ret;
|
||||
@ -236,6 +244,39 @@ static const struct dev_pm_ops rockchip_drm_pm_ops = {
|
||||
static struct platform_driver *rockchip_sub_drivers[MAX_ROCKCHIP_SUB_DRIVERS];
|
||||
static int num_rockchip_sub_drivers;
|
||||
|
||||
/*
|
||||
* Get the endpoint id of the remote endpoint of the given encoder. This
|
||||
* information is used by the VOP2 driver to identify the encoder.
|
||||
*
|
||||
* @rkencoder: The encoder to get the remote endpoint id from
|
||||
* @np: The encoder device node
|
||||
* @port: The number of the port leading to the VOP2
|
||||
* @reg: The endpoint number leading to the VOP2
|
||||
*/
|
||||
int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rkencoder,
|
||||
struct device_node *np, int port, int reg)
|
||||
{
|
||||
struct of_endpoint ep;
|
||||
struct device_node *en, *ren;
|
||||
int ret;
|
||||
|
||||
en = of_graph_get_endpoint_by_regs(np, port, reg);
|
||||
if (!en)
|
||||
return -ENOENT;
|
||||
|
||||
ren = of_graph_get_remote_endpoint(en);
|
||||
if (!ren)
|
||||
return -ENOENT;
|
||||
|
||||
ret = of_graph_parse_endpoint(ren, &ep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rkencoder->crtc_endpoint_id = ep.id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a vop endpoint is leading to a rockchip subdriver or bridge.
|
||||
* Should be called from the component bind stage of the drivers
|
||||
@ -342,8 +383,6 @@ static int rockchip_drm_platform_of_probe(struct device *dev)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
struct device_node *iommu;
|
||||
|
||||
port = of_parse_phandle(np, "ports", i);
|
||||
if (!port)
|
||||
break;
|
||||
@ -353,21 +392,7 @@ static int rockchip_drm_platform_of_probe(struct device *dev)
|
||||
continue;
|
||||
}
|
||||
|
||||
iommu = of_parse_phandle(port->parent, "iommus", 0);
|
||||
if (!iommu || !of_device_is_available(iommu)) {
|
||||
DRM_DEV_DEBUG(dev,
|
||||
"no iommu attached for %pOF, using non-iommu buffers\n",
|
||||
port->parent);
|
||||
/*
|
||||
* if there is a crtc not support iommu, force set all
|
||||
* crtc use non-iommu buffer.
|
||||
*/
|
||||
is_support_iommu = false;
|
||||
}
|
||||
|
||||
found = true;
|
||||
|
||||
of_node_put(iommu);
|
||||
of_node_put(port);
|
||||
}
|
||||
|
||||
@ -456,7 +481,8 @@ static int __init rockchip_drm_init(void)
|
||||
return -ENODEV;
|
||||
|
||||
num_rockchip_sub_drivers = 0;
|
||||
ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
|
||||
ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
|
||||
ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
|
||||
ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
|
||||
CONFIG_ROCKCHIP_LVDS);
|
||||
ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#define ROCKCHIP_MAX_FB_BUFFER 3
|
||||
#define ROCKCHIP_MAX_CONNECTOR 2
|
||||
#define ROCKCHIP_MAX_CRTC 2
|
||||
#define ROCKCHIP_MAX_CRTC 4
|
||||
|
||||
struct drm_device;
|
||||
struct drm_connector;
|
||||
@ -31,6 +31,9 @@ struct rockchip_crtc_state {
|
||||
int output_bpc;
|
||||
int output_flags;
|
||||
bool enable_afbc;
|
||||
u32 bus_format;
|
||||
u32 bus_flags;
|
||||
int color_space;
|
||||
};
|
||||
#define to_rockchip_crtc_state(s) \
|
||||
container_of(s, struct rockchip_crtc_state, base)
|
||||
@ -44,16 +47,25 @@ struct rockchip_crtc_state {
|
||||
*/
|
||||
struct rockchip_drm_private {
|
||||
struct iommu_domain *domain;
|
||||
struct device *iommu_dev;
|
||||
struct mutex mm_lock;
|
||||
struct drm_mm mm;
|
||||
};
|
||||
|
||||
struct rockchip_encoder {
|
||||
int crtc_endpoint_id;
|
||||
struct drm_encoder encoder;
|
||||
};
|
||||
|
||||
int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
|
||||
struct device *dev);
|
||||
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
|
||||
struct device *dev);
|
||||
void rockchip_drm_dma_init_device(struct drm_device *drm_dev,
|
||||
struct device *dev);
|
||||
int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
|
||||
|
||||
int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder,
|
||||
struct device_node *np, int port, int reg);
|
||||
int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
|
||||
extern struct platform_driver cdn_dp_driver;
|
||||
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
|
||||
@ -63,4 +75,11 @@ extern struct platform_driver rockchip_dp_driver;
|
||||
extern struct platform_driver rockchip_lvds_driver;
|
||||
extern struct platform_driver vop_platform_driver;
|
||||
extern struct platform_driver rk3066_hdmi_driver;
|
||||
extern struct platform_driver vop2_platform_driver;
|
||||
|
||||
static inline struct rockchip_encoder *to_rockchip_encoder(struct drm_encoder *encoder)
|
||||
{
|
||||
return container_of(encoder, struct rockchip_encoder, encoder);
|
||||
}
|
||||
|
||||
#endif /* _ROCKCHIP_DRM_DRV_H_ */
|
||||
|
@ -134,4 +134,6 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
|
||||
|
||||
dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
|
||||
dev->mode_config.helper_private = &rockchip_mode_config_helpers;
|
||||
|
||||
dev->mode_config.normalize_zpos = true;
|
||||
}
|
||||
|
@ -262,6 +262,18 @@ static bool has_rb_swapped(uint32_t format)
|
||||
}
|
||||
}
|
||||
|
||||
static bool has_uv_swapped(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV61:
|
||||
case DRM_FORMAT_NV42:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static enum vop_data_format vop_convert_format(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
@ -277,10 +289,13 @@ static enum vop_data_format vop_convert_format(uint32_t format)
|
||||
case DRM_FORMAT_BGR565:
|
||||
return VOP_FMT_RGB565;
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV21:
|
||||
return VOP_FMT_YUV420SP;
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV61:
|
||||
return VOP_FMT_YUV422SP;
|
||||
case DRM_FORMAT_NV24:
|
||||
case DRM_FORMAT_NV42:
|
||||
return VOP_FMT_YUV444SP;
|
||||
default:
|
||||
DRM_ERROR("unsupported format[%08x]\n", format);
|
||||
@ -899,7 +914,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
|
||||
unsigned long offset;
|
||||
dma_addr_t dma_addr;
|
||||
uint32_t val;
|
||||
bool rb_swap;
|
||||
bool rb_swap, uv_swap;
|
||||
int win_index = VOP_WIN_TO_INDEX(vop_win);
|
||||
int format;
|
||||
int is_yuv = fb->format->is_yuv;
|
||||
@ -988,6 +1003,9 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
|
||||
y2r_coefficients[i],
|
||||
bt601_yuv2rgb[i]);
|
||||
}
|
||||
|
||||
uv_swap = has_uv_swapped(fb->format->format);
|
||||
VOP_WIN_SET(vop, win, uv_swap, uv_swap);
|
||||
}
|
||||
|
||||
if (win->phy->scl)
|
||||
@ -2118,10 +2136,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
|
||||
vop_win_init(vop);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
vop->len = resource_size(res);
|
||||
vop->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(vop->regs))
|
||||
return PTR_ERR(vop->regs);
|
||||
vop->len = resource_size(res);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
@ -2175,6 +2193,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
rockchip_drm_dma_init_device(drm_dev, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_pm_runtime:
|
||||
|
@ -54,9 +54,23 @@ struct vop_afbc {
|
||||
struct vop_reg enable;
|
||||
struct vop_reg win_sel;
|
||||
struct vop_reg format;
|
||||
struct vop_reg rb_swap;
|
||||
struct vop_reg uv_swap;
|
||||
struct vop_reg auto_gating_en;
|
||||
struct vop_reg block_split_en;
|
||||
struct vop_reg pic_vir_width;
|
||||
struct vop_reg tile_num;
|
||||
struct vop_reg hreg_block_split;
|
||||
struct vop_reg pic_offset;
|
||||
struct vop_reg pic_size;
|
||||
struct vop_reg dsp_offset;
|
||||
struct vop_reg transform_offset;
|
||||
struct vop_reg hdr_ptr;
|
||||
struct vop_reg half_block_en;
|
||||
struct vop_reg xmirror;
|
||||
struct vop_reg ymirror;
|
||||
struct vop_reg rotate_270;
|
||||
struct vop_reg rotate_90;
|
||||
struct vop_reg rstn;
|
||||
};
|
||||
|
||||
@ -166,6 +180,7 @@ struct vop_win_phy {
|
||||
struct vop_reg gate;
|
||||
struct vop_reg format;
|
||||
struct vop_reg rb_swap;
|
||||
struct vop_reg uv_swap;
|
||||
struct vop_reg act_info;
|
||||
struct vop_reg dsp_info;
|
||||
struct vop_reg dsp_st;
|
||||
|
2706
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
Normal file
2706
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
Normal file
File diff suppressed because it is too large
Load Diff
477
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
Normal file
477
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
Normal file
@ -0,0 +1,477 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
||||
* Author:Mark Yao <mark.yao@rock-chips.com>
|
||||
*/
|
||||
|
||||
#ifndef _ROCKCHIP_DRM_VOP2_H
|
||||
#define _ROCKCHIP_DRM_VOP2_H
|
||||
|
||||
#include "rockchip_drm_vop.h"
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <drm/drm_modes.h>
|
||||
|
||||
#define VOP_FEATURE_OUTPUT_10BIT BIT(0)
|
||||
|
||||
#define WIN_FEATURE_AFBDC BIT(0)
|
||||
#define WIN_FEATURE_CLUSTER BIT(1)
|
||||
|
||||
/*
|
||||
* the delay number of a window in different mode.
|
||||
*/
|
||||
enum win_dly_mode {
|
||||
VOP2_DLY_MODE_DEFAULT, /**< default mode */
|
||||
VOP2_DLY_MODE_HISO_S, /** HDR in SDR out mode, as a SDR window */
|
||||
VOP2_DLY_MODE_HIHO_H, /** HDR in HDR out mode, as a HDR window */
|
||||
VOP2_DLY_MODE_MAX,
|
||||
};
|
||||
|
||||
struct vop_rect {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
enum vop2_scale_up_mode {
|
||||
VOP2_SCALE_UP_NRST_NBOR,
|
||||
VOP2_SCALE_UP_BIL,
|
||||
VOP2_SCALE_UP_BIC,
|
||||
};
|
||||
|
||||
enum vop2_scale_down_mode {
|
||||
VOP2_SCALE_DOWN_NRST_NBOR,
|
||||
VOP2_SCALE_DOWN_BIL,
|
||||
VOP2_SCALE_DOWN_AVG,
|
||||
};
|
||||
|
||||
enum vop2_win_regs {
|
||||
VOP2_WIN_ENABLE,
|
||||
VOP2_WIN_FORMAT,
|
||||
VOP2_WIN_CSC_MODE,
|
||||
VOP2_WIN_XMIRROR,
|
||||
VOP2_WIN_YMIRROR,
|
||||
VOP2_WIN_RB_SWAP,
|
||||
VOP2_WIN_UV_SWAP,
|
||||
VOP2_WIN_ACT_INFO,
|
||||
VOP2_WIN_DSP_INFO,
|
||||
VOP2_WIN_DSP_ST,
|
||||
VOP2_WIN_YRGB_MST,
|
||||
VOP2_WIN_UV_MST,
|
||||
VOP2_WIN_YRGB_VIR,
|
||||
VOP2_WIN_UV_VIR,
|
||||
VOP2_WIN_YUV_CLIP,
|
||||
VOP2_WIN_Y2R_EN,
|
||||
VOP2_WIN_R2Y_EN,
|
||||
VOP2_WIN_COLOR_KEY,
|
||||
VOP2_WIN_COLOR_KEY_EN,
|
||||
VOP2_WIN_DITHER_UP,
|
||||
|
||||
/* scale regs */
|
||||
VOP2_WIN_SCALE_YRGB_X,
|
||||
VOP2_WIN_SCALE_YRGB_Y,
|
||||
VOP2_WIN_SCALE_CBCR_X,
|
||||
VOP2_WIN_SCALE_CBCR_Y,
|
||||
VOP2_WIN_YRGB_HOR_SCL_MODE,
|
||||
VOP2_WIN_YRGB_HSCL_FILTER_MODE,
|
||||
VOP2_WIN_YRGB_VER_SCL_MODE,
|
||||
VOP2_WIN_YRGB_VSCL_FILTER_MODE,
|
||||
VOP2_WIN_CBCR_VER_SCL_MODE,
|
||||
VOP2_WIN_CBCR_HSCL_FILTER_MODE,
|
||||
VOP2_WIN_CBCR_HOR_SCL_MODE,
|
||||
VOP2_WIN_CBCR_VSCL_FILTER_MODE,
|
||||
VOP2_WIN_VSD_CBCR_GT2,
|
||||
VOP2_WIN_VSD_CBCR_GT4,
|
||||
VOP2_WIN_VSD_YRGB_GT2,
|
||||
VOP2_WIN_VSD_YRGB_GT4,
|
||||
VOP2_WIN_BIC_COE_SEL,
|
||||
|
||||
/* cluster regs */
|
||||
VOP2_WIN_CLUSTER_ENABLE,
|
||||
VOP2_WIN_AFBC_ENABLE,
|
||||
VOP2_WIN_CLUSTER_LB_MODE,
|
||||
|
||||
/* afbc regs */
|
||||
VOP2_WIN_AFBC_FORMAT,
|
||||
VOP2_WIN_AFBC_RB_SWAP,
|
||||
VOP2_WIN_AFBC_UV_SWAP,
|
||||
VOP2_WIN_AFBC_AUTO_GATING_EN,
|
||||
VOP2_WIN_AFBC_BLOCK_SPLIT_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_HDR_PTR,
|
||||
VOP2_WIN_AFBC_HALF_BLOCK_EN,
|
||||
VOP2_WIN_AFBC_ROTATE_270,
|
||||
VOP2_WIN_AFBC_ROTATE_90,
|
||||
VOP2_WIN_MAX_REG,
|
||||
};
|
||||
|
||||
struct vop2_win_data {
|
||||
const char *name;
|
||||
unsigned int phys_id;
|
||||
|
||||
u32 base;
|
||||
enum drm_plane_type type;
|
||||
|
||||
u32 nformats;
|
||||
const u32 *formats;
|
||||
const uint64_t *format_modifiers;
|
||||
const unsigned int supported_rotations;
|
||||
|
||||
/**
|
||||
* @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
|
||||
*/
|
||||
unsigned int layer_sel_id;
|
||||
uint64_t feature;
|
||||
|
||||
unsigned int max_upscale_factor;
|
||||
unsigned int max_downscale_factor;
|
||||
const u8 dly[VOP2_DLY_MODE_MAX];
|
||||
};
|
||||
|
||||
struct vop2_video_port_data {
|
||||
unsigned int id;
|
||||
u32 feature;
|
||||
u16 gamma_lut_len;
|
||||
u16 cubic_lut_len;
|
||||
struct vop_rect max_output;
|
||||
const u8 pre_scan_max_dly[4];
|
||||
const struct vop2_video_port_regs *regs;
|
||||
unsigned int offset;
|
||||
};
|
||||
|
||||
struct vop2_data {
|
||||
u8 nr_vps;
|
||||
const struct vop2_ctrl *ctrl;
|
||||
const struct vop2_win_data *win;
|
||||
const struct vop2_video_port_data *vp;
|
||||
const struct vop_csc_table *csc_table;
|
||||
struct vop_rect max_input;
|
||||
struct vop_rect max_output;
|
||||
|
||||
unsigned int win_size;
|
||||
unsigned int soc_id;
|
||||
};
|
||||
|
||||
/* interrupt define */
|
||||
#define FS_NEW_INTR BIT(4)
|
||||
#define ADDR_SAME_INTR BIT(5)
|
||||
#define LINE_FLAG1_INTR BIT(6)
|
||||
#define WIN0_EMPTY_INTR BIT(7)
|
||||
#define WIN1_EMPTY_INTR BIT(8)
|
||||
#define WIN2_EMPTY_INTR BIT(9)
|
||||
#define WIN3_EMPTY_INTR BIT(10)
|
||||
#define HWC_EMPTY_INTR BIT(11)
|
||||
#define POST_BUF_EMPTY_INTR BIT(12)
|
||||
#define PWM_GEN_INTR BIT(13)
|
||||
#define DMA_FINISH_INTR BIT(14)
|
||||
#define FS_FIELD_INTR BIT(15)
|
||||
#define FE_INTR BIT(16)
|
||||
#define WB_UV_FIFO_FULL_INTR BIT(17)
|
||||
#define WB_YRGB_FIFO_FULL_INTR BIT(18)
|
||||
#define WB_COMPLETE_INTR BIT(19)
|
||||
|
||||
/*
|
||||
* display output interface supported by rockchip lcdc
|
||||
*/
|
||||
#define ROCKCHIP_OUT_MODE_P888 0
|
||||
#define ROCKCHIP_OUT_MODE_BT1120 0
|
||||
#define ROCKCHIP_OUT_MODE_P666 1
|
||||
#define ROCKCHIP_OUT_MODE_P565 2
|
||||
#define ROCKCHIP_OUT_MODE_BT656 5
|
||||
#define ROCKCHIP_OUT_MODE_S888 8
|
||||
#define ROCKCHIP_OUT_MODE_S888_DUMMY 12
|
||||
#define ROCKCHIP_OUT_MODE_YUV420 14
|
||||
/* for use special outface */
|
||||
#define ROCKCHIP_OUT_MODE_AAAA 15
|
||||
|
||||
enum vop_csc_format {
|
||||
CSC_BT601L,
|
||||
CSC_BT709L,
|
||||
CSC_BT601F,
|
||||
CSC_BT2020,
|
||||
};
|
||||
|
||||
enum src_factor_mode {
|
||||
SRC_FAC_ALPHA_ZERO,
|
||||
SRC_FAC_ALPHA_ONE,
|
||||
SRC_FAC_ALPHA_DST,
|
||||
SRC_FAC_ALPHA_DST_INVERSE,
|
||||
SRC_FAC_ALPHA_SRC,
|
||||
SRC_FAC_ALPHA_SRC_GLOBAL,
|
||||
};
|
||||
|
||||
enum dst_factor_mode {
|
||||
DST_FAC_ALPHA_ZERO,
|
||||
DST_FAC_ALPHA_ONE,
|
||||
DST_FAC_ALPHA_SRC,
|
||||
DST_FAC_ALPHA_SRC_INVERSE,
|
||||
DST_FAC_ALPHA_DST,
|
||||
DST_FAC_ALPHA_DST_GLOBAL,
|
||||
};
|
||||
|
||||
#define RK3568_GRF_VO_CON1 0x0364
|
||||
/* System registers definition */
|
||||
#define RK3568_REG_CFG_DONE 0x000
|
||||
#define RK3568_VERSION_INFO 0x004
|
||||
#define RK3568_SYS_AUTO_GATING_CTRL 0x008
|
||||
#define RK3568_SYS_AXI_LUT_CTRL 0x024
|
||||
#define RK3568_DSP_IF_EN 0x028
|
||||
#define RK3568_DSP_IF_CTRL 0x02c
|
||||
#define RK3568_DSP_IF_POL 0x030
|
||||
#define RK3568_WB_CTRL 0x40
|
||||
#define RK3568_WB_XSCAL_FACTOR 0x44
|
||||
#define RK3568_WB_YRGB_MST 0x48
|
||||
#define RK3568_WB_CBR_MST 0x4C
|
||||
#define RK3568_OTP_WIN_EN 0x050
|
||||
#define RK3568_LUT_PORT_SEL 0x058
|
||||
#define RK3568_SYS_STATUS0 0x060
|
||||
#define RK3568_VP_LINE_FLAG(vp) (0x70 + (vp) * 0x4)
|
||||
#define RK3568_SYS0_INT_EN 0x80
|
||||
#define RK3568_SYS0_INT_CLR 0x84
|
||||
#define RK3568_SYS0_INT_STATUS 0x88
|
||||
#define RK3568_SYS1_INT_EN 0x90
|
||||
#define RK3568_SYS1_INT_CLR 0x94
|
||||
#define RK3568_SYS1_INT_STATUS 0x98
|
||||
#define RK3568_VP_INT_EN(vp) (0xA0 + (vp) * 0x10)
|
||||
#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)
|
||||
|
||||
/* Video Port registers definition */
|
||||
#define RK3568_VP_DSP_CTRL 0x00
|
||||
#define RK3568_VP_MIPI_CTRL 0x04
|
||||
#define RK3568_VP_COLOR_BAR_CTRL 0x08
|
||||
#define RK3568_VP_3D_LUT_CTRL 0x10
|
||||
#define RK3568_VP_3D_LUT_MST 0x20
|
||||
#define RK3568_VP_DSP_BG 0x2C
|
||||
#define RK3568_VP_PRE_SCAN_HTIMING 0x30
|
||||
#define RK3568_VP_POST_DSP_HACT_INFO 0x34
|
||||
#define RK3568_VP_POST_DSP_VACT_INFO 0x38
|
||||
#define RK3568_VP_POST_SCL_FACTOR_YRGB 0x3C
|
||||
#define RK3568_VP_POST_SCL_CTRL 0x40
|
||||
#define RK3568_VP_POST_DSP_VACT_INFO_F1 0x44
|
||||
#define RK3568_VP_DSP_HTOTAL_HS_END 0x48
|
||||
#define RK3568_VP_DSP_HACT_ST_END 0x4C
|
||||
#define RK3568_VP_DSP_VTOTAL_VS_END 0x50
|
||||
#define RK3568_VP_DSP_VACT_ST_END 0x54
|
||||
#define RK3568_VP_DSP_VS_ST_END_F1 0x58
|
||||
#define RK3568_VP_DSP_VACT_ST_END_F1 0x5C
|
||||
#define RK3568_VP_BCSH_CTRL 0x60
|
||||
#define RK3568_VP_BCSH_BCS 0x64
|
||||
#define RK3568_VP_BCSH_H 0x68
|
||||
#define RK3568_VP_BCSH_COLOR_BAR 0x6C
|
||||
|
||||
/* Overlay registers definition */
|
||||
#define RK3568_OVL_CTRL 0x600
|
||||
#define RK3568_OVL_LAYER_SEL 0x604
|
||||
#define RK3568_OVL_PORT_SEL 0x608
|
||||
#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL 0x610
|
||||
#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL 0x614
|
||||
#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL 0x618
|
||||
#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL 0x61C
|
||||
#define RK3568_MIX0_SRC_COLOR_CTRL 0x650
|
||||
#define RK3568_MIX0_DST_COLOR_CTRL 0x654
|
||||
#define RK3568_MIX0_SRC_ALPHA_CTRL 0x658
|
||||
#define RK3568_MIX0_DST_ALPHA_CTRL 0x65C
|
||||
#define RK3568_HDR0_SRC_COLOR_CTRL 0x6C0
|
||||
#define RK3568_HDR0_DST_COLOR_CTRL 0x6C4
|
||||
#define RK3568_HDR0_SRC_ALPHA_CTRL 0x6C8
|
||||
#define RK3568_HDR0_DST_ALPHA_CTRL 0x6CC
|
||||
#define RK3568_VP_BG_MIX_CTRL(vp) (0x6E0 + (vp) * 4)
|
||||
#define RK3568_CLUSTER_DLY_NUM 0x6F0
|
||||
#define RK3568_SMART_DLY_NUM 0x6F8
|
||||
|
||||
/* Cluster register definition, offset relative to window base */
|
||||
#define RK3568_CLUSTER_WIN_CTRL0 0x00
|
||||
#define RK3568_CLUSTER_WIN_CTRL1 0x04
|
||||
#define RK3568_CLUSTER_WIN_YRGB_MST 0x10
|
||||
#define RK3568_CLUSTER_WIN_CBR_MST 0x14
|
||||
#define RK3568_CLUSTER_WIN_VIR 0x18
|
||||
#define RK3568_CLUSTER_WIN_ACT_INFO 0x20
|
||||
#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_AFBCD_OUTPUT_CTRL 0x50
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_ROTATE_MODE 0x54
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_HDR_PTR 0x58
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_VIR_WIDTH 0x5C
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_PIC_SIZE 0x60
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_PIC_OFFSET 0x64
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_DSP_OFFSET 0x68
|
||||
#define RK3568_CLUSTER_WIN_AFBCD_CTRL 0x6C
|
||||
|
||||
#define RK3568_CLUSTER_CTRL 0x100
|
||||
|
||||
/* (E)smart register definition, offset relative to window base */
|
||||
#define RK3568_SMART_CTRL0 0x00
|
||||
#define RK3568_SMART_CTRL1 0x04
|
||||
#define RK3568_SMART_REGION0_CTRL 0x10
|
||||
#define RK3568_SMART_REGION0_YRGB_MST 0x14
|
||||
#define RK3568_SMART_REGION0_CBR_MST 0x18
|
||||
#define RK3568_SMART_REGION0_VIR 0x1C
|
||||
#define RK3568_SMART_REGION0_ACT_INFO 0x20
|
||||
#define RK3568_SMART_REGION0_DSP_INFO 0x24
|
||||
#define RK3568_SMART_REGION0_DSP_ST 0x28
|
||||
#define RK3568_SMART_REGION0_SCL_CTRL 0x30
|
||||
#define RK3568_SMART_REGION0_SCL_FACTOR_YRGB 0x34
|
||||
#define RK3568_SMART_REGION0_SCL_FACTOR_CBR 0x38
|
||||
#define RK3568_SMART_REGION0_SCL_OFFSET 0x3C
|
||||
#define RK3568_SMART_REGION1_CTRL 0x40
|
||||
#define RK3568_SMART_REGION1_YRGB_MST 0x44
|
||||
#define RK3568_SMART_REGION1_CBR_MST 0x48
|
||||
#define RK3568_SMART_REGION1_VIR 0x4C
|
||||
#define RK3568_SMART_REGION1_ACT_INFO 0x50
|
||||
#define RK3568_SMART_REGION1_DSP_INFO 0x54
|
||||
#define RK3568_SMART_REGION1_DSP_ST 0x58
|
||||
#define RK3568_SMART_REGION1_SCL_CTRL 0x60
|
||||
#define RK3568_SMART_REGION1_SCL_FACTOR_YRGB 0x64
|
||||
#define RK3568_SMART_REGION1_SCL_FACTOR_CBR 0x68
|
||||
#define RK3568_SMART_REGION1_SCL_OFFSET 0x6C
|
||||
#define RK3568_SMART_REGION2_CTRL 0x70
|
||||
#define RK3568_SMART_REGION2_YRGB_MST 0x74
|
||||
#define RK3568_SMART_REGION2_CBR_MST 0x78
|
||||
#define RK3568_SMART_REGION2_VIR 0x7C
|
||||
#define RK3568_SMART_REGION2_ACT_INFO 0x80
|
||||
#define RK3568_SMART_REGION2_DSP_INFO 0x84
|
||||
#define RK3568_SMART_REGION2_DSP_ST 0x88
|
||||
#define RK3568_SMART_REGION2_SCL_CTRL 0x90
|
||||
#define RK3568_SMART_REGION2_SCL_FACTOR_YRGB 0x94
|
||||
#define RK3568_SMART_REGION2_SCL_FACTOR_CBR 0x98
|
||||
#define RK3568_SMART_REGION2_SCL_OFFSET 0x9C
|
||||
#define RK3568_SMART_REGION3_CTRL 0xA0
|
||||
#define RK3568_SMART_REGION3_YRGB_MST 0xA4
|
||||
#define RK3568_SMART_REGION3_CBR_MST 0xA8
|
||||
#define RK3568_SMART_REGION3_VIR 0xAC
|
||||
#define RK3568_SMART_REGION3_ACT_INFO 0xB0
|
||||
#define RK3568_SMART_REGION3_DSP_INFO 0xB4
|
||||
#define RK3568_SMART_REGION3_DSP_ST 0xB8
|
||||
#define RK3568_SMART_REGION3_SCL_CTRL 0xC0
|
||||
#define RK3568_SMART_REGION3_SCL_FACTOR_YRGB 0xC4
|
||||
#define RK3568_SMART_REGION3_SCL_FACTOR_CBR 0xC8
|
||||
#define RK3568_SMART_REGION3_SCL_OFFSET 0xCC
|
||||
#define RK3568_SMART_COLOR_KEY_CTRL 0xD0
|
||||
|
||||
/* HDR register definition */
|
||||
#define RK3568_HDR_LUT_CTRL 0x2000
|
||||
#define RK3568_HDR_LUT_MST 0x2004
|
||||
#define RK3568_SDR2HDR_CTRL 0x2010
|
||||
#define RK3568_HDR2SDR_CTRL 0x2020
|
||||
#define RK3568_HDR2SDR_SRC_RANGE 0x2024
|
||||
#define RK3568_HDR2SDR_NORMFACEETF 0x2028
|
||||
#define RK3568_HDR2SDR_DST_RANGE 0x202C
|
||||
#define RK3568_HDR2SDR_NORMFACCGAMMA 0x2030
|
||||
#define RK3568_HDR_EETF_OETF_Y0 0x203C
|
||||
#define RK3568_HDR_SAT_Y0 0x20C0
|
||||
#define RK3568_HDR_EOTF_OETF_Y0 0x20F0
|
||||
#define RK3568_HDR_OETF_DX_POW1 0x2200
|
||||
#define RK3568_HDR_OETF_XN1 0x2300
|
||||
|
||||
#define RK3568_REG_CFG_DONE__GLB_CFG_DONE_EN BIT(15)
|
||||
|
||||
#define RK3568_VP_DSP_CTRL__STANDBY BIT(31)
|
||||
#define RK3568_VP_DSP_CTRL__DITHER_DOWN_MODE BIT(20)
|
||||
#define RK3568_VP_DSP_CTRL__DITHER_DOWN_SEL GENMASK(19, 18)
|
||||
#define RK3568_VP_DSP_CTRL__DITHER_DOWN_EN BIT(17)
|
||||
#define RK3568_VP_DSP_CTRL__PRE_DITHER_DOWN_EN BIT(16)
|
||||
#define RK3568_VP_DSP_CTRL__POST_DSP_OUT_R2Y BIT(15)
|
||||
#define RK3568_VP_DSP_CTRL__DSP_RB_SWAP BIT(9)
|
||||
#define RK3568_VP_DSP_CTRL__DSP_INTERLACE BIT(7)
|
||||
#define RK3568_VP_DSP_CTRL__DSP_FILED_POL BIT(6)
|
||||
#define RK3568_VP_DSP_CTRL__P2I_EN BIT(5)
|
||||
#define RK3568_VP_DSP_CTRL__CORE_DCLK_DIV BIT(4)
|
||||
#define RK3568_VP_DSP_CTRL__OUT_MODE GENMASK(3, 0)
|
||||
|
||||
#define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN BIT(1)
|
||||
#define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN BIT(0)
|
||||
|
||||
#define RK3568_SYS_DSP_INFACE_EN_LVDS1_MUX GENMASK(26, 25)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_LVDS1 BIT(24)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_MIPI1_MUX GENMASK(22, 21)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_MIPI1 BIT(20)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_LVDS0_MUX GENMASK(19, 18)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_MIPI0_MUX GENMASK(17, 16)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_EDP_MUX GENMASK(15, 14)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_HDMI_MUX GENMASK(11, 10)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_RGB_MUX GENMASK(9, 8)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_LVDS0 BIT(5)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_MIPI0 BIT(4)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_EDP BIT(3)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_HDMI BIT(1)
|
||||
#define RK3568_SYS_DSP_INFACE_EN_RGB BIT(0)
|
||||
|
||||
#define RK3568_DSP_IF_POL__MIPI_PIN_POL GENMASK(19, 16)
|
||||
#define RK3568_DSP_IF_POL__EDP_PIN_POL GENMASK(15, 12)
|
||||
#define RK3568_DSP_IF_POL__HDMI_PIN_POL GENMASK(7, 4)
|
||||
#define RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL GENMASK(3, 0)
|
||||
|
||||
#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2_PHASE_LOCK BIT(5)
|
||||
#define RK3568_VP0_MIPI_CTRL__DCLK_DIV2 BIT(4)
|
||||
|
||||
#define RK3568_SYS_AUTO_GATING_CTRL__AUTO_GATING_EN BIT(31)
|
||||
|
||||
#define RK3568_DSP_IF_POL__CFG_DONE_IMD BIT(28)
|
||||
|
||||
#define VOP2_SYS_AXI_BUS_NUM 2
|
||||
|
||||
#define VOP2_CLUSTER_YUV444_10 0x12
|
||||
|
||||
#define VOP2_COLOR_KEY_MASK BIT(31)
|
||||
|
||||
#define RK3568_OVL_CTRL__LAYERSEL_REGDONE_IMD BIT(28)
|
||||
|
||||
#define RK3568_VP_BG_MIX_CTRL__BG_DLY GENMASK(31, 24)
|
||||
|
||||
#define RK3568_OVL_PORT_SEL__SEL_PORT GENMASK(31, 16)
|
||||
#define RK3568_OVL_PORT_SEL__SMART1 GENMASK(31, 30)
|
||||
#define RK3568_OVL_PORT_SEL__SMART0 GENMASK(29, 28)
|
||||
#define RK3568_OVL_PORT_SEL__ESMART1 GENMASK(27, 26)
|
||||
#define RK3568_OVL_PORT_SEL__ESMART0 GENMASK(25, 24)
|
||||
#define RK3568_OVL_PORT_SEL__CLUSTER1 GENMASK(19, 18)
|
||||
#define RK3568_OVL_PORT_SEL__CLUSTER0 GENMASK(17, 16)
|
||||
#define RK3568_OVL_PORT_SET__PORT2_MUX GENMASK(11, 8)
|
||||
#define RK3568_OVL_PORT_SET__PORT1_MUX GENMASK(7, 4)
|
||||
#define RK3568_OVL_PORT_SET__PORT0_MUX GENMASK(3, 0)
|
||||
#define RK3568_OVL_LAYER_SEL__LAYER(layer, x) ((x) << ((layer) * 4))
|
||||
|
||||
#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_1 GENMASK(31, 24)
|
||||
#define RK3568_CLUSTER_DLY_NUM__CLUSTER1_0 GENMASK(23, 16)
|
||||
#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_1 GENMASK(15, 8)
|
||||
#define RK3568_CLUSTER_DLY_NUM__CLUSTER0_0 GENMASK(7, 0)
|
||||
|
||||
#define RK3568_SMART_DLY_NUM__SMART1 GENMASK(31, 24)
|
||||
#define RK3568_SMART_DLY_NUM__SMART0 GENMASK(23, 16)
|
||||
#define RK3568_SMART_DLY_NUM__ESMART1 GENMASK(15, 8)
|
||||
#define RK3568_SMART_DLY_NUM__ESMART0 GENMASK(7, 0)
|
||||
|
||||
#define VP_INT_DSP_HOLD_VALID BIT(6)
|
||||
#define VP_INT_FS_FIELD BIT(5)
|
||||
#define VP_INT_POST_BUF_EMPTY BIT(4)
|
||||
#define VP_INT_LINE_FLAG1 BIT(3)
|
||||
#define VP_INT_LINE_FLAG0 BIT(2)
|
||||
#define VOP2_INT_BUS_ERRPR BIT(1)
|
||||
#define VP_INT_FS BIT(0)
|
||||
|
||||
#define POLFLAG_DCLK_INV BIT(3)
|
||||
|
||||
enum vop2_layer_phy_id {
|
||||
ROCKCHIP_VOP2_CLUSTER0 = 0,
|
||||
ROCKCHIP_VOP2_CLUSTER1,
|
||||
ROCKCHIP_VOP2_ESMART0,
|
||||
ROCKCHIP_VOP2_ESMART1,
|
||||
ROCKCHIP_VOP2_SMART0,
|
||||
ROCKCHIP_VOP2_SMART1,
|
||||
ROCKCHIP_VOP2_CLUSTER2,
|
||||
ROCKCHIP_VOP2_CLUSTER3,
|
||||
ROCKCHIP_VOP2_ESMART2,
|
||||
ROCKCHIP_VOP2_ESMART3,
|
||||
ROCKCHIP_VOP2_PHY_ID_INVALID = -1,
|
||||
};
|
||||
|
||||
extern const struct component_ops vop2_component_ops;
|
||||
|
||||
#endif /* _ROCKCHIP_DRM_VOP2_H */
|
@ -36,12 +36,6 @@
|
||||
|
||||
struct rockchip_lvds;
|
||||
|
||||
#define connector_to_lvds(c) \
|
||||
container_of(c, struct rockchip_lvds, connector)
|
||||
|
||||
#define encoder_to_lvds(c) \
|
||||
container_of(c, struct rockchip_lvds, encoder)
|
||||
|
||||
/**
|
||||
* struct rockchip_lvds_soc_data - rockchip lvds Soc private data
|
||||
* @probe: LVDS platform probe function
|
||||
@ -65,10 +59,22 @@ struct rockchip_lvds {
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
struct rockchip_encoder encoder;
|
||||
struct dev_pin_info *pins;
|
||||
};
|
||||
|
||||
static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct rockchip_lvds, connector);
|
||||
}
|
||||
|
||||
static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
|
||||
|
||||
return container_of(rkencoder, struct rockchip_lvds, encoder);
|
||||
}
|
||||
|
||||
static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset,
|
||||
u32 val)
|
||||
{
|
||||
@ -599,7 +605,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
|
||||
goto err_put_remote;
|
||||
}
|
||||
|
||||
encoder = &lvds->encoder;
|
||||
encoder = &lvds->encoder.encoder;
|
||||
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
|
||||
dev->of_node);
|
||||
|
||||
@ -674,10 +680,10 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
|
||||
const struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
|
||||
encoder_funcs = lvds->soc_data->helper_funcs;
|
||||
encoder_funcs->disable(&lvds->encoder);
|
||||
encoder_funcs->disable(&lvds->encoder.encoder);
|
||||
pm_runtime_disable(dev);
|
||||
drm_connector_cleanup(&lvds->connector);
|
||||
drm_encoder_cleanup(&lvds->encoder);
|
||||
drm_encoder_cleanup(&lvds->encoder.encoder);
|
||||
}
|
||||
|
||||
static const struct component_ops rockchip_lvds_component_ops = {
|
||||
|
281
drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
Normal file
281
drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
Normal file
@ -0,0 +1,281 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) Rockchip Electronics Co.Ltd
|
||||
* Author: Andy Yan <andy.yan@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "rockchip_drm_vop2.h"
|
||||
|
||||
static const uint32_t formats_win_full_10bit[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_BGR888,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_NV16,
|
||||
DRM_FORMAT_NV24,
|
||||
};
|
||||
|
||||
static const uint32_t formats_win_full_10bit_yuyv[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_BGR888,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_NV16,
|
||||
DRM_FORMAT_NV24,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_VYUY,
|
||||
};
|
||||
|
||||
static const uint32_t formats_win_lite[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_BGR888,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_BGR565,
|
||||
};
|
||||
|
||||
static const uint64_t format_modifiers[] = {
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
DRM_FORMAT_MOD_INVALID,
|
||||
};
|
||||
|
||||
static const uint64_t format_modifiers_afbc[] = {
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
|
||||
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_SPARSE),
|
||||
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_YTR),
|
||||
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_CBR),
|
||||
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_YTR |
|
||||
AFBC_FORMAT_MOD_SPARSE),
|
||||
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_CBR |
|
||||
AFBC_FORMAT_MOD_SPARSE),
|
||||
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_YTR |
|
||||
AFBC_FORMAT_MOD_CBR),
|
||||
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_YTR |
|
||||
AFBC_FORMAT_MOD_CBR |
|
||||
AFBC_FORMAT_MOD_SPARSE),
|
||||
|
||||
/* SPLIT mandates SPARSE, RGB modes mandates YTR */
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
|
||||
AFBC_FORMAT_MOD_YTR |
|
||||
AFBC_FORMAT_MOD_SPARSE |
|
||||
AFBC_FORMAT_MOD_SPLIT),
|
||||
DRM_FORMAT_MOD_INVALID,
|
||||
};
|
||||
|
||||
static const struct vop2_video_port_data rk3568_vop_video_ports[] = {
|
||||
{
|
||||
.id = 0,
|
||||
.feature = VOP_FEATURE_OUTPUT_10BIT,
|
||||
.gamma_lut_len = 1024,
|
||||
.cubic_lut_len = 9 * 9 * 9,
|
||||
.max_output = { 4096, 2304 },
|
||||
.pre_scan_max_dly = { 69, 53, 53, 42 },
|
||||
.offset = 0xc00,
|
||||
}, {
|
||||
.id = 1,
|
||||
.gamma_lut_len = 1024,
|
||||
.max_output = { 2048, 1536 },
|
||||
.pre_scan_max_dly = { 40, 40, 40, 40 },
|
||||
.offset = 0xd00,
|
||||
}, {
|
||||
.id = 2,
|
||||
.gamma_lut_len = 1024,
|
||||
.max_output = { 1920, 1080 },
|
||||
.pre_scan_max_dly = { 40, 40, 40, 40 },
|
||||
.offset = 0xe00,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* rk3568 vop with 2 cluster, 2 esmart win, 2 smart win.
|
||||
* Every cluster can work as 4K win or split into two win.
|
||||
* All win in cluster support AFBCD.
|
||||
*
|
||||
* Every esmart win and smart win support 4 Multi-region.
|
||||
*
|
||||
* Scale filter mode:
|
||||
*
|
||||
* * Cluster: bicubic for horizontal scale up, others use bilinear
|
||||
* * ESmart:
|
||||
* * nearest-neighbor/bilinear/bicubic for scale up
|
||||
* * nearest-neighbor/bilinear/average for scale down
|
||||
*
|
||||
*
|
||||
* @TODO describe the wind like cpu-map dt nodes;
|
||||
*/
|
||||
static const struct vop2_win_data rk3568_vop_win_data[] = {
|
||||
{
|
||||
.name = "Smart0-win0",
|
||||
.phys_id = ROCKCHIP_VOP2_SMART0,
|
||||
.base = 0x1c00,
|
||||
.formats = formats_win_lite,
|
||||
.nformats = ARRAY_SIZE(formats_win_lite),
|
||||
.format_modifiers = format_modifiers,
|
||||
.layer_sel_id = 3,
|
||||
.supported_rotations = DRM_MODE_REFLECT_Y,
|
||||
.type = DRM_PLANE_TYPE_PRIMARY,
|
||||
.max_upscale_factor = 8,
|
||||
.max_downscale_factor = 8,
|
||||
.dly = { 20, 47, 41 },
|
||||
}, {
|
||||
.name = "Smart1-win0",
|
||||
.phys_id = ROCKCHIP_VOP2_SMART1,
|
||||
.formats = formats_win_lite,
|
||||
.nformats = ARRAY_SIZE(formats_win_lite),
|
||||
.format_modifiers = format_modifiers,
|
||||
.base = 0x1e00,
|
||||
.layer_sel_id = 7,
|
||||
.supported_rotations = DRM_MODE_REFLECT_Y,
|
||||
.type = DRM_PLANE_TYPE_PRIMARY,
|
||||
.max_upscale_factor = 8,
|
||||
.max_downscale_factor = 8,
|
||||
.dly = { 20, 47, 41 },
|
||||
}, {
|
||||
.name = "Esmart1-win0",
|
||||
.phys_id = ROCKCHIP_VOP2_ESMART1,
|
||||
.formats = formats_win_full_10bit_yuyv,
|
||||
.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
|
||||
.format_modifiers = format_modifiers,
|
||||
.base = 0x1a00,
|
||||
.layer_sel_id = 6,
|
||||
.supported_rotations = DRM_MODE_REFLECT_Y,
|
||||
.type = DRM_PLANE_TYPE_PRIMARY,
|
||||
.max_upscale_factor = 8,
|
||||
.max_downscale_factor = 8,
|
||||
.dly = { 20, 47, 41 },
|
||||
}, {
|
||||
.name = "Esmart0-win0",
|
||||
.phys_id = ROCKCHIP_VOP2_ESMART0,
|
||||
.formats = formats_win_full_10bit_yuyv,
|
||||
.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
|
||||
.format_modifiers = format_modifiers,
|
||||
.base = 0x1800,
|
||||
.layer_sel_id = 2,
|
||||
.supported_rotations = DRM_MODE_REFLECT_Y,
|
||||
.type = DRM_PLANE_TYPE_OVERLAY,
|
||||
.max_upscale_factor = 8,
|
||||
.max_downscale_factor = 8,
|
||||
.dly = { 20, 47, 41 },
|
||||
}, {
|
||||
.name = "Cluster0-win0",
|
||||
.phys_id = ROCKCHIP_VOP2_CLUSTER0,
|
||||
.base = 0x1000,
|
||||
.formats = formats_win_full_10bit,
|
||||
.nformats = ARRAY_SIZE(formats_win_full_10bit),
|
||||
.format_modifiers = format_modifiers_afbc,
|
||||
.layer_sel_id = 0,
|
||||
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
|
||||
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
|
||||
.max_upscale_factor = 4,
|
||||
.max_downscale_factor = 4,
|
||||
.dly = { 0, 27, 21 },
|
||||
.type = DRM_PLANE_TYPE_OVERLAY,
|
||||
.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
|
||||
}, {
|
||||
.name = "Cluster1-win0",
|
||||
.phys_id = ROCKCHIP_VOP2_CLUSTER1,
|
||||
.base = 0x1200,
|
||||
.formats = formats_win_full_10bit,
|
||||
.nformats = ARRAY_SIZE(formats_win_full_10bit),
|
||||
.format_modifiers = format_modifiers_afbc,
|
||||
.layer_sel_id = 1,
|
||||
.supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 |
|
||||
DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y,
|
||||
.type = DRM_PLANE_TYPE_OVERLAY,
|
||||
.max_upscale_factor = 4,
|
||||
.max_downscale_factor = 4,
|
||||
.dly = { 0, 27, 21 },
|
||||
.feature = WIN_FEATURE_AFBDC | WIN_FEATURE_CLUSTER,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct vop2_data rk3566_vop = {
|
||||
.nr_vps = 3,
|
||||
.max_input = { 4096, 2304 },
|
||||
.max_output = { 4096, 2304 },
|
||||
.vp = rk3568_vop_video_ports,
|
||||
.win = rk3568_vop_win_data,
|
||||
.win_size = ARRAY_SIZE(rk3568_vop_win_data),
|
||||
.soc_id = 3566,
|
||||
};
|
||||
|
||||
static const struct vop2_data rk3568_vop = {
|
||||
.nr_vps = 3,
|
||||
.max_input = { 4096, 2304 },
|
||||
.max_output = { 4096, 2304 },
|
||||
.vp = rk3568_vop_video_ports,
|
||||
.win = rk3568_vop_win_data,
|
||||
.win_size = ARRAY_SIZE(rk3568_vop_win_data),
|
||||
.soc_id = 3568,
|
||||
};
|
||||
|
||||
static const struct of_device_id vop2_dt_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3566-vop",
|
||||
.data = &rk3566_vop,
|
||||
}, {
|
||||
.compatible = "rockchip,rk3568-vop",
|
||||
.data = &rk3568_vop,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vop2_dt_match);
|
||||
|
||||
static int vop2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
return component_add(dev, &vop2_component_ops);
|
||||
}
|
||||
|
||||
static int vop2_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &vop2_component_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver vop2_platform_driver = {
|
||||
.probe = vop2_probe,
|
||||
.remove = vop2_remove,
|
||||
.driver = {
|
||||
.name = "rockchip-vop2",
|
||||
.of_match_table = of_match_ptr(vop2_dt_match),
|
||||
},
|
||||
};
|
@ -46,8 +46,11 @@ static const uint32_t formats_win_full[] = {
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_BGR565,
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_NV21,
|
||||
DRM_FORMAT_NV16,
|
||||
DRM_FORMAT_NV61,
|
||||
DRM_FORMAT_NV24,
|
||||
DRM_FORMAT_NV42,
|
||||
};
|
||||
|
||||
static const uint64_t format_modifiers_win_full[] = {
|
||||
@ -272,6 +275,7 @@ static const struct vop_win_phy px30_win0_data = {
|
||||
.enable = VOP_REG(PX30_WIN0_CTRL0, 0x1, 0),
|
||||
.format = VOP_REG(PX30_WIN0_CTRL0, 0x7, 1),
|
||||
.rb_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 12),
|
||||
.uv_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 15),
|
||||
.act_info = VOP_REG(PX30_WIN0_ACT_INFO, 0xffffffff, 0),
|
||||
.dsp_info = VOP_REG(PX30_WIN0_DSP_INFO, 0xffffffff, 0),
|
||||
.dsp_st = VOP_REG(PX30_WIN0_DSP_ST, 0xffffffff, 0),
|
||||
@ -291,6 +295,7 @@ static const struct vop_win_phy px30_win1_data = {
|
||||
.enable = VOP_REG(PX30_WIN1_CTRL0, 0x1, 0),
|
||||
.format = VOP_REG(PX30_WIN1_CTRL0, 0x7, 4),
|
||||
.rb_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 12),
|
||||
.uv_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 15),
|
||||
.dsp_info = VOP_REG(PX30_WIN1_DSP_INFO, 0xffffffff, 0),
|
||||
.dsp_st = VOP_REG(PX30_WIN1_DSP_ST, 0xffffffff, 0),
|
||||
.yrgb_mst = VOP_REG(PX30_WIN1_MST, 0xffffffff, 0),
|
||||
@ -368,6 +373,7 @@ static const struct vop_win_phy rk3066_win0_data = {
|
||||
.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0),
|
||||
.format = VOP_REG(RK3066_SYS_CTRL1, 0x7, 4),
|
||||
.rb_swap = VOP_REG(RK3066_SYS_CTRL1, 0x1, 19),
|
||||
.uv_swap = VOP_REG(RK3066_SYS_CTRL1, 0x1, 22),
|
||||
.act_info = VOP_REG(RK3066_WIN0_ACT_INFO, 0x1fff1fff, 0),
|
||||
.dsp_info = VOP_REG(RK3066_WIN0_DSP_INFO, 0x0fff0fff, 0),
|
||||
.dsp_st = VOP_REG(RK3066_WIN0_DSP_ST, 0x1fff1fff, 0),
|
||||
@ -386,6 +392,7 @@ static const struct vop_win_phy rk3066_win1_data = {
|
||||
.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1),
|
||||
.format = VOP_REG(RK3066_SYS_CTRL1, 0x7, 7),
|
||||
.rb_swap = VOP_REG(RK3066_SYS_CTRL1, 0x1, 23),
|
||||
.uv_swap = VOP_REG(RK3066_SYS_CTRL1, 0x1, 26),
|
||||
.act_info = VOP_REG(RK3066_WIN1_ACT_INFO, 0x1fff1fff, 0),
|
||||
.dsp_info = VOP_REG(RK3066_WIN1_DSP_INFO, 0x0fff0fff, 0),
|
||||
.dsp_st = VOP_REG(RK3066_WIN1_DSP_ST, 0x1fff1fff, 0),
|
||||
@ -489,6 +496,7 @@ static const struct vop_win_phy rk3188_win0_data = {
|
||||
.enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 0),
|
||||
.format = VOP_REG(RK3188_SYS_CTRL, 0x7, 3),
|
||||
.rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 15),
|
||||
.uv_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 18),
|
||||
.act_info = VOP_REG(RK3188_WIN0_ACT_INFO, 0x1fff1fff, 0),
|
||||
.dsp_info = VOP_REG(RK3188_WIN0_DSP_INFO, 0x0fff0fff, 0),
|
||||
.dsp_st = VOP_REG(RK3188_WIN0_DSP_ST, 0x1fff1fff, 0),
|
||||
@ -619,6 +627,7 @@ static const struct vop_win_phy rk3288_win01_data = {
|
||||
.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
|
||||
.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
|
||||
.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
|
||||
.uv_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 15),
|
||||
.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
|
||||
.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
|
||||
.dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0),
|
||||
@ -753,6 +762,7 @@ static const struct vop_win_phy rk3368_win01_data = {
|
||||
.enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
|
||||
.format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
|
||||
.rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12),
|
||||
.uv_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 15),
|
||||
.x_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 21),
|
||||
.y_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 22),
|
||||
.act_info = VOP_REG(RK3368_WIN0_ACT_INFO, 0x1fff1fff, 0),
|
||||
@ -902,6 +912,7 @@ static const struct vop_win_phy rk3399_win01_data = {
|
||||
.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
|
||||
.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
|
||||
.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
|
||||
.uv_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 15),
|
||||
.x_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 21),
|
||||
.y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22),
|
||||
.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
|
||||
|
@ -1193,8 +1193,8 @@ err_remove_dsi_host:
|
||||
err_unprotect_clk:
|
||||
clk_rate_exclusive_put(dsi->mod_clk);
|
||||
err_attach_clk:
|
||||
if (!IS_ERR(dsi->bus_clk))
|
||||
regmap_mmio_detach_clk(dsi->regs);
|
||||
regmap_mmio_detach_clk(dsi->regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1207,8 +1207,7 @@ static int sun6i_dsi_remove(struct platform_device *pdev)
|
||||
mipi_dsi_host_unregister(&dsi->host);
|
||||
clk_rate_exclusive_put(dsi->mod_clk);
|
||||
|
||||
if (!IS_ERR(dsi->bus_clk))
|
||||
regmap_mmio_detach_clk(dsi->regs);
|
||||
regmap_mmio_detach_clk(dsi->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -801,7 +801,6 @@ static int simpledrm_device_init_modeset(struct simpledrm_device *sdev)
|
||||
dev->mode_config.max_width = max_width;
|
||||
dev->mode_config.min_height = mode->vdisplay;
|
||||
dev->mode_config.max_height = max_height;
|
||||
dev->mode_config.prefer_shadow_fbdev = true;
|
||||
dev->mode_config.preferred_depth = sdev->format->cpp[0] * 8;
|
||||
dev->mode_config.funcs = &simpledrm_mode_config_funcs;
|
||||
|
||||
|
@ -738,19 +738,13 @@ static const struct drm_gem_object_funcs vc4_gem_object_funcs = {
|
||||
|
||||
static int vc4_grab_bin_bo(struct vc4_dev *vc4, struct vc4_file *vc4file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!vc4->v3d)
|
||||
return -ENODEV;
|
||||
|
||||
if (vc4file->bin_bo_used)
|
||||
return 0;
|
||||
|
||||
ret = vc4_v3d_bin_bo_get(vc4, &vc4file->bin_bo_used);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return vc4_v3d_bin_bo_get(vc4, &vc4file->bin_bo_used);
|
||||
}
|
||||
|
||||
int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
|
||||
|
@ -1350,7 +1350,6 @@ static int vc4_prepare_fb(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct vc4_bo *bo;
|
||||
int ret;
|
||||
|
||||
if (!state->fb)
|
||||
return 0;
|
||||
@ -1362,11 +1361,7 @@ static int vc4_prepare_fb(struct drm_plane *plane,
|
||||
if (plane->state->fb == state->fb)
|
||||
return 0;
|
||||
|
||||
ret = vc4_bo_inc_usecnt(bo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return vc4_bo_inc_usecnt(bo);
|
||||
}
|
||||
|
||||
static void vc4_cleanup_fb(struct drm_plane *plane,
|
||||
|
@ -316,19 +316,18 @@ static int vmw_fb_pan_display(struct fb_var_screeninfo *var,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vmw_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void vmw_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
struct vmw_fb_par *par = info->par;
|
||||
unsigned long start, end, min, max;
|
||||
unsigned long flags;
|
||||
struct page *page;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
int y1, y2;
|
||||
|
||||
min = ULONG_MAX;
|
||||
max = 0;
|
||||
list_for_each_entry(page, pagelist, lru) {
|
||||
start = page->index << PAGE_SHIFT;
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
start = pageref->offset;
|
||||
end = start + PAGE_SIZE - 1;
|
||||
min = min(min, start);
|
||||
max = max(max, end);
|
||||
@ -619,6 +618,7 @@ static const struct fb_ops vmw_fb_ops = {
|
||||
.fb_imageblit = vmw_fb_imageblit,
|
||||
.fb_pan_display = vmw_fb_pan_display,
|
||||
.fb_blank = vmw_fb_blank,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
int vmw_fb_init(struct vmw_private *vmw_priv)
|
||||
|
@ -428,11 +428,12 @@ static const struct fb_ops picolcdfb_ops = {
|
||||
.fb_imageblit = picolcd_fb_imageblit,
|
||||
.fb_check_var = picolcd_fb_check_var,
|
||||
.fb_set_par = picolcd_set_par,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
|
||||
/* Callback from deferred IO workqueue */
|
||||
static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
|
||||
static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
picolcd_fb_update(info);
|
||||
}
|
||||
|
@ -322,12 +322,11 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height)
|
||||
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
|
||||
}
|
||||
|
||||
static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
|
||||
static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
struct fbtft_par *par = info->par;
|
||||
unsigned int dirty_lines_start, dirty_lines_end;
|
||||
struct page *page;
|
||||
unsigned long index;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
unsigned int y_low = 0, y_high = 0;
|
||||
int count = 0;
|
||||
|
||||
@ -340,14 +339,13 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
|
||||
spin_unlock(&par->dirty_lock);
|
||||
|
||||
/* Mark display lines as dirty */
|
||||
list_for_each_entry(page, pagelist, lru) {
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
count++;
|
||||
index = page->index << PAGE_SHIFT;
|
||||
y_low = index / info->fix.line_length;
|
||||
y_high = (index + PAGE_SIZE - 1) / info->fix.line_length;
|
||||
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",
|
||||
page->index, y_low, y_high);
|
||||
pageref->page->index, y_low, y_high);
|
||||
if (y_high > info->var.yres - 1)
|
||||
y_high = info->var.yres - 1;
|
||||
if (y_low < dirty_lines_start)
|
||||
@ -652,10 +650,11 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
|
||||
fbops->fb_imageblit = fbtft_fb_imageblit;
|
||||
fbops->fb_setcolreg = fbtft_fb_setcolreg;
|
||||
fbops->fb_blank = fbtft_fb_blank;
|
||||
fbops->fb_mmap = fb_deferred_io_mmap;
|
||||
|
||||
fbdefio->delay = HZ / fps;
|
||||
fbdefio->sort_pagelist = true;
|
||||
fbdefio->deferred_io = fbtft_deferred_io;
|
||||
fbdefio->delay = HZ / fps;
|
||||
fbdefio->sort_pagereflist = true;
|
||||
fbdefio->deferred_io = fbtft_deferred_io;
|
||||
fb_deferred_io_init(info);
|
||||
|
||||
snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name);
|
||||
|
@ -929,13 +929,11 @@ static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
|
||||
}
|
||||
|
||||
/* this is called back from the deferred io workqueue */
|
||||
static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void broadsheetfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
u16 y1 = 0, h = 0;
|
||||
int prev_index = -1;
|
||||
struct page *cur;
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
unsigned long prev_offset = ULONG_MAX;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
int h_inc;
|
||||
u16 yres = info->var.yres;
|
||||
u16 xres = info->var.xres;
|
||||
@ -944,22 +942,22 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
|
||||
h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
|
||||
|
||||
/* walk the written page list and swizzle the data */
|
||||
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
|
||||
if (prev_index < 0) {
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
if (prev_offset == ULONG_MAX) {
|
||||
/* just starting so assign first page */
|
||||
y1 = (cur->index << PAGE_SHIFT) / xres;
|
||||
y1 = pageref->offset / xres;
|
||||
h = h_inc;
|
||||
} else if ((prev_index + 1) == cur->index) {
|
||||
} else if ((prev_offset + PAGE_SIZE) == pageref->offset) {
|
||||
/* this page is consecutive so increase our height */
|
||||
h += h_inc;
|
||||
} else {
|
||||
/* page not consecutive, issue previous update first */
|
||||
broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);
|
||||
/* start over with our non consecutive page */
|
||||
y1 = (cur->index << PAGE_SHIFT) / xres;
|
||||
y1 = pageref->offset / xres;
|
||||
h = h_inc;
|
||||
}
|
||||
prev_index = cur->index;
|
||||
prev_offset = pageref->offset;
|
||||
}
|
||||
|
||||
/* if we still have any pages to update we do so now */
|
||||
@ -1055,12 +1053,13 @@ static const struct fb_ops broadsheetfb_ops = {
|
||||
.fb_fillrect = broadsheetfb_fillrect,
|
||||
.fb_copyarea = broadsheetfb_copyarea,
|
||||
.fb_imageblit = broadsheetfb_imageblit,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
static struct fb_deferred_io broadsheetfb_defio = {
|
||||
.delay = HZ/4,
|
||||
.sort_pagelist = true,
|
||||
.deferred_io = broadsheetfb_dpy_deferred_io,
|
||||
.delay = HZ/4,
|
||||
.sort_pagereflist = true,
|
||||
.deferred_io = broadsheetfb_dpy_deferred_io,
|
||||
};
|
||||
|
||||
static int broadsheetfb_probe(struct platform_device *dev)
|
||||
|
@ -36,6 +36,60 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs
|
||||
return page;
|
||||
}
|
||||
|
||||
static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info *info,
|
||||
unsigned long offset,
|
||||
struct page *page)
|
||||
{
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
struct list_head *pos = &fbdefio->pagereflist;
|
||||
unsigned long pgoff = offset >> PAGE_SHIFT;
|
||||
struct fb_deferred_io_pageref *pageref, *cur;
|
||||
|
||||
if (WARN_ON_ONCE(pgoff >= info->npagerefs))
|
||||
return NULL; /* incorrect allocation size */
|
||||
|
||||
/* 1:1 mapping between pageref and page offset */
|
||||
pageref = &info->pagerefs[pgoff];
|
||||
|
||||
/*
|
||||
* This check is to catch the case where a new process could start
|
||||
* writing to the same page through a new PTE. This new access
|
||||
* can cause a call to .page_mkwrite even if the original process'
|
||||
* PTE is marked writable.
|
||||
*/
|
||||
if (!list_empty(&pageref->list))
|
||||
goto pageref_already_added;
|
||||
|
||||
pageref->page = page;
|
||||
pageref->offset = pgoff << PAGE_SHIFT;
|
||||
|
||||
if (unlikely(fbdefio->sort_pagereflist)) {
|
||||
/*
|
||||
* We loop through the list of pagerefs before adding in
|
||||
* order to keep the pagerefs sorted. This has significant
|
||||
* overhead of O(n^2) with n being the number of written
|
||||
* pages. If possible, drivers should try to work with
|
||||
* unsorted page lists instead.
|
||||
*/
|
||||
list_for_each_entry(cur, &fbdefio->pagereflist, list) {
|
||||
if (cur->offset > pageref->offset)
|
||||
break;
|
||||
}
|
||||
pos = &cur->list;
|
||||
}
|
||||
|
||||
list_add_tail(&pageref->list, pos);
|
||||
|
||||
pageref_already_added:
|
||||
return pageref;
|
||||
}
|
||||
|
||||
static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
|
||||
struct fb_info *info)
|
||||
{
|
||||
list_del_init(&pageref->list);
|
||||
}
|
||||
|
||||
/* this is to find and return the vmalloc-ed fb pages */
|
||||
static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
|
||||
{
|
||||
@ -59,7 +113,7 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
|
||||
printk(KERN_ERR "no mapping available\n");
|
||||
|
||||
BUG_ON(!page->mapping);
|
||||
page->index = vmf->pgoff;
|
||||
page->index = vmf->pgoff; /* for page_mkclean() */
|
||||
|
||||
vmf->page = page;
|
||||
return 0;
|
||||
@ -89,29 +143,30 @@ int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasy
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
|
||||
|
||||
/* vm_ops->page_mkwrite handler */
|
||||
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
|
||||
/*
|
||||
* Adds a page to the dirty list. Call this from struct
|
||||
* vm_operations_struct.page_mkwrite.
|
||||
*/
|
||||
static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
|
||||
struct page *page)
|
||||
{
|
||||
struct page *page = vmf->page;
|
||||
struct fb_info *info = vmf->vma->vm_private_data;
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
struct list_head *pos = &fbdefio->pagelist;
|
||||
|
||||
/* this is a callback we get when userspace first tries to
|
||||
write to the page. we schedule a workqueue. that workqueue
|
||||
will eventually mkclean the touched pages and execute the
|
||||
deferred framebuffer IO. then if userspace touches a page
|
||||
again, we repeat the same scheme */
|
||||
|
||||
file_update_time(vmf->vma->vm_file);
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
vm_fault_t ret;
|
||||
|
||||
/* protect against the workqueue changing the page list */
|
||||
mutex_lock(&fbdefio->lock);
|
||||
|
||||
/* first write in this cycle, notify the driver */
|
||||
if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
|
||||
if (fbdefio->first_io && list_empty(&fbdefio->pagereflist))
|
||||
fbdefio->first_io(info);
|
||||
|
||||
pageref = fb_deferred_io_pageref_get(info, offset, page);
|
||||
if (WARN_ON_ONCE(!pageref)) {
|
||||
ret = VM_FAULT_OOM;
|
||||
goto err_mutex_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want the page to remain locked from ->page_mkwrite until
|
||||
* the PTE is marked dirty to avoid page_mkclean() being called
|
||||
@ -120,47 +175,49 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
|
||||
* Do this by locking the page here and informing the caller
|
||||
* about it with VM_FAULT_LOCKED.
|
||||
*/
|
||||
lock_page(page);
|
||||
lock_page(pageref->page);
|
||||
|
||||
/*
|
||||
* This check is to catch the case where a new process could start
|
||||
* writing to the same page through a new PTE. This new access
|
||||
* can cause a call to .page_mkwrite even if the original process'
|
||||
* PTE is marked writable.
|
||||
*
|
||||
* TODO: The lru field is owned by the page cache; hence the name.
|
||||
* We dequeue in fb_deferred_io_work() after flushing the
|
||||
* page's content into video memory. Instead of lru, fbdefio
|
||||
* should have it's own field.
|
||||
*/
|
||||
if (!list_empty(&page->lru))
|
||||
goto page_already_added;
|
||||
|
||||
if (unlikely(fbdefio->sort_pagelist)) {
|
||||
/*
|
||||
* We loop through the pagelist before adding in order to
|
||||
* keep the pagelist sorted. This has significant overhead
|
||||
* of O(n^2) with n being the number of written pages. If
|
||||
* possible, drivers should try to work with unsorted page
|
||||
* lists instead.
|
||||
*/
|
||||
struct page *cur;
|
||||
|
||||
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
|
||||
if (cur->index > page->index)
|
||||
break;
|
||||
}
|
||||
pos = &cur->lru;
|
||||
}
|
||||
|
||||
list_add_tail(&page->lru, pos);
|
||||
|
||||
page_already_added:
|
||||
mutex_unlock(&fbdefio->lock);
|
||||
|
||||
/* come back after delay to process the deferred IO */
|
||||
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
|
||||
return VM_FAULT_LOCKED;
|
||||
|
||||
err_mutex_unlock:
|
||||
mutex_unlock(&fbdefio->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
|
||||
* @fb_info: The fbdev info structure
|
||||
* @vmf: The VM fault
|
||||
*
|
||||
* This is a callback we get when userspace first tries to
|
||||
* write to the page. We schedule a workqueue. That workqueue
|
||||
* will eventually mkclean the touched pages and execute the
|
||||
* deferred framebuffer IO. Then if userspace touches a page
|
||||
* again, we repeat the same scheme.
|
||||
*
|
||||
* Returns:
|
||||
* VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
|
||||
*/
|
||||
static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
|
||||
{
|
||||
unsigned long offset = vmf->address - vmf->vma->vm_start;
|
||||
struct page *page = vmf->page;
|
||||
|
||||
file_update_time(vmf->vma->vm_file);
|
||||
|
||||
return fb_deferred_io_track_page(info, offset, page);
|
||||
}
|
||||
|
||||
/* vm_ops->page_mkwrite handler */
|
||||
static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
|
||||
{
|
||||
struct fb_info *info = vmf->vma->vm_private_data;
|
||||
|
||||
return fb_deferred_io_page_mkwrite(info, vmf);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct fb_deferred_io_vm_ops = {
|
||||
@ -181,52 +238,70 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
vma->vm_private_data = info;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
|
||||
|
||||
/* workqueue callback */
|
||||
static void fb_deferred_io_work(struct work_struct *work)
|
||||
{
|
||||
struct fb_info *info = container_of(work, struct fb_info,
|
||||
deferred_work.work);
|
||||
struct list_head *node, *next;
|
||||
struct page *cur;
|
||||
struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
|
||||
struct fb_deferred_io_pageref *pageref, *next;
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
|
||||
/* here we mkclean the pages, then do all deferred IO */
|
||||
mutex_lock(&fbdefio->lock);
|
||||
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
|
||||
list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
|
||||
struct page *cur = pageref->page;
|
||||
lock_page(cur);
|
||||
page_mkclean(cur);
|
||||
unlock_page(cur);
|
||||
}
|
||||
|
||||
/* driver's callback with pagelist */
|
||||
fbdefio->deferred_io(info, &fbdefio->pagelist);
|
||||
/* driver's callback with pagereflist */
|
||||
fbdefio->deferred_io(info, &fbdefio->pagereflist);
|
||||
|
||||
/* clear the list */
|
||||
list_for_each_safe(node, next, &fbdefio->pagelist) {
|
||||
list_del_init(node);
|
||||
}
|
||||
list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
|
||||
fb_deferred_io_pageref_put(pageref, info);
|
||||
|
||||
mutex_unlock(&fbdefio->lock);
|
||||
}
|
||||
|
||||
void fb_deferred_io_init(struct fb_info *info)
|
||||
int fb_deferred_io_init(struct fb_info *info)
|
||||
{
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
struct page *page;
|
||||
unsigned int i;
|
||||
struct fb_deferred_io_pageref *pagerefs;
|
||||
unsigned long npagerefs, i;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!fbdefio);
|
||||
|
||||
if (WARN_ON(!info->fix.smem_len))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_init(&fbdefio->lock);
|
||||
INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
|
||||
INIT_LIST_HEAD(&fbdefio->pagelist);
|
||||
INIT_LIST_HEAD(&fbdefio->pagereflist);
|
||||
if (fbdefio->delay == 0) /* set a default of 1 s */
|
||||
fbdefio->delay = HZ;
|
||||
|
||||
/* initialize all the page lists one time */
|
||||
for (i = 0; i < info->fix.smem_len; i += PAGE_SIZE) {
|
||||
page = fb_deferred_io_page(info, i);
|
||||
INIT_LIST_HEAD(&page->lru);
|
||||
npagerefs = DIV_ROUND_UP(info->fix.smem_len, PAGE_SIZE);
|
||||
|
||||
/* alloc a page ref for each page of the display memory */
|
||||
pagerefs = kvcalloc(npagerefs, sizeof(*pagerefs), GFP_KERNEL);
|
||||
if (!pagerefs) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
for (i = 0; i < npagerefs; ++i)
|
||||
INIT_LIST_HEAD(&pagerefs[i].list);
|
||||
info->npagerefs = npagerefs;
|
||||
info->pagerefs = pagerefs;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mutex_destroy(&fbdefio->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fb_deferred_io_init);
|
||||
|
||||
@ -253,6 +328,7 @@ void fb_deferred_io_cleanup(struct fb_info *info)
|
||||
page->mapping = NULL;
|
||||
}
|
||||
|
||||
kvfree(info->pagerefs);
|
||||
mutex_destroy(&fbdefio->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
|
||||
|
@ -1334,7 +1334,6 @@ static int
|
||||
fb_mmap(struct file *file, struct vm_area_struct * vma)
|
||||
{
|
||||
struct fb_info *info = file_fb_info(file);
|
||||
int (*fb_mmap_fn)(struct fb_info *info, struct vm_area_struct *vma);
|
||||
unsigned long mmio_pgoff;
|
||||
unsigned long start;
|
||||
u32 len;
|
||||
@ -1343,14 +1342,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
|
||||
return -ENODEV;
|
||||
mutex_lock(&info->mm_lock);
|
||||
|
||||
fb_mmap_fn = info->fbops->fb_mmap;
|
||||
|
||||
#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
|
||||
if (info->fbdefio)
|
||||
fb_mmap_fn = fb_deferred_io_mmap;
|
||||
#endif
|
||||
|
||||
if (fb_mmap_fn) {
|
||||
if (info->fbops->fb_mmap) {
|
||||
int res;
|
||||
|
||||
/*
|
||||
@ -1358,9 +1350,19 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
|
||||
* SME protection is removed ahead of the call
|
||||
*/
|
||||
vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
|
||||
res = fb_mmap_fn(info, vma);
|
||||
res = info->fbops->fb_mmap(info, vma);
|
||||
mutex_unlock(&info->mm_lock);
|
||||
return res;
|
||||
#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
|
||||
} else if (info->fbdefio) {
|
||||
/*
|
||||
* FB deferred I/O wants you to handle mmap in your drivers. At a
|
||||
* minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap().
|
||||
*/
|
||||
dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n");
|
||||
mutex_unlock(&info->mm_lock);
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -115,8 +115,7 @@ static void hecubafb_dpy_update(struct hecubafb_par *par)
|
||||
}
|
||||
|
||||
/* this is called back from the deferred io workqueue */
|
||||
static void hecubafb_dpy_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void hecubafb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
hecubafb_dpy_update(info->par);
|
||||
}
|
||||
@ -204,6 +203,7 @@ static const struct fb_ops hecubafb_ops = {
|
||||
.fb_fillrect = hecubafb_fillrect,
|
||||
.fb_copyarea = hecubafb_copyarea,
|
||||
.fb_imageblit = hecubafb_imageblit,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
static struct fb_deferred_io hecubafb_defio = {
|
||||
|
@ -420,11 +420,10 @@ static void hvfb_docopy(struct hvfb_par *par,
|
||||
}
|
||||
|
||||
/* Deferred IO callback */
|
||||
static void synthvid_deferred_io(struct fb_info *p,
|
||||
struct list_head *pagelist)
|
||||
static void synthvid_deferred_io(struct fb_info *p, struct list_head *pagereflist)
|
||||
{
|
||||
struct hvfb_par *par = p->par;
|
||||
struct page *page;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
unsigned long start, end;
|
||||
int y1, y2, miny, maxy;
|
||||
|
||||
@ -437,8 +436,8 @@ static void synthvid_deferred_io(struct fb_info *p,
|
||||
* in synthvid_update function by clamping the y2
|
||||
* value to yres.
|
||||
*/
|
||||
list_for_each_entry(page, pagelist, lru) {
|
||||
start = page->index << PAGE_SHIFT;
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
start = pageref->offset;
|
||||
end = start + PAGE_SIZE - 1;
|
||||
y1 = start / p->fix.line_length;
|
||||
y2 = end / p->fix.line_length;
|
||||
@ -909,6 +908,7 @@ static const struct fb_ops hvfb_ops = {
|
||||
.fb_copyarea = hvfb_cfb_copyarea,
|
||||
.fb_imageblit = hvfb_cfb_imageblit,
|
||||
.fb_blank = hvfb_blank,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
|
||||
|
@ -465,20 +465,18 @@ static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
|
||||
}
|
||||
|
||||
/* this is called back from the deferred io workqueue */
|
||||
static void metronomefb_dpy_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void metronomefb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
u16 cksum;
|
||||
struct page *cur;
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
struct metronomefb_par *par = info->par;
|
||||
|
||||
/* walk the written page list and swizzle the data */
|
||||
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
|
||||
cksum = metronomefb_dpy_update_page(par,
|
||||
(cur->index << PAGE_SHIFT));
|
||||
par->metromem_img_csum -= par->csum_table[cur->index];
|
||||
par->csum_table[cur->index] = cksum;
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
unsigned long pgoffset = pageref->offset >> PAGE_SHIFT;
|
||||
cksum = metronomefb_dpy_update_page(par, pageref->offset);
|
||||
par->metromem_img_csum -= par->csum_table[pgoffset];
|
||||
par->csum_table[pgoffset] = cksum;
|
||||
par->metromem_img_csum += cksum;
|
||||
}
|
||||
|
||||
@ -564,12 +562,13 @@ static const struct fb_ops metronomefb_ops = {
|
||||
.fb_fillrect = metronomefb_fillrect,
|
||||
.fb_copyarea = metronomefb_copyarea,
|
||||
.fb_imageblit = metronomefb_imageblit,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
static struct fb_deferred_io metronomefb_defio = {
|
||||
.delay = HZ,
|
||||
.sort_pagelist = true,
|
||||
.deferred_io = metronomefb_dpy_deferred_io,
|
||||
.delay = HZ,
|
||||
.sort_pagereflist = true,
|
||||
.deferred_io = metronomefb_dpy_deferred_io,
|
||||
};
|
||||
|
||||
static int metronomefb_probe(struct platform_device *dev)
|
||||
|
@ -435,24 +435,23 @@ static struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
|
||||
.read_data = lcdc_sys_read_data,
|
||||
};
|
||||
|
||||
static int sh_mobile_lcdc_sginit(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static int sh_mobile_lcdc_sginit(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
struct sh_mobile_lcdc_chan *ch = info->par;
|
||||
unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
|
||||
struct page *page;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
int nr_pages = 0;
|
||||
|
||||
sg_init_table(ch->sglist, nr_pages_max);
|
||||
|
||||
list_for_each_entry(page, pagelist, lru)
|
||||
sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
sg_set_page(&ch->sglist[nr_pages++], pageref->page, PAGE_SIZE, 0);
|
||||
}
|
||||
|
||||
return nr_pages;
|
||||
}
|
||||
|
||||
static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
struct sh_mobile_lcdc_chan *ch = info->par;
|
||||
const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
|
||||
@ -461,7 +460,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
|
||||
sh_mobile_lcdc_clk_on(ch->lcdc);
|
||||
|
||||
/*
|
||||
* It's possible to get here without anything on the pagelist via
|
||||
* It's possible to get here without anything on the pagereflist via
|
||||
* sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
|
||||
* invocation. In the former case, the acceleration routines are
|
||||
* stepped in to when using the framebuffer console causing the
|
||||
@ -471,12 +470,12 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
|
||||
* acceleration routines have their own methods for writing in
|
||||
* that still need to be updated.
|
||||
*
|
||||
* The fsync() and empty pagelist case could be optimized for,
|
||||
* The fsync() and empty pagereflist case could be optimized for,
|
||||
* but we don't bother, as any application exhibiting such
|
||||
* behaviour is fundamentally broken anyways.
|
||||
*/
|
||||
if (!list_empty(pagelist)) {
|
||||
unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
|
||||
if (!list_empty(pagereflist)) {
|
||||
unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagereflist);
|
||||
|
||||
/* trigger panel update */
|
||||
dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
|
||||
@ -1480,6 +1479,9 @@ sh_mobile_lcdc_overlay_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct sh_mobile_lcdc_overlay *ovl = info->par;
|
||||
|
||||
if (info->fbdefio)
|
||||
return fb_deferred_io_mmap(info, vma);
|
||||
|
||||
return dma_mmap_coherent(ovl->channel->lcdc->dev, vma, ovl->fb_mem,
|
||||
ovl->dma_handle, ovl->fb_size);
|
||||
}
|
||||
@ -1954,6 +1956,9 @@ sh_mobile_lcdc_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct sh_mobile_lcdc_chan *ch = info->par;
|
||||
|
||||
if (info->fbdefio)
|
||||
return fb_deferred_io_mmap(info, vma);
|
||||
|
||||
return dma_mmap_coherent(ch->lcdc->dev, vma, ch->fb_mem,
|
||||
ch->dma_handle, ch->fb_size);
|
||||
}
|
||||
|
@ -779,6 +779,9 @@ static int ufx_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
unsigned long page, pos;
|
||||
|
||||
if (info->fbdefio)
|
||||
return fb_deferred_io_mmap(info, vma);
|
||||
|
||||
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
|
||||
return -EINVAL;
|
||||
if (size > info->fix.smem_len)
|
||||
@ -952,12 +955,10 @@ static void ufx_ops_fillrect(struct fb_info *info,
|
||||
* Touching ANY framebuffer memory that triggers a page fault
|
||||
* in fb_defio will cause a deadlock, when it also tries to
|
||||
* grab the same mutex. */
|
||||
static void ufx_dpy_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void ufx_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
struct page *cur;
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
struct ufx_data *dev = info->par;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
|
||||
if (!fb_defio)
|
||||
return;
|
||||
@ -966,12 +967,12 @@ static void ufx_dpy_deferred_io(struct fb_info *info,
|
||||
return;
|
||||
|
||||
/* walk the written page list and render each to device */
|
||||
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
/* create a rectangle of full screen width that encloses the
|
||||
* entire dirty framebuffer page */
|
||||
const int x = 0;
|
||||
const int width = dev->info->var.xres;
|
||||
const int y = (cur->index << PAGE_SHIFT) / (width * 2);
|
||||
const int y = pageref->offset / (width * 2);
|
||||
int height = (PAGE_SIZE / (width * 2)) + 1;
|
||||
height = min(height, (int)(dev->info->var.yres - y));
|
||||
|
||||
|
@ -368,10 +368,10 @@ static const struct fb_ops ssd1307fb_ops = {
|
||||
.fb_fillrect = ssd1307fb_fillrect,
|
||||
.fb_copyarea = ssd1307fb_copyarea,
|
||||
.fb_imageblit = ssd1307fb_imageblit,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
static void ssd1307fb_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
ssd1307fb_update_display(info->par);
|
||||
}
|
||||
|
@ -326,6 +326,9 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
unsigned long page, pos;
|
||||
|
||||
if (info->fbdefio)
|
||||
return fb_deferred_io_mmap(info, vma);
|
||||
|
||||
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
|
||||
return -EINVAL;
|
||||
if (size > info->fix.smem_len)
|
||||
@ -778,11 +781,9 @@ static void dlfb_ops_fillrect(struct fb_info *info,
|
||||
* in fb_defio will cause a deadlock, when it also tries to
|
||||
* grab the same mutex.
|
||||
*/
|
||||
static void dlfb_dpy_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist)
|
||||
static void dlfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
|
||||
{
|
||||
struct page *cur;
|
||||
struct fb_deferred_io *fbdefio = info->fbdefio;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
struct dlfb_data *dlfb = info->par;
|
||||
struct urb *urb;
|
||||
char *cmd;
|
||||
@ -808,11 +809,10 @@ static void dlfb_dpy_deferred_io(struct fb_info *info,
|
||||
cmd = urb->transfer_buffer;
|
||||
|
||||
/* walk the written page list and render each to device */
|
||||
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
|
||||
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
|
||||
&cmd, cur->index << PAGE_SHIFT,
|
||||
PAGE_SIZE, &bytes_identical, &bytes_sent))
|
||||
&cmd, pageref->offset, PAGE_SIZE,
|
||||
&bytes_identical, &bytes_sent))
|
||||
goto error;
|
||||
bytes_rendered += PAGE_SIZE;
|
||||
}
|
||||
@ -980,7 +980,7 @@ static int dlfb_ops_open(struct fb_info *info, int user)
|
||||
|
||||
if (fbdefio) {
|
||||
fbdefio->delay = DL_DEFIO_WRITE_DELAY;
|
||||
fbdefio->sort_pagelist = true;
|
||||
fbdefio->sort_pagereflist = true;
|
||||
fbdefio->deferred_io = dlfb_dpy_deferred_io;
|
||||
}
|
||||
|
||||
|
@ -181,18 +181,17 @@ static void xenfb_refresh(struct xenfb_info *info,
|
||||
xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
|
||||
}
|
||||
|
||||
static void xenfb_deferred_io(struct fb_info *fb_info,
|
||||
struct list_head *pagelist)
|
||||
static void xenfb_deferred_io(struct fb_info *fb_info, struct list_head *pagereflist)
|
||||
{
|
||||
struct xenfb_info *info = fb_info->par;
|
||||
struct page *page;
|
||||
struct fb_deferred_io_pageref *pageref;
|
||||
unsigned long beg, end;
|
||||
int y1, y2, miny, maxy;
|
||||
|
||||
miny = INT_MAX;
|
||||
maxy = 0;
|
||||
list_for_each_entry(page, pagelist, lru) {
|
||||
beg = page->index << PAGE_SHIFT;
|
||||
list_for_each_entry(pageref, pagereflist, list) {
|
||||
beg = pageref->offset;
|
||||
end = beg + PAGE_SIZE - 1;
|
||||
y1 = beg / fb_info->fix.line_length;
|
||||
y2 = end / fb_info->fix.line_length;
|
||||
@ -338,6 +337,7 @@ static const struct fb_ops xenfb_fb_ops = {
|
||||
.fb_imageblit = xenfb_imageblit,
|
||||
.fb_check_var = xenfb_check_var,
|
||||
.fb_set_par = xenfb_set_par,
|
||||
.fb_mmap = fb_deferred_io_mmap,
|
||||
};
|
||||
|
||||
static irqreturn_t xenfb_event_handler(int rq, void *dev_id)
|
||||
|
@ -49,8 +49,6 @@ drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,
|
||||
struct drm_crtc *crtc);
|
||||
void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state,
|
||||
struct drm_framebuffer *fb);
|
||||
void drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state,
|
||||
struct dma_fence *fence);
|
||||
int __must_check
|
||||
drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
|
||||
struct drm_crtc *crtc);
|
||||
|
@ -229,8 +229,7 @@ void drm_fb_helper_fill_info(struct fb_info *info,
|
||||
struct drm_fb_helper *fb_helper,
|
||||
struct drm_fb_helper_surface_size *sizes);
|
||||
|
||||
void drm_fb_helper_deferred_io(struct fb_info *info,
|
||||
struct list_head *pagelist);
|
||||
void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist);
|
||||
|
||||
ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
|
||||
size_t count, loff_t *ppos);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_device;
|
||||
struct mutex;
|
||||
|
||||
typedef void (*drmres_release_t)(struct drm_device *dev, void *res);
|
||||
|
||||
@ -104,4 +105,6 @@ char *drmm_kstrdup(struct drm_device *dev, const char *s, gfp_t gfp);
|
||||
|
||||
void drmm_kfree(struct drm_device *dev, void *data);
|
||||
|
||||
int drmm_mutex_init(struct drm_device *dev, struct mutex *lock);
|
||||
|
||||
#endif
|
||||
|
@ -74,9 +74,7 @@ struct drm_plane_state {
|
||||
*
|
||||
* Optional fence to wait for before scanning out @fb. The core atomic
|
||||
* code will set this when userspace is using explicit fencing. Do not
|
||||
* write this field directly for a driver's implicit fence, use
|
||||
* drm_atomic_set_fence_for_plane() to ensure that an explicit fence is
|
||||
* preserved.
|
||||
* write this field directly for a driver's implicit fence.
|
||||
*
|
||||
* Drivers should store any implicit fence in this from their
|
||||
* &drm_plane_helper_funcs.prepare_fb callback. See drm_gem_plane_helper_prepare_fb()
|
||||
|
14
include/dt-bindings/soc/rockchip,vop2.h
Normal file
14
include/dt-bindings/soc/rockchip,vop2.h
Normal file
@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
|
||||
|
||||
#ifndef __DT_BINDINGS_ROCKCHIP_VOP2_H
|
||||
#define __DT_BINDINGS_ROCKCHIP_VOP2_H
|
||||
|
||||
#define ROCKCHIP_VOP2_EP_RGB0 1
|
||||
#define ROCKCHIP_VOP2_EP_HDMI0 2
|
||||
#define ROCKCHIP_VOP2_EP_EDP0 3
|
||||
#define ROCKCHIP_VOP2_EP_MIPI0 4
|
||||
#define ROCKCHIP_VOP2_EP_LVDS0 5
|
||||
#define ROCKCHIP_VOP2_EP_MIPI1 6
|
||||
#define ROCKCHIP_VOP2_EP_LVDS1 7
|
||||
|
||||
#endif /* __DT_BINDINGS_ROCKCHIP_VOP2_H */
|
@ -201,12 +201,19 @@ struct fb_pixmap {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FB_DEFERRED_IO
|
||||
struct fb_deferred_io_pageref {
|
||||
struct page *page;
|
||||
unsigned long offset;
|
||||
/* private */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct fb_deferred_io {
|
||||
/* delay between mkwrite and deferred handler */
|
||||
unsigned long delay;
|
||||
bool sort_pagelist; /* sort pagelist by offset */
|
||||
struct mutex lock; /* mutex that protects the page list */
|
||||
struct list_head pagelist; /* list of touched pages */
|
||||
bool sort_pagereflist; /* sort pagelist by offset */
|
||||
struct mutex lock; /* mutex that protects the pageref list */
|
||||
struct list_head pagereflist; /* list of pagerefs for touched pages */
|
||||
/* callback */
|
||||
void (*first_io)(struct fb_info *info);
|
||||
void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
|
||||
@ -468,6 +475,8 @@ struct fb_info {
|
||||
#endif
|
||||
#ifdef CONFIG_FB_DEFERRED_IO
|
||||
struct delayed_work deferred_work;
|
||||
unsigned long npagerefs;
|
||||
struct fb_deferred_io_pageref *pagerefs;
|
||||
struct fb_deferred_io *fbdefio;
|
||||
#endif
|
||||
|
||||
@ -661,7 +670,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
|
||||
|
||||
/* drivers/video/fb_defio.c */
|
||||
int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
|
||||
extern void fb_deferred_io_init(struct fb_info *info);
|
||||
extern int fb_deferred_io_init(struct fb_info *info);
|
||||
extern void fb_deferred_io_open(struct fb_info *info,
|
||||
struct inode *inode,
|
||||
struct file *file);
|
||||
|
Loading…
x
Reference in New Issue
Block a user