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:
Dave Airlie 2022-05-06 17:19:11 +10:00
commit c67f84e97b
98 changed files with 6003 additions and 880 deletions

View File

@ -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 {
};
};
};
};
};

View File

@ -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

View File

@ -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

View File

@ -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>;
};
};
};
};

View File

@ -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
----------------------

View File

@ -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;

View File

@ -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;

View File

@ -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

View 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);
}

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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;

View 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");

View File

@ -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

View File

@ -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);

View File

@ -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[] = {

View File

@ -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

View File

@ -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;
}

View File

@ -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.
*

View File

@ -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
*

View File

@ -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)

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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:

View File

@ -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;

View File

@ -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 *

View File

@ -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);

View File

@ -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 *

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = &ampire_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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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 = {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 = {

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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:

View File

@ -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;

File diff suppressed because it is too large Load Diff

View 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 */

View File

@ -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 = {

View 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),
},
};

View File

@ -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),

View File

@ -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;
}

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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
}
/*

View File

@ -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 = {

View File

@ -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,
};

View File

@ -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)

View File

@ -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);
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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()

View 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 */

View File

@ -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);