mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git
synced 2026-04-03 23:38:12 -04:00
Merge tag 'drm-misc-next-2025-12-19' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for 6.20:
Core Changes:
- dma-buf: Add tracepoints
- sched: Introduce new helpers
Driver Changes:
- amdxdna: Enable hardware context priority, Remove (obsolete and
never public) NPU2 Support, Race condition fix
- rockchip: Add RK3368 HDMI Support
- rz-du: Add RZ/V2H(P) MIPI-DSI Support
- panels:
- st7571: Introduce SPI support
- New panels: Sitronix ST7920, Samsung LTL106HL02, LG LH546WF1-ED01, HannStar HSD156JUW2
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maxime Ripard <mripard@redhat.com>
Link: https://patch.msgid.link/20251219-arcane-quaint-skunk-e383b0@houat
This commit is contained in:
@@ -14,16 +14,21 @@ description: |
|
||||
RZ/G2L alike family of SoC's. The encoder can operate in DSI mode, with
|
||||
up to four data lanes.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/display/dsi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a07g044-mipi-dsi # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-mipi-dsi # RZ/V2L
|
||||
- const: renesas,rzg2l-mipi-dsi
|
||||
|
||||
- items:
|
||||
- const: renesas,r9a09g056-mipi-dsi # RZ/V2N
|
||||
- const: renesas,r9a09g057-mipi-dsi
|
||||
|
||||
- enum:
|
||||
- renesas,r9a07g044-mipi-dsi # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-mipi-dsi # RZ/V2L
|
||||
- const: renesas,rzg2l-mipi-dsi
|
||||
- renesas,r9a09g057-mipi-dsi # RZ/V2H(P)
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -49,34 +54,56 @@ properties:
|
||||
- const: debug
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: DSI D-PHY PLL multiplied clock
|
||||
- description: DSI D-PHY system clock
|
||||
- description: DSI AXI bus clock
|
||||
- description: DSI Register access clock
|
||||
- description: DSI Video clock
|
||||
- description: DSI D-PHY Escape mode transmit clock
|
||||
oneOf:
|
||||
- items:
|
||||
- description: DSI D-PHY PLL multiplied clock
|
||||
- description: DSI D-PHY system clock
|
||||
- description: DSI AXI bus clock
|
||||
- description: DSI Register access clock
|
||||
- description: DSI Video clock
|
||||
- description: DSI D-PHY Escape mode transmit clock
|
||||
- items:
|
||||
- description: DSI D-PHY PLL reference clock
|
||||
- description: DSI AXI bus clock
|
||||
- description: DSI Register access clock
|
||||
- description: DSI Video clock
|
||||
- description: DSI D-PHY Escape mode transmit clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pllclk
|
||||
- const: sysclk
|
||||
- const: aclk
|
||||
- const: pclk
|
||||
- const: vclk
|
||||
- const: lpclk
|
||||
oneOf:
|
||||
- items:
|
||||
- const: pllclk
|
||||
- const: sysclk
|
||||
- const: aclk
|
||||
- const: pclk
|
||||
- const: vclk
|
||||
- const: lpclk
|
||||
- items:
|
||||
- const: pllrefclk
|
||||
- const: aclk
|
||||
- const: pclk
|
||||
- const: vclk
|
||||
- const: lpclk
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: MIPI_DSI_CMN_RSTB
|
||||
- description: MIPI_DSI_ARESET_N
|
||||
- description: MIPI_DSI_PRESET_N
|
||||
oneOf:
|
||||
- items:
|
||||
- description: MIPI_DSI_CMN_RSTB
|
||||
- description: MIPI_DSI_ARESET_N
|
||||
- description: MIPI_DSI_PRESET_N
|
||||
- items:
|
||||
- description: MIPI_DSI_ARESET_N
|
||||
- description: MIPI_DSI_PRESET_N
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: rst
|
||||
- const: arst
|
||||
- const: prst
|
||||
oneOf:
|
||||
- items:
|
||||
- const: rst
|
||||
- const: arst
|
||||
- const: prst
|
||||
- items:
|
||||
- const: arst
|
||||
- const: prst
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
@@ -130,6 +157,41 @@ required:
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
- $ref: ../dsi-controller.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a09g057-mipi-dsi
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 5
|
||||
|
||||
clock-names:
|
||||
maxItems: 5
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
maxItems: 2
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 6
|
||||
|
||||
clock-names:
|
||||
minItems: 6
|
||||
|
||||
resets:
|
||||
minItems: 3
|
||||
|
||||
reset-names:
|
||||
minItems: 3
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r9a07g044-cpg.h>
|
||||
|
||||
@@ -4,14 +4,16 @@
|
||||
$id: http://devicetree.org/schemas/display/panel/lg,sw43408.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LG SW43408 1080x2160 DSI panel
|
||||
title: LG SW43408 AMOLED DDIC
|
||||
|
||||
maintainers:
|
||||
- Casey Connolly <casey.connolly@linaro.org>
|
||||
|
||||
description:
|
||||
This panel is used on the Pixel 3, it is a 60hz OLED panel which
|
||||
required DSC (Display Stream Compression) and has rounded corners.
|
||||
The SW43408 is display driver IC with connected panel.
|
||||
|
||||
LG LH546WF1-ED01 panel is used on the Pixel 3, it is a 60hz OLED panel
|
||||
which required DSC (Display Stream Compression) and has rounded corners.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
@@ -19,6 +21,9 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
# LG 5.46 inch, 1080x2160 pixels, 18:9 ratio
|
||||
- lg,sw43408-lh546wf1-ed01
|
||||
- const: lg,sw43408
|
||||
|
||||
reg:
|
||||
@@ -46,7 +51,7 @@ examples:
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "lg,sw43408";
|
||||
compatible = "lg,sw43408-lh546wf1-ed01", "lg,sw43408";
|
||||
reg = <0>;
|
||||
|
||||
vddi-supply = <&vreg_l14a_1p88>;
|
||||
|
||||
@@ -55,6 +55,8 @@ properties:
|
||||
- panasonic,vvx10f004b00
|
||||
# Panasonic 10" WUXGA TFT LCD panel
|
||||
- panasonic,vvx10f034n00
|
||||
# Samsung ltl106hl02 10.6" Full HD TFT LCD panel
|
||||
- samsung,ltl106hl02-001
|
||||
# Samsung s6e3fa7 1080x2220 based AMS559NK06 AMOLED panel
|
||||
- samsung,s6e3fa7-ams559nk06
|
||||
# Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel
|
||||
|
||||
@@ -154,6 +154,8 @@ properties:
|
||||
- hannstar,hsd070pww1
|
||||
# HannStar Display Corp. HSD100PXN1 10.1" XGA LVDS panel
|
||||
- hannstar,hsd100pxn1
|
||||
# HannStar Display Corp. HSD156JUW2 15.6" FHD (1920x1080) TFT LCD panel
|
||||
- hannstar,hsd156juw2
|
||||
# Hitachi Ltd. Corporation 9" WVGA (800x480) TFT LCD panel
|
||||
- hit,tx23d38vm0caa
|
||||
# Innolux AT043TN24 4.3" WQVGA TFT LCD panel
|
||||
|
||||
@@ -6,11 +6,11 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung S6E3FC2X01 AMOLED DDIC
|
||||
|
||||
description: The S6E3FC2X01 is display driver IC with connected panel.
|
||||
|
||||
maintainers:
|
||||
- David Heidelberg <david@ixit.cz>
|
||||
|
||||
description: The S6E3FC2X01 is display driver IC with connected panel.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
@@ -25,25 +25,21 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios: true
|
||||
|
||||
port: true
|
||||
|
||||
vddio-supply:
|
||||
description: VDD regulator
|
||||
poc-supply:
|
||||
description: POC regulator
|
||||
|
||||
vci-supply:
|
||||
description: VCI regulator
|
||||
|
||||
poc-supply:
|
||||
description: POC regulator
|
||||
vddio-supply:
|
||||
description: VDD regulator
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reset-gpios
|
||||
- vddio-supply
|
||||
- vci-supply
|
||||
- poc-supply
|
||||
- vci-supply
|
||||
- vddio-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ properties:
|
||||
- rockchip,rk3228-dw-hdmi
|
||||
- rockchip,rk3288-dw-hdmi
|
||||
- rockchip,rk3328-dw-hdmi
|
||||
- rockchip,rk3368-dw-hdmi
|
||||
- rockchip,rk3399-dw-hdmi
|
||||
- rockchip,rk3568-dw-hdmi
|
||||
|
||||
|
||||
@@ -76,3 +76,28 @@ examples:
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
display@0 {
|
||||
compatible = "sitronix,st7571";
|
||||
reg = <0>;
|
||||
reset-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
|
||||
width-mm = <37>;
|
||||
height-mm = <27>;
|
||||
|
||||
panel-timing {
|
||||
hactive = <128>;
|
||||
vactive = <96>;
|
||||
hback-porch = <0>;
|
||||
vback-porch = <0>;
|
||||
clock-frequency = <0>;
|
||||
hfront-porch = <0>;
|
||||
hsync-len = <0>;
|
||||
vfront-porch = <0>;
|
||||
vsync-len = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/sitronix,st7920.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sitronix ST7920 LCD Display Controllers
|
||||
|
||||
maintainers:
|
||||
- Iker Pedrosa <ikerpedrosam@gmail.com>
|
||||
|
||||
description:
|
||||
The Sitronix ST7920 is a controller for monochrome dot-matrix graphical LCDs,
|
||||
most commonly used for 128x64 pixel displays.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sitronix,st7920
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides 5V Vdd power supply
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 600000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-max-frequency
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
display@0 {
|
||||
compatible = "sitronix,st7920";
|
||||
reg = <0>;
|
||||
vdd-supply = <®_5v>;
|
||||
reset-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
|
||||
spi-max-frequency = <600000>;
|
||||
spi-cs-high;
|
||||
};
|
||||
};
|
||||
10
MAINTAINERS
10
MAINTAINERS
@@ -8201,6 +8201,9 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/sitronix,st7567.yaml
|
||||
F: Documentation/devicetree/bindings/display/sitronix,st7571.yaml
|
||||
F: drivers/gpu/drm/sitronix/st7571-i2c.c
|
||||
F: drivers/gpu/drm/sitronix/st7571-spi.c
|
||||
F: drivers/gpu/drm/sitronix/st7571.c
|
||||
F: drivers/gpu/drm/sitronix/st7571.h
|
||||
|
||||
DRM DRIVER FOR SITRONIX ST7701 PANELS
|
||||
M: Jagan Teki <jagan@amarulasolutions.com>
|
||||
@@ -8223,6 +8226,13 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: Documentation/devicetree/bindings/display/sitronix,st7735r.yaml
|
||||
F: drivers/gpu/drm/sitronix/st7735r.c
|
||||
|
||||
DRM DRIVER FOR SITRONIX ST7920 LCD DISPLAYS
|
||||
M: Iker Pedrosa <ikerpedrosam@gmail.com>
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: Documentation/devicetree/bindings/display/sitronix,st7920.yaml
|
||||
F: drivers/gpu/drm/sitronix/st7920.c
|
||||
|
||||
DRM DRIVER FOR SOLOMON SSD130X OLED DISPLAYS
|
||||
M: Javier Martinez Canillas <javierm@redhat.com>
|
||||
S: Maintained
|
||||
|
||||
@@ -18,7 +18,6 @@ amdxdna-y := \
|
||||
amdxdna_sysfs.o \
|
||||
amdxdna_ubuf.o \
|
||||
npu1_regs.o \
|
||||
npu2_regs.o \
|
||||
npu4_regs.o \
|
||||
npu5_regs.o \
|
||||
npu6_regs.o
|
||||
|
||||
@@ -468,6 +468,12 @@ static int aie2_alloc_resource(struct amdxdna_hwctx *hwctx)
|
||||
struct alloc_requests *xrs_req;
|
||||
int ret;
|
||||
|
||||
if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) {
|
||||
hwctx->num_unused_col = xdna->dev_handle->total_col - hwctx->num_col;
|
||||
hwctx->num_col = xdna->dev_handle->total_col;
|
||||
return aie2_create_context(xdna->dev_handle, hwctx);
|
||||
}
|
||||
|
||||
xrs_req = kzalloc(sizeof(*xrs_req), GFP_KERNEL);
|
||||
if (!xrs_req)
|
||||
return -ENOMEM;
|
||||
@@ -499,9 +505,15 @@ static void aie2_release_resource(struct amdxdna_hwctx *hwctx)
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
int ret;
|
||||
|
||||
ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
|
||||
if (ret)
|
||||
XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
|
||||
if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) {
|
||||
ret = aie2_destroy_context(xdna->dev_handle, hwctx);
|
||||
if (ret)
|
||||
XDNA_ERR(xdna, "Destroy temporal only context failed, ret %d", ret);
|
||||
} else {
|
||||
ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
|
||||
if (ret)
|
||||
XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static int aie2_ctx_syncobj_create(struct amdxdna_hwctx *hwctx)
|
||||
|
||||
@@ -192,6 +192,40 @@ int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id)
|
||||
{
|
||||
DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT);
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
int ret;
|
||||
|
||||
req.context_id = id;
|
||||
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
|
||||
if (ret)
|
||||
XDNA_WARN(xdna, "Destroy context failed, ret %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev,
|
||||
struct amdxdna_hwctx *hwctx)
|
||||
{
|
||||
if (!AIE2_FEATURE_ON(ndev, AIE2_PREEMPT))
|
||||
return PRIORITY_HIGH;
|
||||
|
||||
switch (hwctx->qos.priority) {
|
||||
case AMDXDNA_QOS_REALTIME_PRIORITY:
|
||||
return PRIORITY_REALTIME;
|
||||
case AMDXDNA_QOS_HIGH_PRIORITY:
|
||||
return PRIORITY_HIGH;
|
||||
case AMDXDNA_QOS_NORMAL_PRIORITY:
|
||||
return PRIORITY_NORMAL;
|
||||
case AMDXDNA_QOS_LOW_PRIORITY:
|
||||
return PRIORITY_LOW;
|
||||
default:
|
||||
return PRIORITY_HIGH;
|
||||
}
|
||||
}
|
||||
|
||||
int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
|
||||
{
|
||||
DECLARE_AIE2_MSG(create_ctx, MSG_OP_CREATE_CONTEXT);
|
||||
@@ -205,22 +239,24 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
|
||||
req.aie_type = 1;
|
||||
req.start_col = hwctx->start_col;
|
||||
req.num_col = hwctx->num_col;
|
||||
req.num_unused_col = hwctx->num_unused_col;
|
||||
req.num_cq_pairs_requested = 1;
|
||||
req.pasid = hwctx->client->pasid;
|
||||
req.context_priority = 2;
|
||||
req.context_priority = aie2_get_context_priority(ndev, hwctx);
|
||||
|
||||
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hwctx->fw_ctx_id = resp.context_id;
|
||||
WARN_ONCE(hwctx->fw_ctx_id == -1, "Unexpected context id");
|
||||
if (WARN_ON_ONCE(hwctx->fw_ctx_id == -1))
|
||||
return -EINVAL;
|
||||
|
||||
if (ndev->force_preempt_enabled) {
|
||||
ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FORCE_PREEMPT, &hwctx->fw_ctx_id);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "failed to enable force preempt %d", ret);
|
||||
return ret;
|
||||
goto del_ctx_req;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,51 +273,39 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
|
||||
|
||||
ret = pci_irq_vector(to_pci_dev(xdna->ddev.dev), resp.msix_id);
|
||||
if (ret == -EINVAL) {
|
||||
XDNA_ERR(xdna, "not able to create channel");
|
||||
goto out_destroy_context;
|
||||
XDNA_ERR(xdna, "Alloc IRQ failed %d", ret);
|
||||
goto del_ctx_req;
|
||||
}
|
||||
|
||||
intr_reg = i2x.mb_head_ptr_reg + 4;
|
||||
hwctx->priv->mbox_chann = xdna_mailbox_create_channel(ndev->mbox, &x2i, &i2x,
|
||||
intr_reg, ret);
|
||||
if (!hwctx->priv->mbox_chann) {
|
||||
XDNA_ERR(xdna, "not able to create channel");
|
||||
XDNA_ERR(xdna, "Not able to create channel");
|
||||
ret = -EINVAL;
|
||||
goto out_destroy_context;
|
||||
goto del_ctx_req;
|
||||
}
|
||||
ndev->hwctx_num++;
|
||||
|
||||
XDNA_DBG(xdna, "%s mailbox channel irq: %d, msix_id: %d",
|
||||
hwctx->name, ret, resp.msix_id);
|
||||
XDNA_DBG(xdna, "%s created fw ctx %d pasid %d", hwctx->name,
|
||||
hwctx->fw_ctx_id, hwctx->client->pasid);
|
||||
XDNA_DBG(xdna, "Mailbox channel irq: %d, msix_id: %d", ret, resp.msix_id);
|
||||
XDNA_DBG(xdna, "Created fw ctx %d pasid %d", hwctx->fw_ctx_id, hwctx->client->pasid);
|
||||
|
||||
return 0;
|
||||
|
||||
out_destroy_context:
|
||||
aie2_destroy_context(ndev, hwctx);
|
||||
del_ctx_req:
|
||||
aie2_destroy_context_req(ndev, hwctx->fw_ctx_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
|
||||
{
|
||||
DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT);
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
int ret;
|
||||
|
||||
if (hwctx->fw_ctx_id == -1)
|
||||
return 0;
|
||||
|
||||
xdna_mailbox_stop_channel(hwctx->priv->mbox_chann);
|
||||
|
||||
req.context_id = hwctx->fw_ctx_id;
|
||||
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
|
||||
if (ret)
|
||||
XDNA_WARN(xdna, "%s destroy context failed, ret %d", hwctx->name, ret);
|
||||
|
||||
ret = aie2_destroy_context_req(ndev, hwctx->fw_ctx_id);
|
||||
xdna_mailbox_destroy_channel(hwctx->priv->mbox_chann);
|
||||
XDNA_DBG(xdna, "%s destroyed fw ctx %d", hwctx->name,
|
||||
hwctx->fw_ctx_id);
|
||||
XDNA_DBG(xdna, "Destroyed fw ctx %d", hwctx->fw_ctx_id);
|
||||
hwctx->priv->mbox_chann = NULL;
|
||||
hwctx->fw_ctx_id = -1;
|
||||
ndev->hwctx_num--;
|
||||
|
||||
@@ -108,11 +108,17 @@ struct cq_pair {
|
||||
struct cq_info i2x_q;
|
||||
};
|
||||
|
||||
#define PRIORITY_REALTIME 1
|
||||
#define PRIORITY_HIGH 2
|
||||
#define PRIORITY_NORMAL 3
|
||||
#define PRIORITY_LOW 4
|
||||
|
||||
struct create_ctx_req {
|
||||
__u32 aie_type;
|
||||
__u8 start_col;
|
||||
__u8 num_col;
|
||||
__u16 reserved;
|
||||
__u8 num_unused_col;
|
||||
__u8 reserved;
|
||||
__u8 num_cq_pairs_requested;
|
||||
__u8 reserved1;
|
||||
__u16 pasid;
|
||||
|
||||
@@ -232,6 +232,7 @@ struct aie2_hw_ops {
|
||||
enum aie2_fw_feature {
|
||||
AIE2_NPU_COMMAND,
|
||||
AIE2_PREEMPT,
|
||||
AIE2_TEMPORAL_ONLY,
|
||||
AIE2_FEATURE_MAX
|
||||
};
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ struct amdxdna_hwctx {
|
||||
u32 *col_list;
|
||||
u32 start_col;
|
||||
u32 num_col;
|
||||
u32 num_unused_col;
|
||||
#define HWCTX_STAT_INIT 0
|
||||
#define HWCTX_STAT_READY 1
|
||||
#define HWCTX_STAT_STOP 2
|
||||
|
||||
@@ -191,26 +191,34 @@ mailbox_send_msg(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
|
||||
u32 head, tail;
|
||||
u32 start_addr;
|
||||
u32 tmp_tail;
|
||||
int ret;
|
||||
|
||||
head = mailbox_get_headptr(mb_chann, CHAN_RES_X2I);
|
||||
tail = mb_chann->x2i_tail;
|
||||
ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I);
|
||||
ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I) - sizeof(u32);
|
||||
start_addr = mb_chann->res[CHAN_RES_X2I].rb_start_addr;
|
||||
tmp_tail = tail + mb_msg->pkg_size;
|
||||
|
||||
if (tail < head && tmp_tail >= head)
|
||||
goto no_space;
|
||||
|
||||
if (tail >= head && (tmp_tail > ringbuf_size - sizeof(u32) &&
|
||||
mb_msg->pkg_size >= head))
|
||||
goto no_space;
|
||||
|
||||
if (tail >= head && tmp_tail > ringbuf_size - sizeof(u32)) {
|
||||
check_again:
|
||||
if (tail >= head && tmp_tail > ringbuf_size) {
|
||||
write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
|
||||
writel(TOMBSTONE, write_addr);
|
||||
|
||||
/* tombstone is set. Write from the start of the ringbuf */
|
||||
tail = 0;
|
||||
tmp_tail = tail + mb_msg->pkg_size;
|
||||
}
|
||||
|
||||
if (tail < head && tmp_tail >= head) {
|
||||
ret = read_poll_timeout(mailbox_get_headptr, head,
|
||||
tmp_tail < head || tail >= head,
|
||||
1, 100, false, mb_chann, CHAN_RES_X2I);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tail >= head)
|
||||
goto check_again;
|
||||
}
|
||||
|
||||
write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
|
||||
@@ -222,9 +230,6 @@ mailbox_send_msg(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
|
||||
mb_msg->pkg.header.id);
|
||||
|
||||
return 0;
|
||||
|
||||
no_space:
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -51,7 +51,6 @@ MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
static const struct amdxdna_device_id amdxdna_ids[] = {
|
||||
{ 0x1502, 0x0, &dev_npu1_info },
|
||||
{ 0x17f0, 0x0, &dev_npu2_info },
|
||||
{ 0x17f0, 0x10, &dev_npu4_info },
|
||||
{ 0x17f0, 0x11, &dev_npu5_info },
|
||||
{ 0x17f0, 0x20, &dev_npu6_info },
|
||||
@@ -105,43 +104,38 @@ failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp)
|
||||
static void amdxdna_client_cleanup(struct amdxdna_client *client)
|
||||
{
|
||||
struct amdxdna_client *client = filp->driver_priv;
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(ddev);
|
||||
|
||||
XDNA_DBG(xdna, "closing pid %d", client->pid);
|
||||
|
||||
list_del(&client->node);
|
||||
amdxdna_hwctx_remove_all(client);
|
||||
xa_destroy(&client->hwctx_xa);
|
||||
cleanup_srcu_struct(&client->hwctx_srcu);
|
||||
mutex_destroy(&client->mm_lock);
|
||||
|
||||
if (client->dev_heap)
|
||||
drm_gem_object_put(to_gobj(client->dev_heap));
|
||||
|
||||
iommu_sva_unbind_device(client->sva);
|
||||
|
||||
XDNA_DBG(xdna, "pid %d closed", client->pid);
|
||||
kfree(client);
|
||||
}
|
||||
|
||||
static int amdxdna_flush(struct file *f, fl_owner_t id)
|
||||
static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp)
|
||||
{
|
||||
struct drm_file *filp = f->private_data;
|
||||
struct amdxdna_client *client = filp->driver_priv;
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(ddev);
|
||||
int idx;
|
||||
|
||||
XDNA_DBG(xdna, "PID %d flushing...", client->pid);
|
||||
XDNA_DBG(xdna, "closing pid %d", client->pid);
|
||||
|
||||
if (!drm_dev_enter(&xdna->ddev, &idx))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
mutex_lock(&xdna->dev_lock);
|
||||
list_del_init(&client->node);
|
||||
amdxdna_hwctx_remove_all(client);
|
||||
amdxdna_client_cleanup(client);
|
||||
mutex_unlock(&xdna->dev_lock);
|
||||
|
||||
drm_dev_exit(idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdxdna_drm_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
|
||||
@@ -217,7 +211,6 @@ static const struct file_operations amdxdna_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = accel_open,
|
||||
.release = drm_release,
|
||||
.flush = amdxdna_flush,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.poll = drm_poll,
|
||||
@@ -333,8 +326,7 @@ static void amdxdna_remove(struct pci_dev *pdev)
|
||||
client = list_first_entry_or_null(&xdna->client_list,
|
||||
struct amdxdna_client, node);
|
||||
while (client) {
|
||||
list_del_init(&client->node);
|
||||
amdxdna_hwctx_remove_all(client);
|
||||
amdxdna_client_cleanup(client);
|
||||
|
||||
client = list_first_entry_or_null(&xdna->client_list,
|
||||
struct amdxdna_client, node);
|
||||
|
||||
@@ -137,7 +137,6 @@ struct amdxdna_client {
|
||||
|
||||
/* Add device info below */
|
||||
extern const struct amdxdna_dev_info dev_npu1_info;
|
||||
extern const struct amdxdna_dev_info dev_npu2_info;
|
||||
extern const struct amdxdna_dev_info dev_npu4_info;
|
||||
extern const struct amdxdna_dev_info dev_npu5_info;
|
||||
extern const struct amdxdna_dev_info dev_npu6_info;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <drm/amdxdna_accel.h>
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/gpu_scheduler.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#include "aie2_pci.h"
|
||||
#include "amdxdna_mailbox.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
/* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */
|
||||
#define MPNPU_PWAITMODE 0x301003C
|
||||
#define MPNPU_PUB_SEC_INTR 0x3010060
|
||||
#define MPNPU_PUB_PWRMGMT_INTR 0x3010064
|
||||
#define MPNPU_PUB_SCRATCH0 0x301006C
|
||||
#define MPNPU_PUB_SCRATCH1 0x3010070
|
||||
#define MPNPU_PUB_SCRATCH2 0x3010074
|
||||
#define MPNPU_PUB_SCRATCH3 0x3010078
|
||||
#define MPNPU_PUB_SCRATCH4 0x301007C
|
||||
#define MPNPU_PUB_SCRATCH5 0x3010080
|
||||
#define MPNPU_PUB_SCRATCH6 0x3010084
|
||||
#define MPNPU_PUB_SCRATCH7 0x3010088
|
||||
#define MPNPU_PUB_SCRATCH8 0x301008C
|
||||
#define MPNPU_PUB_SCRATCH9 0x3010090
|
||||
#define MPNPU_PUB_SCRATCH10 0x3010094
|
||||
#define MPNPU_PUB_SCRATCH11 0x3010098
|
||||
#define MPNPU_PUB_SCRATCH12 0x301009C
|
||||
#define MPNPU_PUB_SCRATCH13 0x30100A0
|
||||
#define MPNPU_PUB_SCRATCH14 0x30100A4
|
||||
#define MPNPU_PUB_SCRATCH15 0x30100A8
|
||||
#define MP0_C2PMSG_73 0x3810A24
|
||||
#define MP0_C2PMSG_123 0x3810AEC
|
||||
|
||||
#define MP1_C2PMSG_0 0x3B10900
|
||||
#define MP1_C2PMSG_60 0x3B109F0
|
||||
#define MP1_C2PMSG_61 0x3B109F4
|
||||
|
||||
#define MPNPU_SRAM_X2I_MAILBOX_0 0x3600000
|
||||
#define MPNPU_SRAM_X2I_MAILBOX_15 0x361E000
|
||||
#define MPNPU_SRAM_X2I_MAILBOX_31 0x363E000
|
||||
#define MPNPU_SRAM_I2X_MAILBOX_31 0x363F000
|
||||
|
||||
#define MMNPU_APERTURE0_BASE 0x3000000
|
||||
#define MMNPU_APERTURE1_BASE 0x3600000
|
||||
#define MMNPU_APERTURE3_BASE 0x3810000
|
||||
#define MMNPU_APERTURE4_BASE 0x3B10000
|
||||
|
||||
/* PCIe BAR Index for NPU2 */
|
||||
#define NPU2_REG_BAR_INDEX 0
|
||||
#define NPU2_MBOX_BAR_INDEX 0
|
||||
#define NPU2_PSP_BAR_INDEX 4
|
||||
#define NPU2_SMU_BAR_INDEX 5
|
||||
#define NPU2_SRAM_BAR_INDEX 2
|
||||
/* Associated BARs and Apertures */
|
||||
#define NPU2_REG_BAR_BASE MMNPU_APERTURE0_BASE
|
||||
#define NPU2_MBOX_BAR_BASE MMNPU_APERTURE0_BASE
|
||||
#define NPU2_PSP_BAR_BASE MMNPU_APERTURE3_BASE
|
||||
#define NPU2_SMU_BAR_BASE MMNPU_APERTURE4_BASE
|
||||
#define NPU2_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
|
||||
|
||||
static const struct amdxdna_dev_priv npu2_dev_priv = {
|
||||
.fw_path = "amdnpu/17f0_00/npu.sbin",
|
||||
.protocol_major = 0x6,
|
||||
.protocol_minor = 0x6,
|
||||
.rt_config = npu4_default_rt_cfg,
|
||||
.dpm_clk_tbl = npu4_dpm_clk_table,
|
||||
.fw_feature_tbl = npu4_fw_feature_table,
|
||||
.col_align = COL_ALIGN_NATURE,
|
||||
.mbox_dev_addr = NPU2_MBOX_BAR_BASE,
|
||||
.mbox_size = 0, /* Use BAR size */
|
||||
.sram_dev_addr = NPU2_SRAM_BAR_BASE,
|
||||
.hwctx_limit = 16,
|
||||
.sram_offs = {
|
||||
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
|
||||
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
|
||||
},
|
||||
.psp_regs_off = {
|
||||
DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU2_PSP, MP0_C2PMSG_123),
|
||||
DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU2_REG, MPNPU_PUB_SCRATCH3),
|
||||
DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU2_REG, MPNPU_PUB_SCRATCH4),
|
||||
DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU2_REG, MPNPU_PUB_SCRATCH9),
|
||||
DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU2_PSP, MP0_C2PMSG_73),
|
||||
DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU2_PSP, MP0_C2PMSG_123),
|
||||
DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU2_REG, MPNPU_PUB_SCRATCH3),
|
||||
DEFINE_BAR_OFFSET(PSP_PWAITMODE_REG, NPU2_REG, MPNPU_PWAITMODE),
|
||||
},
|
||||
.smu_regs_off = {
|
||||
DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU2_SMU, MP1_C2PMSG_0),
|
||||
DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU2_SMU, MP1_C2PMSG_60),
|
||||
DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU2_SMU, MMNPU_APERTURE4_BASE),
|
||||
DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU2_SMU, MP1_C2PMSG_61),
|
||||
DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU2_SMU, MP1_C2PMSG_60),
|
||||
},
|
||||
.hw_ops = {
|
||||
.set_dpm = npu4_set_dpm,
|
||||
},
|
||||
};
|
||||
|
||||
const struct amdxdna_dev_info dev_npu2_info = {
|
||||
.reg_bar = NPU2_REG_BAR_INDEX,
|
||||
.mbox_bar = NPU2_MBOX_BAR_INDEX,
|
||||
.sram_bar = NPU2_SRAM_BAR_INDEX,
|
||||
.psp_bar = NPU2_PSP_BAR_INDEX,
|
||||
.smu_bar = NPU2_SMU_BAR_INDEX,
|
||||
.first_col = 0,
|
||||
.dev_mem_buf_shift = 15, /* 32 KiB aligned */
|
||||
.dev_mem_base = AIE2_DEVM_BASE,
|
||||
.dev_mem_size = AIE2_DEVM_SIZE,
|
||||
.vbnv = "RyzenAI-npu2",
|
||||
.device_type = AMDXDNA_DEV_TYPE_KMQ,
|
||||
.dev_priv = &npu2_dev_priv,
|
||||
.ops = &aie2_ops, /* NPU2 can share NPU1's callback */
|
||||
};
|
||||
@@ -90,6 +90,7 @@ const struct dpm_clk_freq npu4_dpm_clk_table[] = {
|
||||
const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = {
|
||||
{ .feature = AIE2_NPU_COMMAND, .min_minor = 15 },
|
||||
{ .feature = AIE2_PREEMPT, .min_minor = 12 },
|
||||
{ .feature = AIE2_TEMPORAL_ONLY, .min_minor = 12 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ int __must_check ivpu_bo_bind(struct ivpu_bo *bo)
|
||||
|
||||
if (!bo->mmu_mapped) {
|
||||
drm_WARN_ON(&vdev->drm, !bo->ctx);
|
||||
ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt,
|
||||
ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt, ivpu_bo_size(bo),
|
||||
ivpu_bo_is_snooped(bo), ivpu_bo_is_read_only(bo));
|
||||
if (ret) {
|
||||
ivpu_err(vdev, "Failed to map BO in MMU: %d\n", ret);
|
||||
|
||||
@@ -429,11 +429,12 @@ static void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_a
|
||||
}
|
||||
|
||||
int
|
||||
ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
||||
u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only)
|
||||
ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr,
|
||||
struct sg_table *sgt, size_t bo_size, bool llc_coherent, bool read_only)
|
||||
{
|
||||
size_t start_vpu_addr = vpu_addr;
|
||||
struct scatterlist *sg;
|
||||
size_t sgt_size = 0;
|
||||
int ret;
|
||||
u64 prot;
|
||||
u64 i;
|
||||
@@ -462,12 +463,25 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
||||
ivpu_dbg(vdev, MMU_MAP, "Map ctx: %u dma_addr: 0x%llx vpu_addr: 0x%llx size: %lu\n",
|
||||
ctx->id, dma_addr, vpu_addr, size);
|
||||
|
||||
if (sgt_size + size > bo_size) {
|
||||
ivpu_err(vdev, "Scatter-gather table size exceeds buffer object size\n");
|
||||
ret = -EINVAL;
|
||||
goto err_unmap_pages;
|
||||
}
|
||||
|
||||
ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot);
|
||||
if (ret) {
|
||||
ivpu_err(vdev, "Failed to map context pages\n");
|
||||
goto err_unmap_pages;
|
||||
}
|
||||
vpu_addr += size;
|
||||
sgt_size += size;
|
||||
}
|
||||
|
||||
if (sgt_size < bo_size) {
|
||||
ivpu_err(vdev, "Scatter-gather table size too small to cover buffer object size\n");
|
||||
ret = -EINVAL;
|
||||
goto err_unmap_pages;
|
||||
}
|
||||
|
||||
if (!ctx->is_cd_valid) {
|
||||
@@ -493,7 +507,7 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
||||
return 0;
|
||||
|
||||
err_unmap_pages:
|
||||
ivpu_mmu_context_unmap_pages(ctx, start_vpu_addr, vpu_addr - start_vpu_addr);
|
||||
ivpu_mmu_context_unmap_pages(ctx, start_vpu_addr, sgt_size);
|
||||
mutex_unlock(&ctx->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,9 @@ int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu
|
||||
u64 size, struct drm_mm_node *node);
|
||||
void ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node);
|
||||
|
||||
int ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
||||
u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only);
|
||||
int
|
||||
ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr,
|
||||
struct sg_table *sgt, size_t bo_size, bool llc_coherent, bool read_only);
|
||||
void ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
||||
u64 vpu_addr, struct sg_table *sgt);
|
||||
int ivpu_mmu_context_set_pages_ro(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
||||
|
||||
@@ -35,6 +35,28 @@
|
||||
|
||||
#include "dma-buf-sysfs-stats.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/dma_buf.h>
|
||||
|
||||
/*
|
||||
* dmabuf->name must be accessed with holding dmabuf->name_lock.
|
||||
* we need to take the lock around the tracepoint call itself where
|
||||
* it is called in the code.
|
||||
*
|
||||
* Note: FUNC##_enabled() is a static branch that will only
|
||||
* be set when the trace event is enabled.
|
||||
*/
|
||||
#define DMA_BUF_TRACE(FUNC, ...) \
|
||||
do { \
|
||||
if (FUNC##_enabled()) { \
|
||||
guard(spinlock)(&dmabuf->name_lock); \
|
||||
FUNC(__VA_ARGS__); \
|
||||
} else if (IS_ENABLED(CONFIG_LOCKDEP)) { \
|
||||
/* Expose this lock when lockdep is enabled */ \
|
||||
guard(spinlock)(&dmabuf->name_lock); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Wrapper to hide the sg_table page link from the importer */
|
||||
struct dma_buf_sg_table_wrapper {
|
||||
struct sg_table *original;
|
||||
@@ -226,6 +248,8 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
|
||||
dmabuf->size >> PAGE_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
DMA_BUF_TRACE(trace_dma_buf_mmap_internal, dmabuf);
|
||||
|
||||
return dmabuf->ops->mmap(dmabuf, vma);
|
||||
}
|
||||
|
||||
@@ -751,6 +775,8 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
|
||||
|
||||
__dma_buf_list_add(dmabuf);
|
||||
|
||||
DMA_BUF_TRACE(trace_dma_buf_export, dmabuf);
|
||||
|
||||
return dmabuf;
|
||||
|
||||
err_dmabuf:
|
||||
@@ -774,10 +800,16 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_export, "DMA_BUF");
|
||||
*/
|
||||
int dma_buf_fd(struct dma_buf *dmabuf, int flags)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (!dmabuf || !dmabuf->file)
|
||||
return -EINVAL;
|
||||
|
||||
return FD_ADD(flags, dmabuf->file);
|
||||
fd = FD_ADD(flags, dmabuf->file);
|
||||
if (fd >= 0)
|
||||
DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd);
|
||||
|
||||
return fd;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");
|
||||
|
||||
@@ -792,6 +824,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");
|
||||
struct dma_buf *dma_buf_get(int fd)
|
||||
{
|
||||
struct file *file;
|
||||
struct dma_buf *dmabuf;
|
||||
|
||||
file = fget(fd);
|
||||
|
||||
@@ -803,7 +836,11 @@ struct dma_buf *dma_buf_get(int fd)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return file->private_data;
|
||||
dmabuf = file->private_data;
|
||||
|
||||
DMA_BUF_TRACE(trace_dma_buf_get, dmabuf, fd);
|
||||
|
||||
return dmabuf;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_get, "DMA_BUF");
|
||||
|
||||
@@ -823,6 +860,8 @@ void dma_buf_put(struct dma_buf *dmabuf)
|
||||
return;
|
||||
|
||||
fput(dmabuf->file);
|
||||
|
||||
DMA_BUF_TRACE(trace_dma_buf_put, dmabuf);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_put, "DMA_BUF");
|
||||
|
||||
@@ -1015,6 +1054,9 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev,
|
||||
list_add(&attach->node, &dmabuf->attachments);
|
||||
dma_resv_unlock(dmabuf->resv);
|
||||
|
||||
DMA_BUF_TRACE(trace_dma_buf_dynamic_attach, dmabuf, attach,
|
||||
dma_buf_attachment_is_dynamic(attach), dev);
|
||||
|
||||
return attach;
|
||||
|
||||
err_attach:
|
||||
@@ -1059,6 +1101,9 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
|
||||
if (dmabuf->ops->detach)
|
||||
dmabuf->ops->detach(dmabuf, attach);
|
||||
|
||||
DMA_BUF_TRACE(trace_dma_buf_detach, dmabuf, attach,
|
||||
dma_buf_attachment_is_dynamic(attach), attach->dev);
|
||||
|
||||
kfree(attach);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_detach, "DMA_BUF");
|
||||
@@ -1525,6 +1570,8 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
|
||||
vma_set_file(vma, dmabuf->file);
|
||||
vma->vm_pgoff = pgoff;
|
||||
|
||||
DMA_BUF_TRACE(trace_dma_buf_mmap, dmabuf);
|
||||
|
||||
return dmabuf->ops->mmap(dmabuf, vma);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dma_buf_mmap, "DMA_BUF");
|
||||
|
||||
@@ -33,50 +33,9 @@ static void mock_fence_release(struct dma_fence *f)
|
||||
kmem_cache_free(slab_fences, to_mock_fence(f));
|
||||
}
|
||||
|
||||
struct wait_cb {
|
||||
struct dma_fence_cb cb;
|
||||
struct task_struct *task;
|
||||
};
|
||||
|
||||
static void mock_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
|
||||
{
|
||||
wake_up_process(container_of(cb, struct wait_cb, cb)->task);
|
||||
}
|
||||
|
||||
static long mock_wait(struct dma_fence *f, bool intr, long timeout)
|
||||
{
|
||||
const int state = intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
|
||||
struct wait_cb cb = { .task = current };
|
||||
|
||||
if (dma_fence_add_callback(f, &cb.cb, mock_wakeup))
|
||||
return timeout;
|
||||
|
||||
while (timeout) {
|
||||
set_current_state(state);
|
||||
|
||||
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags))
|
||||
break;
|
||||
|
||||
if (signal_pending_state(state, current))
|
||||
break;
|
||||
|
||||
timeout = schedule_timeout(timeout);
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
|
||||
if (!dma_fence_remove_callback(f, &cb.cb))
|
||||
return timeout;
|
||||
|
||||
if (signal_pending_state(state, current))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
static const struct dma_fence_ops mock_ops = {
|
||||
.get_driver_name = mock_name,
|
||||
.get_timeline_name = mock_name,
|
||||
.wait = mock_wait,
|
||||
.release = mock_fence_release,
|
||||
};
|
||||
|
||||
@@ -349,14 +308,14 @@ static int test_wait(void *arg)
|
||||
|
||||
dma_fence_enable_sw_signaling(f);
|
||||
|
||||
if (dma_fence_wait_timeout(f, false, 0) != -ETIME) {
|
||||
if (dma_fence_wait_timeout(f, false, 0) != 0) {
|
||||
pr_err("Wait reported complete before being signaled\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
dma_fence_signal(f);
|
||||
|
||||
if (dma_fence_wait_timeout(f, false, 0) != 0) {
|
||||
if (dma_fence_wait_timeout(f, false, 0) != 1) {
|
||||
pr_err("Wait reported incomplete after being signaled\n");
|
||||
goto err_free;
|
||||
}
|
||||
@@ -393,16 +352,16 @@ static int test_wait_timeout(void *arg)
|
||||
|
||||
dma_fence_enable_sw_signaling(wt.f);
|
||||
|
||||
if (dma_fence_wait_timeout(wt.f, false, 1) != -ETIME) {
|
||||
if (dma_fence_wait_timeout(wt.f, false, 1) != 0) {
|
||||
pr_err("Wait reported complete before being signaled\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
mod_timer(&wt.timer, jiffies + 1);
|
||||
|
||||
if (dma_fence_wait_timeout(wt.f, false, 2) == -ETIME) {
|
||||
if (dma_fence_wait_timeout(wt.f, false, HZ) == 0) {
|
||||
if (timer_pending(&wt.timer)) {
|
||||
pr_notice("Timer did not fire within the jiffy!\n");
|
||||
pr_notice("Timer did not fire within one HZ!\n");
|
||||
err = 0; /* not our fault! */
|
||||
} else {
|
||||
pr_err("Wait reported incomplete after timeout\n");
|
||||
|
||||
@@ -94,9 +94,12 @@ err_release:
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_init - Initialize an allocated object.
|
||||
* drm_gem_shmem_init - Initialize an allocated object of the given size
|
||||
* @dev: DRM device
|
||||
* @obj: The allocated shmem GEM object.
|
||||
* @shmem: shmem GEM object to initialize
|
||||
* @size: Size of the object to initialize
|
||||
*
|
||||
* This function initializes an allocated shmem GEM object.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, or a negative error code on failure.
|
||||
|
||||
@@ -250,14 +250,14 @@ struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
|
||||
{
|
||||
struct drm_syncobj *syncobj;
|
||||
|
||||
spin_lock(&file_private->syncobj_table_lock);
|
||||
xa_lock(&file_private->syncobj_xa);
|
||||
|
||||
/* Check if we currently have a reference on the object */
|
||||
syncobj = idr_find(&file_private->syncobj_idr, handle);
|
||||
syncobj = xa_load(&file_private->syncobj_xa, handle);
|
||||
if (syncobj)
|
||||
drm_syncobj_get(syncobj);
|
||||
|
||||
spin_unlock(&file_private->syncobj_table_lock);
|
||||
xa_unlock(&file_private->syncobj_xa);
|
||||
|
||||
return syncobj;
|
||||
}
|
||||
@@ -598,23 +598,15 @@ int drm_syncobj_get_handle(struct drm_file *file_private,
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* take a reference to put in the idr */
|
||||
/* take a reference to put in the xarray */
|
||||
drm_syncobj_get(syncobj);
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock(&file_private->syncobj_table_lock);
|
||||
ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
|
||||
spin_unlock(&file_private->syncobj_table_lock);
|
||||
|
||||
idr_preload_end();
|
||||
|
||||
if (ret < 0) {
|
||||
ret = xa_alloc(&file_private->syncobj_xa, handle, syncobj, xa_limit_32b,
|
||||
GFP_NOWAIT);
|
||||
if (ret)
|
||||
drm_syncobj_put(syncobj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*handle = ret;
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_syncobj_get_handle);
|
||||
|
||||
@@ -638,10 +630,7 @@ static int drm_syncobj_destroy(struct drm_file *file_private,
|
||||
{
|
||||
struct drm_syncobj *syncobj;
|
||||
|
||||
spin_lock(&file_private->syncobj_table_lock);
|
||||
syncobj = idr_remove(&file_private->syncobj_idr, handle);
|
||||
spin_unlock(&file_private->syncobj_table_lock);
|
||||
|
||||
syncobj = xa_erase(&file_private->syncobj_xa, handle);
|
||||
if (!syncobj)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -722,20 +711,13 @@ static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
|
||||
if (fd_file(f)->f_op != &drm_syncobj_file_fops)
|
||||
return -EINVAL;
|
||||
|
||||
/* take a reference to put in the idr */
|
||||
/* take a reference to put in the xarray */
|
||||
syncobj = fd_file(f)->private_data;
|
||||
drm_syncobj_get(syncobj);
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock(&file_private->syncobj_table_lock);
|
||||
ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
|
||||
spin_unlock(&file_private->syncobj_table_lock);
|
||||
idr_preload_end();
|
||||
|
||||
if (ret > 0) {
|
||||
*handle = ret;
|
||||
ret = 0;
|
||||
} else
|
||||
ret = xa_alloc(&file_private->syncobj_xa, handle, syncobj, xa_limit_32b,
|
||||
GFP_NOWAIT);
|
||||
if (ret)
|
||||
drm_syncobj_put(syncobj);
|
||||
|
||||
return ret;
|
||||
@@ -814,17 +796,7 @@ err_put_fd:
|
||||
void
|
||||
drm_syncobj_open(struct drm_file *file_private)
|
||||
{
|
||||
idr_init_base(&file_private->syncobj_idr, 1);
|
||||
spin_lock_init(&file_private->syncobj_table_lock);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_syncobj_release_handle(int id, void *ptr, void *data)
|
||||
{
|
||||
struct drm_syncobj *syncobj = ptr;
|
||||
|
||||
drm_syncobj_put(syncobj);
|
||||
return 0;
|
||||
xa_init_flags(&file_private->syncobj_xa, XA_FLAGS_ALLOC1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -838,9 +810,12 @@ drm_syncobj_release_handle(int id, void *ptr, void *data)
|
||||
void
|
||||
drm_syncobj_release(struct drm_file *file_private)
|
||||
{
|
||||
idr_for_each(&file_private->syncobj_idr,
|
||||
&drm_syncobj_release_handle, file_private);
|
||||
idr_destroy(&file_private->syncobj_idr);
|
||||
struct drm_syncobj *syncobj;
|
||||
unsigned long handle;
|
||||
|
||||
xa_for_each(&file_private->syncobj_xa, handle, syncobj)
|
||||
drm_syncobj_put(syncobj);
|
||||
xa_destroy(&file_private->syncobj_xa);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -781,6 +781,19 @@ config DRM_PANEL_SAMSUNG_LD9040
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select VIDEOMODE_HELPERS
|
||||
|
||||
config DRM_PANEL_SAMSUNG_LTL106HL02
|
||||
tristate "Samsung LTL106HL02 panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
select VIDEOMODE_HELPERS
|
||||
help
|
||||
Say Y here if you want to enable support for the Samsung LTL106HL02
|
||||
panel driver which is used in Microsoft Surface 2.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called panel-samsung-ltl106hl02.
|
||||
|
||||
config DRM_PANEL_SAMSUNG_S6E3FA7
|
||||
tristate "Samsung S6E3FA7 panel driver"
|
||||
depends on OF
|
||||
|
||||
@@ -76,6 +76,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS639RQ08) += panel-samsung-ams639rq08.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LTL106HL02) += panel-samsung-ltl106hl02.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D27A1) += panel-samsung-s6d27a1.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D7AA0) += panel-samsung-s6d7aa0.o
|
||||
|
||||
@@ -20,13 +20,18 @@
|
||||
#include <drm/display/drm_dsc.h>
|
||||
#include <drm/display/drm_dsc_helper.h>
|
||||
|
||||
#define NUM_SUPPLIES 2
|
||||
static const struct regulator_bulk_data sw43408_supplies[] = {
|
||||
{ .supply = "vddi", /* 1.8 V */
|
||||
.init_load_uA = 62000 },
|
||||
{ .supply = "vpnl", /* 3.0 V */
|
||||
.init_load_uA = 857000 },
|
||||
};
|
||||
|
||||
struct sw43408_panel {
|
||||
struct drm_panel base;
|
||||
struct mipi_dsi_device *link;
|
||||
|
||||
struct regulator_bulk_data supplies[NUM_SUPPLIES];
|
||||
struct regulator_bulk_data *supplies;
|
||||
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
||||
@@ -52,7 +57,7 @@ static int sw43408_unprepare(struct drm_panel *panel)
|
||||
|
||||
gpiod_set_value(sw43408->reset_gpio, 1);
|
||||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(sw43408->supplies), sw43408->supplies);
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(sw43408_supplies), sw43408->supplies);
|
||||
|
||||
return ret ? : ctx.accum_err;
|
||||
}
|
||||
@@ -119,23 +124,28 @@ static int sw43408_program(struct drm_panel *panel)
|
||||
return ctx.accum_err;
|
||||
}
|
||||
|
||||
static int sw43408_prepare(struct drm_panel *panel)
|
||||
static void sw43408_reset(struct sw43408_panel *ctx)
|
||||
{
|
||||
struct sw43408_panel *ctx = to_panel_info(panel);
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
gpiod_set_value(ctx->reset_gpio, 0);
|
||||
usleep_range(9000, 10000);
|
||||
gpiod_set_value(ctx->reset_gpio, 1);
|
||||
usleep_range(1000, 2000);
|
||||
gpiod_set_value(ctx->reset_gpio, 0);
|
||||
usleep_range(9000, 10000);
|
||||
}
|
||||
|
||||
static int sw43408_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct sw43408_panel *ctx = to_panel_info(panel);
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(sw43408_supplies), ctx->supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
sw43408_reset(ctx);
|
||||
|
||||
ret = sw43408_program(panel);
|
||||
if (ret)
|
||||
@@ -145,11 +155,11 @@ static int sw43408_prepare(struct drm_panel *panel)
|
||||
|
||||
poweroff:
|
||||
gpiod_set_value(ctx->reset_gpio, 1);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
regulator_bulk_disable(ARRAY_SIZE(sw43408_supplies), ctx->supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode sw43408_mode = {
|
||||
static const struct drm_display_mode lh546wf1_ed01_mode = {
|
||||
.clock = (1080 + 20 + 32 + 20) * (2160 + 20 + 4 + 20) * 60 / 1000,
|
||||
|
||||
.hdisplay = 1080,
|
||||
@@ -171,7 +181,7 @@ static const struct drm_display_mode sw43408_mode = {
|
||||
static int sw43408_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
return drm_connector_helper_get_modes_fixed(connector, &sw43408_mode);
|
||||
return drm_connector_helper_get_modes_fixed(connector, &lh546wf1_ed01_mode);
|
||||
}
|
||||
|
||||
static int sw43408_backlight_update_status(struct backlight_device *bl)
|
||||
@@ -214,7 +224,8 @@ static const struct drm_panel_funcs sw43408_funcs = {
|
||||
};
|
||||
|
||||
static const struct of_device_id sw43408_of_match[] = {
|
||||
{ .compatible = "lg,sw43408", },
|
||||
{ .compatible = "lg,sw43408", }, /* legacy */
|
||||
{ .compatible = "lg,sw43408-lh546wf1-ed01", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sw43408_of_match);
|
||||
@@ -224,20 +235,17 @@ static int sw43408_add(struct sw43408_panel *ctx)
|
||||
struct device *dev = &ctx->link->dev;
|
||||
int ret;
|
||||
|
||||
ctx->supplies[0].supply = "vddi"; /* 1.88 V */
|
||||
ctx->supplies[0].init_load_uA = 62000;
|
||||
ctx->supplies[1].supply = "vpnl"; /* 3.0 V */
|
||||
ctx->supplies[1].init_load_uA = 857000;
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
|
||||
ctx->supplies);
|
||||
ret = devm_regulator_bulk_get_const(dev,
|
||||
ARRAY_SIZE(sw43408_supplies),
|
||||
sw43408_supplies,
|
||||
&ctx->supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->reset_gpio)) {
|
||||
ret = PTR_ERR(ctx->reset_gpio);
|
||||
return dev_err_probe(dev, ret, "cannot get reset gpio\n");
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
|
||||
"Failed to get reset-gpios\n");
|
||||
}
|
||||
|
||||
ret = sw43408_backlight_init(ctx);
|
||||
@@ -294,10 +302,6 @@ static void sw43408_remove(struct mipi_dsi_device *dsi)
|
||||
struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
int ret;
|
||||
|
||||
ret = sw43408_unprepare(&ctx->base);
|
||||
if (ret < 0)
|
||||
dev_err(&dsi->dev, "failed to unprepare panel: %d\n", ret);
|
||||
|
||||
ret = mipi_dsi_detach(dsi);
|
||||
if (ret < 0)
|
||||
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
|
||||
|
||||
@@ -109,177 +109,140 @@ static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel)
|
||||
return container_of(panel, struct otm8009a, panel);
|
||||
}
|
||||
|
||||
static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data,
|
||||
size_t len)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
|
||||
if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0)
|
||||
dev_warn(ctx->dev, "mipi dsi dcs write buffer failed\n");
|
||||
}
|
||||
|
||||
#define dcs_write_seq(ctx, seq...) \
|
||||
({ \
|
||||
static const u8 d[] = { seq }; \
|
||||
otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \
|
||||
})
|
||||
|
||||
#define dcs_write_cmd_at(ctx, cmd, seq...) \
|
||||
({ \
|
||||
dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF); \
|
||||
dcs_write_seq(ctx, (cmd) >> 8, seq); \
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, MCS_ADRSFT, (cmd) & 0xFF); \
|
||||
mipi_dsi_dcs_write_seq_multi(ctx, (cmd) >> 8, seq); \
|
||||
})
|
||||
|
||||
static int otm8009a_init_sequence(struct otm8009a *ctx)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
int ret;
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
|
||||
|
||||
/* Enter CMD2 */
|
||||
dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01);
|
||||
|
||||
/* Enter Orise Command2 */
|
||||
dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_CMD2_ENA2, 0x80, 0x09);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30);
|
||||
mdelay(10);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_SD_PCH_CTRL, 0x30);
|
||||
mipi_dsi_msleep(&dsi_ctx, 10);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40);
|
||||
mdelay(10);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_NO_DOC1, 0x40);
|
||||
mipi_dsi_msleep(&dsi_ctx, 10);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34);
|
||||
dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50);
|
||||
dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E);
|
||||
dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33);
|
||||
dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79);
|
||||
dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B);
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83);
|
||||
dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83);
|
||||
dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E);
|
||||
dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL4 + 1, 0xA9);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 1, 0x34);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_P_DRV_M, 0x50);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_VCOMDC, 0x4E);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 2, 0x01);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 5, 0x34);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 4, 0x33);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_GVDDSET, 0x79, 0x79);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_SD_CTRL + 1, 0x1B);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL1 + 2, 0x83);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_SD_PCH_CTRL + 1, 0x83);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_RGB_VID_SET, 0x0E);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANSET, 0x00, 0x01);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00,
|
||||
0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00,
|
||||
0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00,
|
||||
0x01, 0x02, 0x00, 0x00);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_NO_DOC2, 0x00);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
|
||||
4, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25,
|
||||
0x00, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02);
|
||||
dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26,
|
||||
0x00, 0x00, 0x00, 0x00);
|
||||
dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01);
|
||||
dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL1 + 1, 0x66);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_NO_DOC3, 0x06);
|
||||
|
||||
dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
|
||||
0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
|
||||
0x01);
|
||||
dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10,
|
||||
0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A,
|
||||
0x01);
|
||||
|
||||
/* Exit CMD2 */
|
||||
dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
|
||||
dcs_write_cmd_at(&dsi_ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
ret = mipi_dsi_dcs_nop(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
mipi_dsi_dcs_nop_multi(&dsi_ctx);
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for sleep out exit */
|
||||
mdelay(120);
|
||||
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 120);
|
||||
|
||||
/* Default portrait 480x800 rgb24 */
|
||||
dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
|
||||
|
||||
ret = mipi_dsi_dcs_set_column_address(dsi, 0, OTM8009A_HDISPLAY - 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, OTM8009A_HDISPLAY - 1);
|
||||
|
||||
ret = mipi_dsi_dcs_set_page_address(dsi, 0, OTM8009A_VDISPLAY - 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, OTM8009A_VDISPLAY - 1);
|
||||
|
||||
/* See otm8009a driver documentation for pixel format descriptions */
|
||||
ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT |
|
||||
mipi_dsi_dcs_set_pixel_format_multi(&dsi_ctx, MIPI_DCS_PIXEL_FMT_24BIT |
|
||||
MIPI_DCS_PIXEL_FMT_24BIT << 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable CABC feature */
|
||||
dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_on(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
|
||||
|
||||
ret = mipi_dsi_dcs_nop(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
mipi_dsi_dcs_nop_multi(&dsi_ctx);
|
||||
|
||||
/* Send Command GRAM memory write (no parameters) */
|
||||
dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START);
|
||||
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_MEMORY_START);
|
||||
|
||||
/* Wait a short while to let the panel be ready before the 1st frame */
|
||||
mdelay(10);
|
||||
mipi_dsi_msleep(&dsi_ctx, 10);
|
||||
|
||||
return 0;
|
||||
return dsi_ctx.accum_err;
|
||||
}
|
||||
|
||||
static int otm8009a_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct otm8009a *ctx = panel_to_otm8009a(panel);
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
int ret;
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
|
||||
|
||||
backlight_disable(ctx->bl_dev);
|
||||
|
||||
ret = mipi_dsi_dcs_set_display_off(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
|
||||
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 120);
|
||||
|
||||
ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msleep(120);
|
||||
|
||||
return 0;
|
||||
return dsi_ctx.accum_err;
|
||||
}
|
||||
|
||||
static int otm8009a_unprepare(struct drm_panel *panel)
|
||||
@@ -383,6 +346,8 @@ static const struct drm_panel_funcs otm8009a_drm_funcs = {
|
||||
static int otm8009a_backlight_update_status(struct backlight_device *bd)
|
||||
{
|
||||
struct otm8009a *ctx = bl_get_data(bd);
|
||||
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
|
||||
u8 data[2];
|
||||
|
||||
if (!ctx->prepared) {
|
||||
@@ -397,7 +362,7 @@ static int otm8009a_backlight_update_status(struct backlight_device *bd)
|
||||
*/
|
||||
data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS;
|
||||
data[1] = bd->props.brightness;
|
||||
otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
|
||||
mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, data, ARRAY_SIZE(data));
|
||||
|
||||
/* set Brightness Control & Backlight on */
|
||||
data[1] = 0x24;
|
||||
@@ -409,9 +374,9 @@ static int otm8009a_backlight_update_status(struct backlight_device *bd)
|
||||
|
||||
/* Update Brightness Control & Backlight */
|
||||
data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY;
|
||||
otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data));
|
||||
mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, data, ARRAY_SIZE(data));
|
||||
|
||||
return 0;
|
||||
return dsi_ctx.accum_err;
|
||||
}
|
||||
|
||||
static const struct backlight_ops otm8009a_backlight_ops = {
|
||||
|
||||
179
drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c
Normal file
179
drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c
Normal file
@@ -0,0 +1,179 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
struct samsung_ltl106hl02 {
|
||||
struct drm_panel panel;
|
||||
struct mipi_dsi_device *dsi;
|
||||
|
||||
struct regulator *supply;
|
||||
struct gpio_desc *reset_gpio;
|
||||
};
|
||||
|
||||
static inline struct samsung_ltl106hl02 *to_samsung_ltl106hl02(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct samsung_ltl106hl02, panel);
|
||||
}
|
||||
|
||||
static void samsung_ltl106hl02_reset(struct samsung_ltl106hl02 *ctx)
|
||||
{
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
usleep_range(10000, 11000);
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
||||
usleep_range(2000, 3000);
|
||||
}
|
||||
|
||||
static int samsung_ltl106hl02_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct samsung_ltl106hl02 *ctx = to_samsung_ltl106hl02(panel);
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
|
||||
struct device *dev = &ctx->dsi->dev;
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(ctx->supply);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable power supply %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ctx->reset_gpio)
|
||||
samsung_ltl106hl02_reset(ctx);
|
||||
|
||||
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 70);
|
||||
|
||||
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 5);
|
||||
|
||||
return dsi_ctx.accum_err;
|
||||
}
|
||||
|
||||
static int samsung_ltl106hl02_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct samsung_ltl106hl02 *ctx = to_samsung_ltl106hl02(panel);
|
||||
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
|
||||
|
||||
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 50);
|
||||
mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
|
||||
mipi_dsi_msleep(&dsi_ctx, 150);
|
||||
|
||||
if (ctx->reset_gpio)
|
||||
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
||||
|
||||
regulator_disable(ctx->supply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_display_mode samsung_ltl106hl02_mode = {
|
||||
.clock = (1920 + 32 + 32 + 64) * (1080 + 6 + 3 + 22) * 60 / 1000,
|
||||
.hdisplay = 1920,
|
||||
.hsync_start = 1920 + 32,
|
||||
.hsync_end = 1920 + 32 + 32,
|
||||
.htotal = 1920 + 32 + 32 + 64,
|
||||
.vdisplay = 1080,
|
||||
.vsync_start = 1080 + 6,
|
||||
.vsync_end = 1080 + 6 + 3,
|
||||
.vtotal = 1080 + 6 + 3 + 22,
|
||||
.width_mm = 235,
|
||||
.height_mm = 132,
|
||||
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
|
||||
};
|
||||
|
||||
static int samsung_ltl106hl02_get_modes(struct drm_panel *panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
return drm_connector_helper_get_modes_fixed(connector, &samsung_ltl106hl02_mode);
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs samsung_ltl106hl02_panel_funcs = {
|
||||
.prepare = samsung_ltl106hl02_prepare,
|
||||
.unprepare = samsung_ltl106hl02_unprepare,
|
||||
.get_modes = samsung_ltl106hl02_get_modes,
|
||||
};
|
||||
|
||||
static int samsung_ltl106hl02_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct device *dev = &dsi->dev;
|
||||
struct samsung_ltl106hl02 *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_drm_panel_alloc(dev, struct samsung_ltl106hl02, panel,
|
||||
&samsung_ltl106hl02_panel_funcs,
|
||||
DRM_MODE_CONNECTOR_DSI);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
ctx->supply = devm_regulator_get(dev, "power");
|
||||
if (IS_ERR(ctx->supply))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->supply),
|
||||
"Failed to get power regulator\n");
|
||||
|
||||
ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->reset_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
|
||||
"Failed to get reset-gpios\n");
|
||||
|
||||
ctx->dsi = dsi;
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
|
||||
dsi->lanes = 4;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
|
||||
|
||||
ret = drm_panel_of_backlight(&ctx->panel);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get backlight\n");
|
||||
|
||||
drm_panel_add(&ctx->panel);
|
||||
|
||||
ret = devm_mipi_dsi_attach(dev, dsi);
|
||||
if (ret < 0) {
|
||||
drm_panel_remove(&ctx->panel);
|
||||
return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void samsung_ltl106hl02_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct samsung_ltl106hl02 *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
drm_panel_remove(&ctx->panel);
|
||||
}
|
||||
|
||||
static const struct of_device_id samsung_ltl106hl02_of_match[] = {
|
||||
{ .compatible = "samsung,ltl106hl02-001" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, samsung_ltl106hl02_of_match);
|
||||
|
||||
static struct mipi_dsi_driver samsung_ltl106hl02_driver = {
|
||||
.driver = {
|
||||
.name = "panel-samsung-ltl106hl02",
|
||||
.of_match_table = samsung_ltl106hl02_of_match,
|
||||
},
|
||||
.probe = samsung_ltl106hl02_probe,
|
||||
.remove = samsung_ltl106hl02_remove,
|
||||
};
|
||||
module_mipi_dsi_driver(samsung_ltl106hl02_driver);
|
||||
|
||||
MODULE_AUTHOR("Anton Bambura <jenneron@protonmail.com>");
|
||||
MODULE_DESCRIPTION("DRM driver for Samsung LTL106HL02 video mode DSI panel");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -2509,6 +2509,31 @@ static const struct panel_desc hannstar_hsd101pww2 = {
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct display_timing hannstar_hsd156juw2_timing = {
|
||||
.pixelclock = { 66000000, 72800000, 80500000 },
|
||||
.hactive = { 1920, 1920, 1920 },
|
||||
.hfront_porch = { 20, 30, 30 },
|
||||
.hback_porch = { 20, 30, 30 },
|
||||
.hsync_len = { 50, 60, 90 },
|
||||
.vactive = { 1080, 1080, 1080 },
|
||||
.vfront_porch = { 1, 2, 4 },
|
||||
.vback_porch = { 1, 2, 4 },
|
||||
.vsync_len = { 3, 40, 80 },
|
||||
.flags = DISPLAY_FLAGS_DE_HIGH,
|
||||
};
|
||||
|
||||
static const struct panel_desc hannstar_hsd156juw2 = {
|
||||
.timings = &hannstar_hsd156juw2_timing,
|
||||
.num_timings = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 344,
|
||||
.height = 194,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
|
||||
.connector_type = DRM_MODE_CONNECTOR_LVDS,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode hitachi_tx23d38vm0caa_mode = {
|
||||
.clock = 33333,
|
||||
.hdisplay = 800,
|
||||
@@ -5253,6 +5278,9 @@ static const struct of_device_id platform_of_match[] = {
|
||||
}, {
|
||||
.compatible = "hannstar,hsd101pww2",
|
||||
.data = &hannstar_hsd101pww2,
|
||||
}, {
|
||||
.compatible = "hannstar,hsd156juw2",
|
||||
.data = &hannstar_hsd156juw2,
|
||||
}, {
|
||||
.compatible = "hit,tx23d38vm0caa",
|
||||
.data = &hitachi_tx23d38vm0caa
|
||||
|
||||
@@ -923,8 +923,10 @@ static int panthor_ioctl_bo_create(struct drm_device *ddev, void *data,
|
||||
}
|
||||
|
||||
if ((args->flags & DRM_PANTHOR_BO_NO_MMAP) &&
|
||||
(args->flags & DRM_PANTHOR_BO_WB_MMAP))
|
||||
return -EINVAL;
|
||||
(args->flags & DRM_PANTHOR_BO_WB_MMAP)) {
|
||||
ret = -EINVAL;
|
||||
goto out_dev_exit;
|
||||
}
|
||||
|
||||
if (args->exclusive_vm_id) {
|
||||
vm = panthor_vm_pool_get_vm(pfile->vms, args->exclusive_vm_id);
|
||||
|
||||
@@ -1261,10 +1261,6 @@ void panthor_fw_unplug(struct panthor_device *ptdev)
|
||||
if (ptdev->fw->irq.irq)
|
||||
panthor_job_irq_suspend(&ptdev->fw->irq);
|
||||
|
||||
panthor_fw_halt_mcu(ptdev);
|
||||
if (!panthor_fw_wait_mcu_halted(ptdev))
|
||||
drm_warn(&ptdev->base, "Failed to halt MCU on unplug");
|
||||
|
||||
panthor_fw_stop(ptdev);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
#include "panthor_devfreq.h"
|
||||
#include "panthor_device.h"
|
||||
@@ -943,6 +944,9 @@ static void group_release_work(struct work_struct *work)
|
||||
release_work);
|
||||
u32 i;
|
||||
|
||||
/* dma-fences may still be accessing group->queues under rcu lock. */
|
||||
synchronize_rcu();
|
||||
|
||||
for (i = 0; i < group->queue_count; i++)
|
||||
group_free_queue(group, group->queues[i]);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/renesas.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
@@ -32,6 +33,8 @@
|
||||
|
||||
#include "rzg2l_mipi_dsi_regs.h"
|
||||
|
||||
MODULE_IMPORT_NS("RZV2H_CPG");
|
||||
|
||||
#define RZG2L_DCS_BUF_SIZE 128 /* Maximum DCS buffer size in external memory. */
|
||||
|
||||
#define RZ_MIPI_DSI_FEATURE_16BPP BIT(0)
|
||||
@@ -46,6 +49,11 @@ struct rzg2l_mipi_dsi_hw_info {
|
||||
u64 *hsfreq_millihz);
|
||||
unsigned int (*dphy_mode_clk_check)(struct rzg2l_mipi_dsi *dsi,
|
||||
unsigned long mode_freq);
|
||||
struct {
|
||||
const struct rzv2h_pll_limits **limits;
|
||||
const u8 *table;
|
||||
const u8 table_size;
|
||||
} cpg_plldsi;
|
||||
u32 phy_reg_offset;
|
||||
u32 link_reg_offset;
|
||||
unsigned long min_dclk;
|
||||
@@ -53,6 +61,11 @@ struct rzg2l_mipi_dsi_hw_info {
|
||||
u8 features;
|
||||
};
|
||||
|
||||
struct rzv2h_dsi_mode_calc {
|
||||
unsigned long mode_freq_khz;
|
||||
struct rzv2h_pll_pars dsi_parameters;
|
||||
};
|
||||
|
||||
struct rzg2l_mipi_dsi {
|
||||
struct device *dev;
|
||||
void __iomem *mmio;
|
||||
@@ -68,17 +81,29 @@ struct rzg2l_mipi_dsi {
|
||||
struct drm_bridge *next_bridge;
|
||||
|
||||
struct clk *vclk;
|
||||
struct clk *lpclk;
|
||||
|
||||
enum mipi_dsi_pixel_format format;
|
||||
unsigned int num_data_lanes;
|
||||
unsigned int lanes;
|
||||
unsigned long mode_flags;
|
||||
|
||||
struct rzv2h_dsi_mode_calc mode_calc;
|
||||
|
||||
/* DCS buffer pointers when using external memory. */
|
||||
dma_addr_t dcs_buf_phys;
|
||||
u8 *dcs_buf_virt;
|
||||
};
|
||||
|
||||
static const struct rzv2h_pll_limits rzv2h_plldsi_div_limits = {
|
||||
.fout = { .min = 80 * MEGA, .max = 1500 * MEGA },
|
||||
.fvco = { .min = 1050 * MEGA, .max = 2100 * MEGA },
|
||||
.m = { .min = 64, .max = 1023 },
|
||||
.p = { .min = 1, .max = 4 },
|
||||
.s = { .min = 0, .max = 5 },
|
||||
.k = { .min = -32768, .max = 32767 },
|
||||
};
|
||||
|
||||
static inline struct rzg2l_mipi_dsi *
|
||||
bridge_to_rzg2l_mipi_dsi(struct drm_bridge *bridge)
|
||||
{
|
||||
@@ -193,6 +218,237 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rzv2h_mipi_dsi_timings - Timing parameter table structure
|
||||
*
|
||||
* @hsfreq: Pointer to frequency threshold array
|
||||
* @len: Number of entries in the hsfreq array
|
||||
* @base_value: Base register value offset for this timing parameter
|
||||
*
|
||||
* Each timing parameter (TCLK*, THS*, etc.) has its own table with
|
||||
* frequency thresholds and corresponding base register values.
|
||||
*/
|
||||
struct rzv2h_mipi_dsi_timings {
|
||||
const u8 *hsfreq;
|
||||
u8 len;
|
||||
u8 base_value;
|
||||
};
|
||||
|
||||
/*
|
||||
* enum rzv2h_dsi_timing_idx - MIPI DSI timing parameter indices
|
||||
*
|
||||
* These enums correspond to different MIPI DSI PHY timing parameters.
|
||||
*/
|
||||
enum rzv2h_dsi_timing_idx {
|
||||
TCLKPRPRCTL,
|
||||
TCLKZEROCTL,
|
||||
TCLKPOSTCTL,
|
||||
TCLKTRAILCTL,
|
||||
THSPRPRCTL,
|
||||
THSZEROCTL,
|
||||
THSTRAILCTL,
|
||||
TLPXCTL,
|
||||
THSEXITCTL,
|
||||
};
|
||||
|
||||
/*
|
||||
* RZ/V2H(P) Frequency threshold lookup tables for D-PHY timing parameters
|
||||
*
|
||||
* - Each array contains frequency thresholds (in units of 10 Mbps),
|
||||
* taken directly from the table 9.5-4 hardware manual.
|
||||
* - These thresholds define the frequency ranges for which timing
|
||||
* register values must be programmed.
|
||||
* - The actual register value is calculated in
|
||||
* rzv2h_dphy_find_timings_val():
|
||||
*
|
||||
* register_value = timings->base_value + table_index
|
||||
*
|
||||
* Example (TCLKPRPRCTL, from HW manual):
|
||||
* 0-150 Mbps -> index 0 -> register_value = base + 0 = 0 + 0 = 0
|
||||
* 151-260 Mbps -> index 1 -> register_value = base + 1 = 0 + 1 = 1
|
||||
* 261-370 Mbps -> index 2 -> register_value = base + 2 = 0 + 2 = 2
|
||||
*
|
||||
* Each of the following arrays corresponds to a specific timing
|
||||
* parameter (TCLKPRPRCTL, TCLKZEROCTL, TCLKPOSTCTL, etc.).
|
||||
*/
|
||||
static const u8 tclkprprctl[] = {
|
||||
15, 26, 37, 47, 58, 69, 79, 90, 101, 111, 122, 133, 143, 150,
|
||||
};
|
||||
|
||||
static const u8 tclkzeroctl[] = {
|
||||
9, 11, 13, 15, 18, 21, 23, 24, 25, 27, 29, 31, 34, 36, 38,
|
||||
41, 43, 45, 47, 50, 52, 54, 57, 59, 61, 63, 66, 68, 70, 73,
|
||||
75, 77, 79, 82, 84, 86, 89, 91, 93, 95, 98, 100, 102, 105,
|
||||
107, 109, 111, 114, 116, 118, 121, 123, 125, 127, 130, 132,
|
||||
134, 137, 139, 141, 143, 146, 148, 150,
|
||||
};
|
||||
|
||||
static const u8 tclkpostctl[] = {
|
||||
8, 21, 34, 48, 61, 74, 88, 101, 114, 128, 141, 150,
|
||||
};
|
||||
|
||||
static const u8 tclktrailctl[] = {
|
||||
14, 25, 37, 48, 59, 71, 82, 94, 105, 117, 128, 139, 150,
|
||||
};
|
||||
|
||||
static const u8 thsprprctl[] = {
|
||||
11, 19, 29, 40, 50, 61, 72, 82, 93, 103, 114, 125, 135, 146, 150,
|
||||
};
|
||||
|
||||
static const u8 thszeroctl[] = {
|
||||
18, 24, 29, 35, 40, 46, 51, 57, 62, 68, 73, 79, 84, 90,
|
||||
95, 101, 106, 112, 117, 123, 128, 134, 139, 145, 150,
|
||||
};
|
||||
|
||||
static const u8 thstrailctl[] = {
|
||||
10, 21, 32, 42, 53, 64, 75, 85, 96, 107, 118, 128, 139, 150,
|
||||
};
|
||||
|
||||
static const u8 tlpxctl[] = {
|
||||
13, 26, 39, 53, 66, 79, 93, 106, 119, 133, 146, 150,
|
||||
};
|
||||
|
||||
static const u8 thsexitctl[] = {
|
||||
15, 23, 31, 39, 47, 55, 63, 71, 79, 87,
|
||||
95, 103, 111, 119, 127, 135, 143, 150,
|
||||
};
|
||||
|
||||
/*
|
||||
* rzv2h_dsi_timings_tables - main timing parameter lookup table
|
||||
* Maps timing parameter enum to its frequency table, array length and
|
||||
* base register offset value.
|
||||
*/
|
||||
static const struct rzv2h_mipi_dsi_timings rzv2h_dsi_timings_tables[] = {
|
||||
[TCLKPRPRCTL] = {
|
||||
.hsfreq = tclkprprctl,
|
||||
.len = ARRAY_SIZE(tclkprprctl),
|
||||
.base_value = 0,
|
||||
},
|
||||
[TCLKZEROCTL] = {
|
||||
.hsfreq = tclkzeroctl,
|
||||
.len = ARRAY_SIZE(tclkzeroctl),
|
||||
.base_value = 2,
|
||||
},
|
||||
[TCLKPOSTCTL] = {
|
||||
.hsfreq = tclkpostctl,
|
||||
.len = ARRAY_SIZE(tclkpostctl),
|
||||
.base_value = 6,
|
||||
},
|
||||
[TCLKTRAILCTL] = {
|
||||
.hsfreq = tclktrailctl,
|
||||
.len = ARRAY_SIZE(tclktrailctl),
|
||||
.base_value = 1,
|
||||
},
|
||||
[THSPRPRCTL] = {
|
||||
.hsfreq = thsprprctl,
|
||||
.len = ARRAY_SIZE(thsprprctl),
|
||||
.base_value = 0,
|
||||
},
|
||||
[THSZEROCTL] = {
|
||||
.hsfreq = thszeroctl,
|
||||
.len = ARRAY_SIZE(thszeroctl),
|
||||
.base_value = 0,
|
||||
},
|
||||
[THSTRAILCTL] = {
|
||||
.hsfreq = thstrailctl,
|
||||
.len = ARRAY_SIZE(thstrailctl),
|
||||
.base_value = 3,
|
||||
},
|
||||
[TLPXCTL] = {
|
||||
.hsfreq = tlpxctl,
|
||||
.len = ARRAY_SIZE(tlpxctl),
|
||||
.base_value = 0,
|
||||
},
|
||||
[THSEXITCTL] = {
|
||||
.hsfreq = thsexitctl,
|
||||
.len = ARRAY_SIZE(thsexitctl),
|
||||
.base_value = 1,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* rzv2h_dphy_find_ulpsexit - Find ULP Exit timing value based on frequency
|
||||
* The function maps frequency ranges to ULP exit timing values.
|
||||
* Thresholds in the local hsfreq[] are expressed in Hz already.
|
||||
*
|
||||
* @freq: Input frequency in Hz
|
||||
*
|
||||
* Return: ULP exit timing value
|
||||
*/
|
||||
static u16 rzv2h_dphy_find_ulpsexit(unsigned long freq)
|
||||
{
|
||||
/* Frequency thresholds in Hz for ULP exit timing selection */
|
||||
static const unsigned long hsfreq[] = {
|
||||
1953125UL,
|
||||
3906250UL,
|
||||
7812500UL,
|
||||
15625000UL,
|
||||
};
|
||||
/* Corresponding ULP exit timing values for each frequency range */
|
||||
static const u16 ulpsexit[] = {49, 98, 195, 391};
|
||||
unsigned int i;
|
||||
|
||||
/* Find the appropriate frequency range */
|
||||
for (i = 0; i < ARRAY_SIZE(hsfreq); i++) {
|
||||
if (freq <= hsfreq[i])
|
||||
break;
|
||||
}
|
||||
|
||||
/* If frequency exceeds all thresholds, use the highest range */
|
||||
if (i == ARRAY_SIZE(hsfreq))
|
||||
i--;
|
||||
|
||||
return ulpsexit[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* rzv2h_dphy_find_timings_val - Find timing parameter value from lookup tables
|
||||
* @freq: Input frequency in Hz
|
||||
* @index: Index to select timing parameter table (see enum rzv2h_dsi_timing_idx)
|
||||
*
|
||||
* Selects the timing table for the requested parameter, finds the
|
||||
* frequency range entry and returns the register value to program:
|
||||
*
|
||||
* register_value = timings->base_value + table_index
|
||||
*
|
||||
* Note: frequency table entries are stored as small integers (units of 10):
|
||||
* threshold_in_hz = (unsigned long)table_entry * 10 * MEGA
|
||||
*
|
||||
* Return: timing register value to be programmed into hardware
|
||||
*/
|
||||
static u16 rzv2h_dphy_find_timings_val(unsigned long freq, u8 index)
|
||||
{
|
||||
const struct rzv2h_mipi_dsi_timings *timings;
|
||||
u16 i;
|
||||
|
||||
/* Get the timing table structure for the requested parameter */
|
||||
timings = &rzv2h_dsi_timings_tables[index];
|
||||
|
||||
/*
|
||||
* Search through frequency table to find appropriate range
|
||||
* timings->hsfreq[i] contains frequency values from HW manual
|
||||
* Convert to Hz by multiplying by 10 * MEGA.
|
||||
*/
|
||||
for (i = 0; i < timings->len; i++) {
|
||||
unsigned long hsfreq = timings->hsfreq[i] * 10 * MEGA;
|
||||
|
||||
if (freq <= hsfreq)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If frequency exceeds table range, use the last entry */
|
||||
if (i == timings->len)
|
||||
i--;
|
||||
|
||||
/*
|
||||
* Calculate final register value:
|
||||
* - timings->base_value: base value for this timing parameter
|
||||
* - i: index into frequency table (0-based)
|
||||
* Combined they give the exact register value to program
|
||||
*/
|
||||
return timings->base_value + i;
|
||||
};
|
||||
|
||||
static void rzg2l_mipi_dsi_phy_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data)
|
||||
{
|
||||
iowrite32(data, dsi->mmio + dsi->info->phy_reg_offset + reg);
|
||||
@@ -317,6 +573,169 @@ static int rzg2l_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_f
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rzv2h_dphy_mode_clk_check(struct rzg2l_mipi_dsi *dsi,
|
||||
unsigned long mode_freq)
|
||||
{
|
||||
u64 hsfreq_millihz, mode_freq_hz, mode_freq_millihz;
|
||||
struct rzv2h_pll_div_pars cpg_dsi_parameters;
|
||||
struct rzv2h_pll_pars dsi_parameters;
|
||||
bool parameters_found;
|
||||
unsigned int bpp;
|
||||
|
||||
bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
|
||||
mode_freq_hz = mul_u32_u32(mode_freq, KILO);
|
||||
mode_freq_millihz = mode_freq_hz * MILLI;
|
||||
parameters_found =
|
||||
rzv2h_get_pll_divs_pars(dsi->info->cpg_plldsi.limits[0],
|
||||
&cpg_dsi_parameters,
|
||||
dsi->info->cpg_plldsi.table,
|
||||
dsi->info->cpg_plldsi.table_size,
|
||||
mode_freq_millihz);
|
||||
if (!parameters_found)
|
||||
return MODE_CLOCK_RANGE;
|
||||
|
||||
hsfreq_millihz = DIV_ROUND_CLOSEST_ULL(cpg_dsi_parameters.div.freq_millihz * bpp,
|
||||
dsi->lanes);
|
||||
parameters_found = rzv2h_get_pll_pars(&rzv2h_plldsi_div_limits,
|
||||
&dsi_parameters, hsfreq_millihz);
|
||||
if (!parameters_found)
|
||||
return MODE_CLOCK_RANGE;
|
||||
|
||||
if (abs(dsi_parameters.error_millihz) >= 500)
|
||||
return MODE_CLOCK_RANGE;
|
||||
|
||||
memcpy(&dsi->mode_calc.dsi_parameters, &dsi_parameters, sizeof(dsi_parameters));
|
||||
dsi->mode_calc.mode_freq_khz = mode_freq;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static int rzv2h_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_freq,
|
||||
u64 *hsfreq_millihz)
|
||||
{
|
||||
struct rzv2h_pll_pars *dsi_parameters = &dsi->mode_calc.dsi_parameters;
|
||||
unsigned long status;
|
||||
|
||||
if (dsi->mode_calc.mode_freq_khz != mode_freq) {
|
||||
status = rzv2h_dphy_mode_clk_check(dsi, mode_freq);
|
||||
if (status != MODE_OK) {
|
||||
dev_err(dsi->dev, "No PLL parameters found for mode clk %lu\n",
|
||||
mode_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
*hsfreq_millihz = dsi_parameters->freq_millihz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi,
|
||||
u64 hsfreq_millihz)
|
||||
{
|
||||
struct rzv2h_pll_pars *dsi_parameters = &dsi->mode_calc.dsi_parameters;
|
||||
unsigned long lpclk_rate = clk_get_rate(dsi->lpclk);
|
||||
u32 phytclksetr, phythssetr, phytlpxsetr, phycr;
|
||||
struct rzg2l_mipi_dsi_timings dphy_timings;
|
||||
u16 ulpsexit;
|
||||
u64 hsfreq;
|
||||
|
||||
hsfreq = DIV_ROUND_CLOSEST_ULL(hsfreq_millihz, MILLI);
|
||||
|
||||
if (dsi_parameters->freq_millihz != hsfreq_millihz &&
|
||||
!rzv2h_get_pll_pars(&rzv2h_plldsi_div_limits, dsi_parameters,
|
||||
hsfreq_millihz)) {
|
||||
dev_err(dsi->dev, "No PLL parameters found for HSFREQ %lluHz\n", hsfreq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dphy_timings.tclk_trail =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, TCLKTRAILCTL);
|
||||
dphy_timings.tclk_post =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, TCLKPOSTCTL);
|
||||
dphy_timings.tclk_zero =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, TCLKZEROCTL);
|
||||
dphy_timings.tclk_prepare =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, TCLKPRPRCTL);
|
||||
dphy_timings.ths_exit =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, THSEXITCTL);
|
||||
dphy_timings.ths_trail =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, THSTRAILCTL);
|
||||
dphy_timings.ths_zero =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, THSZEROCTL);
|
||||
dphy_timings.ths_prepare =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, THSPRPRCTL);
|
||||
dphy_timings.tlpx =
|
||||
rzv2h_dphy_find_timings_val(hsfreq, TLPXCTL);
|
||||
ulpsexit = rzv2h_dphy_find_ulpsexit(lpclk_rate);
|
||||
|
||||
phytclksetr = FIELD_PREP(PHYTCLKSETR_TCLKTRAILCTL, dphy_timings.tclk_trail) |
|
||||
FIELD_PREP(PHYTCLKSETR_TCLKPOSTCTL, dphy_timings.tclk_post) |
|
||||
FIELD_PREP(PHYTCLKSETR_TCLKZEROCTL, dphy_timings.tclk_zero) |
|
||||
FIELD_PREP(PHYTCLKSETR_TCLKPRPRCTL, dphy_timings.tclk_prepare);
|
||||
phythssetr = FIELD_PREP(PHYTHSSETR_THSEXITCTL, dphy_timings.ths_exit) |
|
||||
FIELD_PREP(PHYTHSSETR_THSTRAILCTL, dphy_timings.ths_trail) |
|
||||
FIELD_PREP(PHYTHSSETR_THSZEROCTL, dphy_timings.ths_zero) |
|
||||
FIELD_PREP(PHYTHSSETR_THSPRPRCTL, dphy_timings.ths_prepare);
|
||||
phytlpxsetr = rzg2l_mipi_dsi_phy_read(dsi, PHYTLPXSETR) & ~PHYTLPXSETR_TLPXCTL;
|
||||
phytlpxsetr |= FIELD_PREP(PHYTLPXSETR_TLPXCTL, dphy_timings.tlpx);
|
||||
phycr = rzg2l_mipi_dsi_phy_read(dsi, PHYCR) & ~GENMASK(9, 0);
|
||||
phycr |= FIELD_PREP(PHYCR_ULPSEXIT, ulpsexit);
|
||||
|
||||
/* Setting all D-PHY Timings Registers */
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PHYTCLKSETR, phytclksetr);
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PHYTHSSETR, phythssetr);
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PHYTLPXSETR, phytlpxsetr);
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PHYCR, phycr);
|
||||
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PLLCLKSET0R,
|
||||
FIELD_PREP(PLLCLKSET0R_PLL_S, dsi_parameters->s) |
|
||||
FIELD_PREP(PLLCLKSET0R_PLL_P, dsi_parameters->p) |
|
||||
FIELD_PREP(PLLCLKSET0R_PLL_M, dsi_parameters->m));
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PLLCLKSET1R,
|
||||
FIELD_PREP(PLLCLKSET1R_PLL_K, dsi_parameters->k));
|
||||
|
||||
/*
|
||||
* From RZ/V2H HW manual (Rev.1.20) section 9.5.3 Operation,
|
||||
* (C) After write to D-PHY registers we need to wait for more than 1 x tp
|
||||
*
|
||||
* tp = 1 / (PLLREFCLK / PLLCLKSET0R.PLL_P)
|
||||
* PLLREFCLK = 24MHz
|
||||
* PLLCLKSET0R.PLL_P = {1, 2, 3, 4}
|
||||
*
|
||||
* To handle all the cases lets use PLLCLKSET0R.PLL_P = 4
|
||||
* tp = 1 / (24MHz / 4) = 1 / 6MHz = 166.67ns
|
||||
*/
|
||||
ndelay(200);
|
||||
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PLLENR, PLLENR_PLLEN);
|
||||
/*
|
||||
* From RZ/V2H HW manual (Rev.1.20) section 9.5.3 Operation,
|
||||
* (D) After write to PLLENR.PLLEN we need to wait for more than 3000 x tp
|
||||
*
|
||||
* 3000 x tp = 3000 x 0.16667 ns = 500.01 microseconds
|
||||
*/
|
||||
usleep_range(510, 520);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rzv2h_mipi_dsi_dphy_startup_late_init(struct rzg2l_mipi_dsi *dsi)
|
||||
{
|
||||
/*
|
||||
* From RZ/V2H HW manual (Rev.1.20) section 9.5.3 Operation,
|
||||
* (E) After write to TXSETR we need to wait for more than 200 microseconds
|
||||
* and then write to PHYRSTR
|
||||
*/
|
||||
usleep_range(210, 220);
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PHYRSTR, PHYRSTR_PHYMRSTN);
|
||||
}
|
||||
|
||||
static void rzv2h_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi)
|
||||
{
|
||||
rzg2l_mipi_dsi_phy_write(dsi, PLLENR, 0);
|
||||
}
|
||||
|
||||
static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
@@ -429,6 +848,9 @@ static void rzg2l_mipi_dsi_set_display_timing(struct rzg2l_mipi_dsi *dsi,
|
||||
case 18:
|
||||
vich1ppsetr = VICH1PPSETR_DT_RGB18;
|
||||
break;
|
||||
case 16:
|
||||
vich1ppsetr = VICH1PPSETR_DT_RGB16;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) &&
|
||||
@@ -979,6 +1401,10 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(dsi->vclk))
|
||||
return PTR_ERR(dsi->vclk);
|
||||
|
||||
dsi->lpclk = devm_clk_get(dsi->dev, "lpclk");
|
||||
if (IS_ERR(dsi->lpclk))
|
||||
return PTR_ERR(dsi->lpclk);
|
||||
|
||||
dsi->rstc = devm_reset_control_get_optional_exclusive(dsi->dev, "rst");
|
||||
if (IS_ERR(dsi->rstc))
|
||||
return dev_err_probe(dsi->dev, PTR_ERR(dsi->rstc),
|
||||
@@ -1051,6 +1477,32 @@ static void rzg2l_mipi_dsi_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
RZV2H_CPG_PLL_DSI_LIMITS(rzv2h_cpg_pll_dsi_limits);
|
||||
|
||||
static const struct rzv2h_pll_limits *rzv2h_plldsi_limits[] = {
|
||||
&rzv2h_cpg_pll_dsi_limits,
|
||||
};
|
||||
|
||||
static const u8 rzv2h_cpg_div_table[] = {
|
||||
2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
|
||||
};
|
||||
|
||||
static const struct rzg2l_mipi_dsi_hw_info rzv2h_mipi_dsi_info = {
|
||||
.dphy_init = rzv2h_mipi_dsi_dphy_init,
|
||||
.dphy_startup_late_init = rzv2h_mipi_dsi_dphy_startup_late_init,
|
||||
.dphy_exit = rzv2h_mipi_dsi_dphy_exit,
|
||||
.dphy_mode_clk_check = rzv2h_dphy_mode_clk_check,
|
||||
.dphy_conf_clks = rzv2h_dphy_conf_clks,
|
||||
.cpg_plldsi.limits = rzv2h_plldsi_limits,
|
||||
.cpg_plldsi.table = rzv2h_cpg_div_table,
|
||||
.cpg_plldsi.table_size = ARRAY_SIZE(rzv2h_cpg_div_table),
|
||||
.phy_reg_offset = 0x10000,
|
||||
.link_reg_offset = 0,
|
||||
.min_dclk = 5440,
|
||||
.max_dclk = 187500,
|
||||
.features = RZ_MIPI_DSI_FEATURE_16BPP,
|
||||
};
|
||||
|
||||
static const struct rzg2l_mipi_dsi_hw_info rzg2l_mipi_dsi_info = {
|
||||
.dphy_init = rzg2l_mipi_dsi_dphy_init,
|
||||
.dphy_exit = rzg2l_mipi_dsi_dphy_exit,
|
||||
@@ -1061,6 +1513,7 @@ static const struct rzg2l_mipi_dsi_hw_info rzg2l_mipi_dsi_info = {
|
||||
};
|
||||
|
||||
static const struct of_device_id rzg2l_mipi_dsi_of_table[] = {
|
||||
{ .compatible = "renesas,r9a09g057-mipi-dsi", .data = &rzv2h_mipi_dsi_info, },
|
||||
{ .compatible = "renesas,rzg2l-mipi-dsi", .data = &rzg2l_mipi_dsi_info, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
@@ -40,6 +40,39 @@
|
||||
#define DSIDPHYTIM3_THS_TRAIL(x) ((x) << 8)
|
||||
#define DSIDPHYTIM3_THS_ZERO(x) ((x) << 0)
|
||||
|
||||
/* RZ/V2H DPHY Registers */
|
||||
#define PLLENR 0x000
|
||||
#define PLLENR_PLLEN BIT(0)
|
||||
|
||||
#define PHYRSTR 0x004
|
||||
#define PHYRSTR_PHYMRSTN BIT(0)
|
||||
|
||||
#define PLLCLKSET0R 0x010
|
||||
#define PLLCLKSET0R_PLL_S GENMASK(2, 0)
|
||||
#define PLLCLKSET0R_PLL_P GENMASK(13, 8)
|
||||
#define PLLCLKSET0R_PLL_M GENMASK(25, 16)
|
||||
|
||||
#define PLLCLKSET1R 0x014
|
||||
#define PLLCLKSET1R_PLL_K GENMASK(15, 0)
|
||||
|
||||
#define PHYTCLKSETR 0x020
|
||||
#define PHYTCLKSETR_TCLKTRAILCTL GENMASK(7, 0)
|
||||
#define PHYTCLKSETR_TCLKPOSTCTL GENMASK(15, 8)
|
||||
#define PHYTCLKSETR_TCLKZEROCTL GENMASK(23, 16)
|
||||
#define PHYTCLKSETR_TCLKPRPRCTL GENMASK(31, 24)
|
||||
|
||||
#define PHYTHSSETR 0x024
|
||||
#define PHYTHSSETR_THSEXITCTL GENMASK(7, 0)
|
||||
#define PHYTHSSETR_THSTRAILCTL GENMASK(15, 8)
|
||||
#define PHYTHSSETR_THSZEROCTL GENMASK(23, 16)
|
||||
#define PHYTHSSETR_THSPRPRCTL GENMASK(31, 24)
|
||||
|
||||
#define PHYTLPXSETR 0x028
|
||||
#define PHYTLPXSETR_TLPXCTL GENMASK(7, 0)
|
||||
|
||||
#define PHYCR 0x030
|
||||
#define PHYCR_ULPSEXIT GENMASK(9, 0)
|
||||
|
||||
/* --------------------------------------------------------*/
|
||||
|
||||
/* Link Status Register */
|
||||
@@ -130,6 +163,7 @@
|
||||
|
||||
/* Video-Input Channel 1 Pixel Packet Set Register */
|
||||
#define VICH1PPSETR 0x420
|
||||
#define VICH1PPSETR_DT_RGB16 (0x0e << 16)
|
||||
#define VICH1PPSETR_DT_RGB18 (0x1e << 16)
|
||||
#define VICH1PPSETR_DT_RGB18_LS (0x2e << 16)
|
||||
#define VICH1PPSETR_DT_RGB24 (0x3e << 16)
|
||||
|
||||
@@ -467,6 +467,19 @@ static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
|
||||
.use_drm_infoframe = true,
|
||||
};
|
||||
|
||||
static struct rockchip_hdmi_chip_data rk3368_chip_data = {
|
||||
.lcdsel_grf_reg = -1,
|
||||
};
|
||||
|
||||
static const struct dw_hdmi_plat_data rk3368_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 = &rk3368_chip_data,
|
||||
.use_drm_infoframe = true,
|
||||
};
|
||||
|
||||
static struct rockchip_hdmi_chip_data rk3399_chip_data = {
|
||||
.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
|
||||
.lcdsel_big = FIELD_PREP_WM16_CONST(RK3399_HDMI_LCDC_SEL, 0),
|
||||
@@ -507,6 +520,9 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
|
||||
{ .compatible = "rockchip,rk3328-dw-hdmi",
|
||||
.data = &rk3328_hdmi_drv_data
|
||||
},
|
||||
{ .compatible = "rockchip,rk3368-dw-hdmi",
|
||||
.data = &rk3368_hdmi_drv_data
|
||||
},
|
||||
{ .compatible = "rockchip,rk3399-dw-hdmi",
|
||||
.data = &rk3399_hdmi_drv_data
|
||||
},
|
||||
|
||||
@@ -344,7 +344,7 @@ drm_sched_rq_select_entity_fifo(struct drm_gpu_scheduler *sched,
|
||||
*/
|
||||
static void drm_sched_run_job_queue(struct drm_gpu_scheduler *sched)
|
||||
{
|
||||
if (!READ_ONCE(sched->pause_submit))
|
||||
if (!drm_sched_is_stopped(sched))
|
||||
queue_work(sched->submit_wq, &sched->work_run_job);
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ static void drm_sched_run_job_queue(struct drm_gpu_scheduler *sched)
|
||||
*/
|
||||
static void drm_sched_run_free_queue(struct drm_gpu_scheduler *sched)
|
||||
{
|
||||
if (!READ_ONCE(sched->pause_submit))
|
||||
if (!drm_sched_is_stopped(sched))
|
||||
queue_work(sched->submit_wq, &sched->work_free_job);
|
||||
}
|
||||
|
||||
@@ -729,7 +729,9 @@ EXPORT_SYMBOL(drm_sched_start);
|
||||
*
|
||||
* Drivers can still save and restore their state for recovery operations, but
|
||||
* we shouldn't make this a general scheduler feature around the dma_fence
|
||||
* interface.
|
||||
* interface. The suggested driver-side replacement is to use
|
||||
* drm_sched_for_each_pending_job() after stopping the scheduler and implement
|
||||
* their own recovery operations.
|
||||
*/
|
||||
void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched)
|
||||
{
|
||||
@@ -1567,3 +1569,35 @@ void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched)
|
||||
queue_work(sched->submit_wq, &sched->work_free_job);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_sched_wqueue_start);
|
||||
|
||||
/**
|
||||
* drm_sched_is_stopped() - Checks whether drm_sched is stopped
|
||||
* @sched: DRM scheduler
|
||||
*
|
||||
* Return: true if sched is stopped, false otherwise
|
||||
*/
|
||||
bool drm_sched_is_stopped(struct drm_gpu_scheduler *sched)
|
||||
{
|
||||
return READ_ONCE(sched->pause_submit);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_sched_is_stopped);
|
||||
|
||||
/**
|
||||
* drm_sched_job_is_signaled() - DRM scheduler job is signaled
|
||||
* @job: DRM scheduler job
|
||||
*
|
||||
* Determine if DRM scheduler job is signaled. DRM scheduler should be stopped
|
||||
* to obtain a stable snapshot of state. Both parent fence (hardware fence) and
|
||||
* finished fence (software fence) are checked to determine signaling state.
|
||||
*
|
||||
* Return: true if job is signaled, false otherwise
|
||||
*/
|
||||
bool drm_sched_job_is_signaled(struct drm_sched_job *job)
|
||||
{
|
||||
struct drm_sched_fence *s_fence = job->s_fence;
|
||||
|
||||
WARN_ON(!drm_sched_is_stopped(job->sched));
|
||||
return (s_fence->parent && dma_fence_is_signaled(s_fence->parent)) ||
|
||||
dma_fence_is_signaled(&s_fence->finished);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_sched_job_is_signaled);
|
||||
|
||||
@@ -1,16 +1,44 @@
|
||||
config DRM_ST7571_I2C
|
||||
tristate "DRM support for Sitronix ST7571 display panels (I2C)"
|
||||
depends on DRM && I2C && MMU
|
||||
config DRM_ST7571
|
||||
tristate "DRM support for Sitronix ST7567/ST7571 display panels"
|
||||
depends on DRM && MMU
|
||||
select DRM_CLIENT_SELECTION
|
||||
select DRM_GEM_SHMEM_HELPER
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_I2C
|
||||
select VIDEOMODE_HELPERS
|
||||
help
|
||||
DRM driver for Sitronix ST7571 panels controlled over I2C.
|
||||
Sitronix ST7571 is a driver and controller for 4-level gray
|
||||
scale and monochrome dot matrix LCD panels.
|
||||
|
||||
DRM driver for Sitronix ST7567/ST7571 panels.
|
||||
This is only the core driver, a driver for the appropriate bus
|
||||
transport in your chip also must be selected.
|
||||
|
||||
if M is selected the module will be called st7571.
|
||||
|
||||
config DRM_ST7571_I2C
|
||||
tristate "DRM support for Sitronix ST7567/ST7571 display panels (I2C)"
|
||||
depends on DRM_ST7571 && I2C
|
||||
select REGMAP
|
||||
help
|
||||
Sitronix ST7571 is a driver and controller for 4-level gray
|
||||
scale and monochrome dot matrix LCD panels.
|
||||
|
||||
DRM driver for Sitronix ST7565/ST7571 panels connected via I2C bus.
|
||||
|
||||
if M is selected the module will be called st7571-i2c.
|
||||
|
||||
config DRM_ST7571_SPI
|
||||
tristate "DRM support for Sitronix ST7567/ST7571 display panels (SPI)"
|
||||
depends on DRM_ST7571 && SPI
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Sitronix ST7571 is a driver and controller for 4-level gray
|
||||
scale and monochrome dot matrix LCD panels.
|
||||
|
||||
DRM driver for Sitronix ST7565/ST7571 panels connected via SPI bus.
|
||||
|
||||
if M is selected the module will be called st7571-spi.
|
||||
|
||||
config DRM_ST7586
|
||||
tristate "DRM support for Sitronix ST7586 display panels"
|
||||
depends on DRM && SPI
|
||||
@@ -40,3 +68,13 @@ config DRM_ST7735R
|
||||
|
||||
If M is selected the module will be called st7735r.
|
||||
|
||||
config DRM_ST7920
|
||||
tristate "DRM support for Sitronix ST7920 LCD displays"
|
||||
depends on DRM && SPI && MMU
|
||||
select DRM_GEM_SHMEM_HELPER
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_SPI
|
||||
help
|
||||
DRM driver for the ST7920 Sitronix LCD controllers.
|
||||
|
||||
If M is selected the module will be called st7920.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
obj-$(CONFIG_DRM_ST7571) += st7571.o
|
||||
obj-$(CONFIG_DRM_ST7571_I2C) += st7571-i2c.o
|
||||
obj-$(CONFIG_DRM_ST7571_SPI) += st7571-spi.o
|
||||
obj-$(CONFIG_DRM_ST7586) += st7586.o
|
||||
obj-$(CONFIG_DRM_ST7735R) += st7735r.o
|
||||
obj-$(CONFIG_DRM_ST7920) += st7920.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
76
drivers/gpu/drm/sitronix/st7571-spi.c
Normal file
76
drivers/gpu/drm/sitronix/st7571-spi.c
Normal file
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Driver for Sitronix ST7571 connected via SPI bus.
|
||||
*
|
||||
* Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "st7571.h"
|
||||
|
||||
static const struct regmap_config st7571_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.can_multi_write = true,
|
||||
};
|
||||
|
||||
static int st7571_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct st7571_device *st7571;
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &st7571_spi_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(regmap),
|
||||
"Failed to initialize regmap\n");
|
||||
}
|
||||
|
||||
st7571 = st7571_probe(&spi->dev, regmap);
|
||||
if (IS_ERR(st7571))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(st7571),
|
||||
"Failed to initialize regmap\n");
|
||||
|
||||
spi_set_drvdata(spi, st7571);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void st7571_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct st7571_device *st7571 = spi_get_drvdata(spi);
|
||||
|
||||
st7571_remove(st7571);
|
||||
}
|
||||
|
||||
static const struct of_device_id st7571_of_match[] = {
|
||||
{ .compatible = "sitronix,st7567", .data = &st7567_config },
|
||||
{ .compatible = "sitronix,st7571", .data = &st7571_config },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st7571_of_match);
|
||||
|
||||
static const struct spi_device_id st7571_spi_id[] = {
|
||||
{ "st7567", 0 },
|
||||
{ "st7571", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st7571_spi_id);
|
||||
|
||||
static struct spi_driver st7571_spi_driver = {
|
||||
.driver = {
|
||||
.name = "st7571-spi",
|
||||
.of_match_table = st7571_of_match,
|
||||
},
|
||||
.probe = st7571_spi_probe,
|
||||
.remove = st7571_spi_remove,
|
||||
.id_table = st7571_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(st7571_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
|
||||
MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (SPI)");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("DRM_ST7571");
|
||||
918
drivers/gpu/drm/sitronix/st7571.c
Normal file
918
drivers/gpu/drm/sitronix/st7571.c
Normal file
@@ -0,0 +1,918 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
|
||||
*
|
||||
* Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/clients/drm_client_setup.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fbdev_shmem.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_modeset_helper_vtables.h>
|
||||
#include <drm/drm_module.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include <video/display_timing.h>
|
||||
#include <video/of_display_timing.h>
|
||||
|
||||
#include "st7571.h"
|
||||
|
||||
#define ST7571_COMMAND_MODE (0x00)
|
||||
#define ST7571_DATA_MODE (0x40)
|
||||
|
||||
/* Normal mode command set */
|
||||
#define ST7571_DISPLAY_OFF (0xae)
|
||||
#define ST7571_DISPLAY_ON (0xaf)
|
||||
#define ST7571_OSC_ON (0xab)
|
||||
#define ST7571_SET_COLUMN_LSB(c) (0x00 | FIELD_PREP(GENMASK(3, 0), (c)))
|
||||
#define ST7571_SET_COLUMN_MSB(c) (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4))
|
||||
#define ST7571_SET_COM0_LSB(x) (FIELD_PREP(GENMASK(6, 0), (x)))
|
||||
#define ST7571_SET_COM0_MSB (0x44)
|
||||
#define ST7571_SET_COM_SCAN_DIR(d) (0xc0 | FIELD_PREP(GENMASK(3, 3), (d)))
|
||||
#define ST7571_SET_CONTRAST_LSB(c) (FIELD_PREP(GENMASK(5, 0), (c)))
|
||||
#define ST7571_SET_CONTRAST_MSB (0x81)
|
||||
#define ST7571_SET_DISPLAY_DUTY_LSB(d) (FIELD_PREP(GENMASK(7, 0), (d)))
|
||||
#define ST7571_SET_DISPLAY_DUTY_MSB (0x48)
|
||||
#define ST7571_SET_ENTIRE_DISPLAY_ON(p) (0xa4 | FIELD_PREP(GENMASK(0, 0), (p)))
|
||||
#define ST7571_SET_LCD_BIAS(b) (0x50 | FIELD_PREP(GENMASK(2, 0), (b)))
|
||||
#define ST7571_SET_MODE_LSB(m) (FIELD_PREP(GENMASK(7, 2), (m)))
|
||||
#define ST7571_SET_MODE_MSB (0x38)
|
||||
#define ST7571_SET_PAGE(p) (0xb0 | FIELD_PREP(GENMASK(3, 0), (p)))
|
||||
#define ST7571_SET_POWER(p) (0x28 | FIELD_PREP(GENMASK(2, 0), (p)))
|
||||
#define ST7571_SET_REGULATOR_REG(r) (0x20 | FIELD_PREP(GENMASK(2, 0), (r)))
|
||||
#define ST7571_SET_REVERSE(r) (0xa6 | FIELD_PREP(GENMASK(0, 0), (r)))
|
||||
#define ST7571_SET_SEG_SCAN_DIR(d) (0xa0 | FIELD_PREP(GENMASK(0, 0), (d)))
|
||||
#define ST7571_SET_START_LINE_LSB(l) (FIELD_PREP(GENMASK(6, 0), (l)))
|
||||
#define ST7571_SET_START_LINE_MSB (0x40)
|
||||
|
||||
/* Extension command set 3 */
|
||||
#define ST7571_COMMAND_SET_3 (0x7b)
|
||||
#define ST7571_SET_COLOR_MODE(c) (0x10 | FIELD_PREP(GENMASK(0, 0), (c)))
|
||||
#define ST7571_COMMAND_SET_NORMAL (0x00)
|
||||
|
||||
/* ST7567 commands */
|
||||
#define ST7567_SET_LCD_BIAS(m) (0xa2 | FIELD_PREP(GENMASK(0, 0), (m)))
|
||||
|
||||
#define ST7571_PAGE_HEIGHT 8
|
||||
|
||||
#define DRIVER_NAME "st7571"
|
||||
#define DRIVER_DESC "ST7571 DRM driver"
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
|
||||
{
|
||||
return container_of(drm, struct st7571_device, drm);
|
||||
}
|
||||
|
||||
static int st7571_send_command_list(struct st7571_device *st7571,
|
||||
const u8 *cmd_list, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp)
|
||||
{
|
||||
int xrest = x % 8;
|
||||
u8 result = 0;
|
||||
u8 row_len = 16 * bpp;
|
||||
|
||||
/*
|
||||
* Transforms an (x, y) pixel coordinate into a vertical 8-bit
|
||||
* column from the framebuffer. It calculates the corresponding byte in the
|
||||
* framebuffer, extracts the bit at the given x position across 8 consecutive
|
||||
* rows, and packs those bits into a single byte.
|
||||
*
|
||||
* Return an 8-bit value representing a vertical column of pixels.
|
||||
*/
|
||||
x = x / 8;
|
||||
y = (y / 8) * 8;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int row_idx = y + i;
|
||||
u8 byte = p[row_idx * row_len + x];
|
||||
u8 bit = (byte >> xrest) & 1;
|
||||
|
||||
result |= (bit << i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int st7571_set_position(struct st7571_device *st7571, int x, int y)
|
||||
{
|
||||
u8 cmd_list[] = {
|
||||
ST7571_SET_COLUMN_LSB(x),
|
||||
ST7571_SET_COLUMN_MSB(x),
|
||||
ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT),
|
||||
};
|
||||
|
||||
return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list));
|
||||
}
|
||||
|
||||
static int st7571_fb_clear_screen(struct st7571_device *st7571)
|
||||
{
|
||||
u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
|
||||
char pixelvalue = 0x00;
|
||||
|
||||
st7571_set_position(st7571, 0, 0);
|
||||
for (int i = 0; i < npixels; i++)
|
||||
regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571,
|
||||
const struct iosys_map *vmap,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_rect *rect,
|
||||
struct drm_format_conv_state *fmtcnv_state)
|
||||
{
|
||||
unsigned int dst_pitch;
|
||||
struct iosys_map dst;
|
||||
u32 size;
|
||||
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
|
||||
iosys_map_set_vaddr(&dst, st7571->hwbuf);
|
||||
|
||||
drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
|
||||
break;
|
||||
|
||||
case DRM_FORMAT_R1:
|
||||
size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
|
||||
memcpy(st7571->hwbuf, vmap->vaddr, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
|
||||
const struct iosys_map *vmap,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_rect *rect,
|
||||
struct drm_format_conv_state *fmtcnv_state)
|
||||
{
|
||||
u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
|
||||
unsigned int dst_pitch;
|
||||
struct iosys_map dst;
|
||||
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4);
|
||||
iosys_map_set_vaddr(&dst, st7571->hwbuf);
|
||||
|
||||
drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
|
||||
break;
|
||||
|
||||
case DRM_FORMAT_R1:
|
||||
size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
|
||||
memcpy(st7571->hwbuf, vmap->vaddr, size);
|
||||
break;
|
||||
|
||||
case DRM_FORMAT_R2:
|
||||
size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
|
||||
memcpy(st7571->hwbuf, vmap->vaddr, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
|
||||
{
|
||||
struct st7571_device *st7571 = drm_to_st7571(fb->dev);
|
||||
char *row = st7571->row;
|
||||
|
||||
/* Align y to display page boundaries */
|
||||
rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
|
||||
rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
|
||||
|
||||
for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
|
||||
for (int x = rect->x1; x < rect->x2; x++)
|
||||
row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1);
|
||||
|
||||
st7571_set_position(st7571, rect->x1, y);
|
||||
|
||||
/* TODO: Investige why we can't write multiple bytes at once */
|
||||
for (int x = rect->x1; x < rect->x2; x++)
|
||||
regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect)
|
||||
{
|
||||
struct st7571_device *st7571 = drm_to_st7571(fb->dev);
|
||||
u32 format = fb->format->format;
|
||||
char *row = st7571->row;
|
||||
int x1;
|
||||
int x2;
|
||||
|
||||
/* Align y to display page boundaries */
|
||||
rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
|
||||
rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_R1:
|
||||
x1 = rect->x1 * 1;
|
||||
x2 = rect->x2 * 1;
|
||||
break;
|
||||
case DRM_FORMAT_R2:
|
||||
fallthrough;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
x1 = rect->x1 * 2;
|
||||
x2 = rect->x2 * 2;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
|
||||
for (int x = x1; x < x2; x++)
|
||||
row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2);
|
||||
|
||||
st7571_set_position(st7571, rect->x1, y);
|
||||
|
||||
/* TODO: Investige why we can't write multiple bytes at once */
|
||||
for (int x = x1; x < x2; x++) {
|
||||
regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
|
||||
|
||||
/*
|
||||
* As the display supports grayscale, all pixels must be written as two bits
|
||||
* even if the format is monochrome.
|
||||
*
|
||||
* The bit values maps to the following grayscale:
|
||||
* 0 0 = Black
|
||||
* 0 1 = Dark gray
|
||||
* 1 0 = Light gray
|
||||
* 1 1 = White
|
||||
*
|
||||
* For monochrome formats, write the same value twice to get
|
||||
* either a black or white pixel.
|
||||
*/
|
||||
if (format == DRM_FORMAT_R1)
|
||||
regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7571_connector_get_modes(struct drm_connector *conn)
|
||||
{
|
||||
struct st7571_device *st7571 = drm_to_st7571(conn->dev);
|
||||
|
||||
return drm_connector_helper_get_modes_fixed(conn, &st7571->mode);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = {
|
||||
.get_modes = st7571_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct st7571_panel_format st7571_monochrome = {
|
||||
.prepare_buffer = st7571_prepare_buffer_monochrome,
|
||||
.update_rect = st7571_fb_update_rect_monochrome,
|
||||
.mode = ST7571_COLOR_MODE_BLACKWHITE,
|
||||
.formats = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_R1,
|
||||
},
|
||||
.nformats = 2,
|
||||
};
|
||||
|
||||
static const struct st7571_panel_format st7571_grayscale = {
|
||||
.prepare_buffer = st7571_prepare_buffer_grayscale,
|
||||
.update_rect = st7571_fb_update_rect_grayscale,
|
||||
.mode = ST7571_COLOR_MODE_GRAY,
|
||||
.formats = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_R1,
|
||||
DRM_FORMAT_R2,
|
||||
},
|
||||
.nformats = 3,
|
||||
};
|
||||
|
||||
static const u64 st7571_primary_plane_fmtmods[] = {
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
DRM_FORMAT_MOD_INVALID
|
||||
};
|
||||
|
||||
static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct drm_crtc *new_crtc = new_plane_state->crtc;
|
||||
struct drm_crtc_state *new_crtc_state = NULL;
|
||||
|
||||
if (new_crtc)
|
||||
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
|
||||
|
||||
return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
false, false);
|
||||
}
|
||||
|
||||
static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
|
||||
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
|
||||
struct drm_framebuffer *fb = plane_state->fb;
|
||||
struct drm_atomic_helper_damage_iter iter;
|
||||
struct drm_device *drm = plane->dev;
|
||||
struct drm_rect damage;
|
||||
struct st7571_device *st7571 = drm_to_st7571(plane->dev);
|
||||
int ret, idx;
|
||||
|
||||
if (!fb)
|
||||
return; /* no framebuffer; plane is disabled */
|
||||
|
||||
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if (!drm_dev_enter(drm, &idx))
|
||||
goto out_drm_gem_fb_end_cpu_access;
|
||||
|
||||
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
|
||||
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
||||
st7571->pformat->prepare_buffer(st7571,
|
||||
&shadow_plane_state->data[0],
|
||||
fb, &damage,
|
||||
&shadow_plane_state->fmtcnv_state);
|
||||
|
||||
st7571->pformat->update_rect(fb, &damage);
|
||||
}
|
||||
|
||||
drm_dev_exit(idx);
|
||||
|
||||
out_drm_gem_fb_end_cpu_access:
|
||||
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm = plane->dev;
|
||||
struct st7571_device *st7571 = drm_to_st7571(plane->dev);
|
||||
int idx;
|
||||
|
||||
if (!drm_dev_enter(drm, &idx))
|
||||
return;
|
||||
|
||||
st7571_fb_clear_screen(st7571);
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = {
|
||||
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
|
||||
.atomic_check = st7571_primary_plane_helper_atomic_check,
|
||||
.atomic_update = st7571_primary_plane_helper_atomic_update,
|
||||
.atomic_disable = st7571_primary_plane_helper_atomic_disable,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs st7571_primary_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
DRM_GEM_SHADOW_PLANE_FUNCS,
|
||||
};
|
||||
|
||||
/*
|
||||
* CRTC
|
||||
*/
|
||||
|
||||
static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct st7571_device *st7571 = drm_to_st7571(crtc->dev);
|
||||
|
||||
return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = {
|
||||
.atomic_check = drm_crtc_helper_atomic_check,
|
||||
.mode_valid = st7571_crtc_mode_valid,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs st7571_crtc_funcs = {
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
};
|
||||
|
||||
/*
|
||||
* Encoder
|
||||
*/
|
||||
|
||||
static void st7571_encoder_atomic_enable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct st7571_device *st7571 = drm_to_st7571(drm);
|
||||
u8 command = ST7571_DISPLAY_ON;
|
||||
int ret;
|
||||
|
||||
ret = st7571->pdata->init(st7571);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
st7571_send_command_list(st7571, &command, 1);
|
||||
}
|
||||
|
||||
static void st7571_encoder_atomic_disable(struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm = encoder->dev;
|
||||
struct st7571_device *st7571 = drm_to_st7571(drm);
|
||||
u8 command = ST7571_DISPLAY_OFF;
|
||||
|
||||
st7571_send_command_list(st7571, &command, 1);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs st7571_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
|
||||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = {
|
||||
.atomic_enable = st7571_encoder_atomic_enable,
|
||||
.atomic_disable = st7571_encoder_atomic_disable,
|
||||
};
|
||||
|
||||
/*
|
||||
* Connector
|
||||
*/
|
||||
|
||||
static const struct drm_connector_funcs st7571_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 const struct drm_mode_config_funcs st7571_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create_with_dirty,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static struct drm_display_mode st7571_mode(struct st7571_device *st7571)
|
||||
{
|
||||
struct drm_display_mode mode = {
|
||||
DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines,
|
||||
st7571->width_mm, st7571->height_mm),
|
||||
};
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int st7571_mode_config_init(struct st7571_device *st7571)
|
||||
{
|
||||
struct drm_device *drm = &st7571->drm;
|
||||
const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
|
||||
int ret;
|
||||
|
||||
ret = drmm_mode_config_init(drm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm->mode_config.min_width = constraints->min_ncols;
|
||||
drm->mode_config.min_height = constraints->min_nlines;
|
||||
drm->mode_config.max_width = constraints->max_ncols;
|
||||
drm->mode_config.max_height = constraints->max_nlines;
|
||||
drm->mode_config.preferred_depth = 24;
|
||||
drm->mode_config.funcs = &st7571_mode_config_funcs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7571_plane_init(struct st7571_device *st7571,
|
||||
const struct st7571_panel_format *pformat)
|
||||
{
|
||||
struct drm_plane *primary_plane = &st7571->primary_plane;
|
||||
struct drm_device *drm = &st7571->drm;
|
||||
int ret;
|
||||
|
||||
ret = drm_universal_plane_init(drm, primary_plane, 0,
|
||||
&st7571_primary_plane_funcs,
|
||||
pformat->formats,
|
||||
pformat->nformats,
|
||||
st7571_primary_plane_fmtmods,
|
||||
DRM_PLANE_TYPE_PRIMARY, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs);
|
||||
drm_plane_enable_fb_damage_clips(primary_plane);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7571_crtc_init(struct st7571_device *st7571)
|
||||
{
|
||||
struct drm_plane *primary_plane = &st7571->primary_plane;
|
||||
struct drm_crtc *crtc = &st7571->crtc;
|
||||
struct drm_device *drm = &st7571->drm;
|
||||
int ret;
|
||||
|
||||
ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
|
||||
&st7571_crtc_funcs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7571_encoder_init(struct st7571_device *st7571)
|
||||
{
|
||||
struct drm_encoder *encoder = &st7571->encoder;
|
||||
struct drm_crtc *crtc = &st7571->crtc;
|
||||
struct drm_device *drm = &st7571->drm;
|
||||
int ret;
|
||||
|
||||
ret = drm_encoder_init(drm, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs);
|
||||
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7571_connector_init(struct st7571_device *st7571)
|
||||
{
|
||||
struct drm_connector *connector = &st7571->connector;
|
||||
struct drm_encoder *encoder = &st7571->encoder;
|
||||
struct drm_device *drm = &st7571->drm;
|
||||
int ret;
|
||||
|
||||
ret = drm_connector_init(drm, connector, &st7571_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &st7571_connector_helper_funcs);
|
||||
|
||||
return drm_connector_attach_encoder(connector, encoder);
|
||||
}
|
||||
|
||||
DEFINE_DRM_GEM_FOPS(st7571_fops);
|
||||
|
||||
static const struct drm_driver st7571_driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
|
||||
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
|
||||
.fops = &st7571_fops,
|
||||
DRM_GEM_SHMEM_DRIVER_OPS,
|
||||
DRM_FBDEV_SHMEM_DRIVER_OPS,
|
||||
};
|
||||
|
||||
static int st7571_validate_parameters(struct st7571_device *st7571)
|
||||
{
|
||||
struct device *dev = st7571->dev;
|
||||
const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
|
||||
|
||||
if (st7571->width_mm == 0) {
|
||||
dev_err(dev, "Invalid panel width\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (st7571->height_mm == 0) {
|
||||
dev_err(dev, "Invalid panel height\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (st7571->nlines < constraints->min_nlines ||
|
||||
st7571->nlines > constraints->max_nlines) {
|
||||
dev_err(dev, "Invalid timing configuration.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (st7571->startline + st7571->nlines > constraints->max_nlines) {
|
||||
dev_err(dev, "Invalid timing configuration.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (st7571->ncols < constraints->min_ncols ||
|
||||
st7571->ncols > constraints->max_ncols) {
|
||||
dev_err(dev, "Invalid timing configuration.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (st7571->grayscale && !constraints->support_grayscale) {
|
||||
dev_err(dev, "Grayscale not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7567_parse_dt(struct st7571_device *st7567)
|
||||
{
|
||||
struct device *dev = st7567->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct display_timing dt;
|
||||
int ret;
|
||||
|
||||
ret = of_get_display_timing(np, "panel-timing", &dt);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get display timing from DT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "width-mm", &st7567->width_mm);
|
||||
of_property_read_u32(np, "height-mm", &st7567->height_mm);
|
||||
st7567->inverted = of_property_read_bool(np, "sitronix,inverted");
|
||||
|
||||
st7567->pformat = &st7571_monochrome;
|
||||
st7567->bpp = 1;
|
||||
|
||||
st7567->startline = dt.vfront_porch.typ;
|
||||
st7567->nlines = dt.vactive.typ;
|
||||
st7567->ncols = dt.hactive.typ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7571_parse_dt(struct st7571_device *st7571)
|
||||
{
|
||||
struct device *dev = st7571->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct display_timing dt;
|
||||
int ret;
|
||||
|
||||
ret = of_get_display_timing(np, "panel-timing", &dt);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get display timing from DT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "width-mm", &st7571->width_mm);
|
||||
of_property_read_u32(np, "height-mm", &st7571->height_mm);
|
||||
st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale");
|
||||
st7571->inverted = of_property_read_bool(np, "sitronix,inverted");
|
||||
|
||||
if (st7571->grayscale) {
|
||||
st7571->pformat = &st7571_grayscale;
|
||||
st7571->bpp = 2;
|
||||
} else {
|
||||
st7571->pformat = &st7571_monochrome;
|
||||
st7571->bpp = 1;
|
||||
}
|
||||
|
||||
st7571->startline = dt.vfront_porch.typ;
|
||||
st7571->nlines = dt.vactive.typ;
|
||||
st7571->ncols = dt.hactive.typ;
|
||||
|
||||
st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st7571->reset))
|
||||
return dev_err_probe(dev, PTR_ERR(st7571->reset),
|
||||
"Failed to get reset gpio\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void st7571_reset(struct st7571_device *st7571)
|
||||
{
|
||||
gpiod_set_value_cansleep(st7571->reset, 1);
|
||||
fsleep(20);
|
||||
gpiod_set_value_cansleep(st7571->reset, 0);
|
||||
}
|
||||
|
||||
static int st7567_lcd_init(struct st7571_device *st7567)
|
||||
{
|
||||
/*
|
||||
* Most of the initialization sequence is taken directly from the
|
||||
* referential initial code in the ST7567 datasheet.
|
||||
*/
|
||||
u8 commands[] = {
|
||||
ST7571_DISPLAY_OFF,
|
||||
|
||||
ST7567_SET_LCD_BIAS(1),
|
||||
|
||||
ST7571_SET_SEG_SCAN_DIR(0),
|
||||
ST7571_SET_COM_SCAN_DIR(1),
|
||||
|
||||
ST7571_SET_REGULATOR_REG(4),
|
||||
ST7571_SET_CONTRAST_MSB,
|
||||
ST7571_SET_CONTRAST_LSB(0x20),
|
||||
|
||||
ST7571_SET_START_LINE_MSB,
|
||||
ST7571_SET_START_LINE_LSB(st7567->startline),
|
||||
|
||||
ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */
|
||||
ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */
|
||||
ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */
|
||||
|
||||
ST7571_SET_REVERSE(st7567->inverted ? 1 : 0),
|
||||
ST7571_SET_ENTIRE_DISPLAY_ON(0),
|
||||
};
|
||||
|
||||
return st7571_send_command_list(st7567, commands, ARRAY_SIZE(commands));
|
||||
}
|
||||
|
||||
static int st7571_lcd_init(struct st7571_device *st7571)
|
||||
{
|
||||
/*
|
||||
* Most of the initialization sequence is taken directly from the
|
||||
* referential initial code in the ST7571 datasheet.
|
||||
*/
|
||||
u8 commands[] = {
|
||||
ST7571_DISPLAY_OFF,
|
||||
|
||||
ST7571_SET_MODE_MSB,
|
||||
ST7571_SET_MODE_LSB(0x2e),
|
||||
|
||||
ST7571_SET_SEG_SCAN_DIR(0),
|
||||
ST7571_SET_COM_SCAN_DIR(1),
|
||||
|
||||
ST7571_SET_COM0_MSB,
|
||||
ST7571_SET_COM0_LSB(0x00),
|
||||
|
||||
ST7571_SET_START_LINE_MSB,
|
||||
ST7571_SET_START_LINE_LSB(st7571->startline),
|
||||
|
||||
ST7571_OSC_ON,
|
||||
ST7571_SET_REGULATOR_REG(5),
|
||||
ST7571_SET_CONTRAST_MSB,
|
||||
ST7571_SET_CONTRAST_LSB(0x33),
|
||||
ST7571_SET_LCD_BIAS(0x04),
|
||||
ST7571_SET_DISPLAY_DUTY_MSB,
|
||||
ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines),
|
||||
|
||||
ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */
|
||||
ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */
|
||||
ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */
|
||||
|
||||
ST7571_COMMAND_SET_3,
|
||||
ST7571_SET_COLOR_MODE(st7571->pformat->mode),
|
||||
ST7571_COMMAND_SET_NORMAL,
|
||||
|
||||
ST7571_SET_REVERSE(st7571->inverted ? 1 : 0),
|
||||
ST7571_SET_ENTIRE_DISPLAY_ON(0),
|
||||
};
|
||||
|
||||
/* Perform a reset before initializing the controller */
|
||||
st7571_reset(st7571);
|
||||
|
||||
return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
|
||||
}
|
||||
|
||||
struct st7571_device *st7571_probe(struct device *dev,
|
||||
struct regmap *regmap)
|
||||
{
|
||||
struct st7571_device *st7571;
|
||||
struct drm_device *drm;
|
||||
int ret;
|
||||
|
||||
st7571 = devm_drm_dev_alloc(dev, &st7571_driver,
|
||||
struct st7571_device, drm);
|
||||
if (IS_ERR(st7571))
|
||||
return st7571;
|
||||
|
||||
drm = &st7571->drm;
|
||||
st7571->dev = dev;
|
||||
st7571->pdata = device_get_match_data(st7571->dev);
|
||||
|
||||
ret = st7571->pdata->parse_dt(st7571);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
ret = st7571_validate_parameters(st7571);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
st7571->mode = st7571_mode(st7571);
|
||||
st7571->regmap = regmap;
|
||||
|
||||
st7571->hwbuf = devm_kzalloc(st7571->dev,
|
||||
(st7571->nlines * st7571->ncols * st7571->bpp) / 8,
|
||||
GFP_KERNEL);
|
||||
if (!st7571->hwbuf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
st7571->row = devm_kzalloc(st7571->dev,
|
||||
(st7571->ncols * st7571->bpp),
|
||||
GFP_KERNEL);
|
||||
if (!st7571->row)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = st7571_mode_config_init(st7571);
|
||||
if (ret) {
|
||||
dev_err(st7571->dev, "Failed to initialize mode config\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = st7571_plane_init(st7571, st7571->pformat);
|
||||
if (ret) {
|
||||
dev_err(st7571->dev, "Failed to initialize primary plane\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = st7571_crtc_init(st7571);
|
||||
if (ret < 0) {
|
||||
dev_err(st7571->dev, "Failed to initialize CRTC\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = st7571_encoder_init(st7571);
|
||||
if (ret < 0) {
|
||||
dev_err(st7571->dev, "Failed to initialize encoder\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = st7571_connector_init(st7571);
|
||||
if (ret < 0) {
|
||||
dev_err(st7571->dev, "Failed to initialize connector\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret) {
|
||||
dev_err(st7571->dev, "Failed to register DRM device\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_client_setup(drm, NULL);
|
||||
return st7571;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(st7571_probe);
|
||||
|
||||
void st7571_remove(struct st7571_device *st7571)
|
||||
{
|
||||
drm_dev_unplug(&st7571->drm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(st7571_remove);
|
||||
|
||||
const struct st7571_panel_data st7567_config = {
|
||||
.init = st7567_lcd_init,
|
||||
.parse_dt = st7567_parse_dt,
|
||||
.constraints = {
|
||||
.min_nlines = 1,
|
||||
.max_nlines = 64,
|
||||
.min_ncols = 128,
|
||||
.max_ncols = 128,
|
||||
.support_grayscale = false,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(st7567_config, "DRM_ST7571");
|
||||
|
||||
const struct st7571_panel_data st7571_config = {
|
||||
.init = st7571_lcd_init,
|
||||
.parse_dt = st7571_parse_dt,
|
||||
.constraints = {
|
||||
.min_nlines = 1,
|
||||
.max_nlines = 128,
|
||||
.min_ncols = 128,
|
||||
.max_ncols = 128,
|
||||
.support_grayscale = true,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(st7571_config, "DRM_ST7571");
|
||||
|
||||
MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
|
||||
MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
91
drivers/gpu/drm/sitronix/st7571.h
Normal file
91
drivers/gpu/drm/sitronix/st7571.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Header file for:
|
||||
* Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
|
||||
*
|
||||
* Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __ST7571_H__
|
||||
#define __ST7571_H__
|
||||
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_format_helper.h>
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
enum st7571_color_mode {
|
||||
ST7571_COLOR_MODE_GRAY = 0,
|
||||
ST7571_COLOR_MODE_BLACKWHITE = 1,
|
||||
};
|
||||
|
||||
struct st7571_device;
|
||||
|
||||
struct st7571_panel_constraints {
|
||||
u32 min_nlines;
|
||||
u32 max_nlines;
|
||||
u32 min_ncols;
|
||||
u32 max_ncols;
|
||||
bool support_grayscale;
|
||||
};
|
||||
|
||||
struct st7571_panel_data {
|
||||
int (*init)(struct st7571_device *st7571);
|
||||
int (*parse_dt)(struct st7571_device *st7571);
|
||||
struct st7571_panel_constraints constraints;
|
||||
};
|
||||
|
||||
struct st7571_panel_format {
|
||||
void (*prepare_buffer)(struct st7571_device *st7571,
|
||||
const struct iosys_map *vmap,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_rect *rect,
|
||||
struct drm_format_conv_state *fmtcnv_state);
|
||||
int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect);
|
||||
enum st7571_color_mode mode;
|
||||
const u8 nformats;
|
||||
const u32 formats[];
|
||||
};
|
||||
|
||||
struct st7571_device {
|
||||
struct drm_device drm;
|
||||
struct device *dev;
|
||||
|
||||
struct drm_plane primary_plane;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
|
||||
struct drm_display_mode mode;
|
||||
|
||||
const struct st7571_panel_format *pformat;
|
||||
const struct st7571_panel_data *pdata;
|
||||
struct gpio_desc *reset;
|
||||
struct regmap *regmap;
|
||||
|
||||
bool grayscale;
|
||||
bool inverted;
|
||||
u32 height_mm;
|
||||
u32 width_mm;
|
||||
u32 startline;
|
||||
u32 nlines;
|
||||
u32 ncols;
|
||||
u32 bpp;
|
||||
|
||||
/* Intermediate buffer in LCD friendly format */
|
||||
u8 *hwbuf;
|
||||
|
||||
/* Row of (transformed) pixels ready to be written to the display */
|
||||
u8 *row;
|
||||
};
|
||||
|
||||
extern const struct st7571_panel_data st7567_config;
|
||||
extern const struct st7571_panel_data st7571_config;
|
||||
|
||||
struct st7571_device *st7571_probe(struct device *dev, struct regmap *regmap);
|
||||
void st7571_remove(struct st7571_device *st7571);
|
||||
|
||||
#endif /* __ST7571_H__ */
|
||||
867
drivers/gpu/drm/sitronix/st7920.c
Normal file
867
drivers/gpu/drm/sitronix/st7920.c
Normal file
@@ -0,0 +1,867 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* DRM driver for Sitronix ST7920 LCD displays
|
||||
*
|
||||
* Copyright 2025 Iker Pedrosa <ikerpedrosam@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <drm/clients/drm_client_setup.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fbdev_shmem.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_atomic_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#define DRIVER_NAME "sitronix_st7920"
|
||||
#define DRIVER_DESC "DRM driver for Sitronix ST7920 LCD displays"
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
/* Display organization */
|
||||
#define ST7920_PITCH 16
|
||||
#define ST7920_SCANLINES 64
|
||||
#define BYTES_IN_DISPLAY (ST7920_PITCH * ST7920_SCANLINES)
|
||||
#define BYTES_IN_SEGMENT 2
|
||||
#define PIXELS_PER_SEGMENT (BYTES_IN_SEGMENT * 8)
|
||||
#define ST7920_DEFAULT_WIDTH 128
|
||||
#define ST7920_DEFAULT_HEIGHT 64
|
||||
|
||||
/* Sync sequence */
|
||||
#define SYNC_BITS 0xF8
|
||||
#define RW_HIGH 0x04
|
||||
#define RS_HIGH 0x02
|
||||
|
||||
/* Commands */
|
||||
#define SET_DISPLAY_ON 0x0C
|
||||
#define SET_DISPLAY_OFF 0x08
|
||||
#define SET_DISPLAY_CLEAR 0x01
|
||||
#define SET_BASIC_INSTRUCTION_SET 0x30
|
||||
#define SET_EXT_INSTRUCTION_SET 0x34
|
||||
#define SET_GRAPHICS_DISPLAY 0x36
|
||||
#define SET_GDRAM_ADDRESS 0x80
|
||||
#define SET_GDRAM_DATA 0xFF /* Driver internal command */
|
||||
|
||||
/* Masks */
|
||||
#define HIGH_DATA_MASK 0xF0
|
||||
#define LOW_DATA_MASK 0x0F
|
||||
#define TOP_VERTICAL_ADDRESS 0x80
|
||||
#define BOTTOM_VERTICAL_ADDRESS 0x60
|
||||
#define TOP_HORIZONTAL_ADDRESS 0x00
|
||||
#define BOTTOM_HORIZONTAL_ADDRESS 0x80
|
||||
|
||||
#define CMD_SIZE 35
|
||||
|
||||
struct spi7920_error {
|
||||
int errno;
|
||||
};
|
||||
|
||||
struct st7920_device {
|
||||
struct drm_device drm;
|
||||
struct drm_display_mode mode;
|
||||
struct drm_plane primary_plane;
|
||||
struct drm_crtc crtc;
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
struct spi_device *spi;
|
||||
|
||||
struct regmap *regmap;
|
||||
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
||||
u32 height;
|
||||
u32 width;
|
||||
};
|
||||
|
||||
struct st7920_plane_state {
|
||||
struct drm_shadow_plane_state base;
|
||||
/* Intermediate buffer to convert pixels from XRGB8888 to HW format */
|
||||
u8 *buffer;
|
||||
};
|
||||
|
||||
struct st7920_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
/* Buffer to store pixels in HW format and written to the panel */
|
||||
u8 *data_array;
|
||||
};
|
||||
|
||||
static inline struct st7920_plane_state *to_st7920_plane_state(struct drm_plane_state *state)
|
||||
{
|
||||
return container_of(state, struct st7920_plane_state, base.base);
|
||||
}
|
||||
|
||||
static inline struct st7920_crtc_state *to_st7920_crtc_state(struct drm_crtc_state *state)
|
||||
{
|
||||
return container_of(state, struct st7920_crtc_state, base);
|
||||
}
|
||||
|
||||
static inline struct st7920_device *drm_to_st7920(struct drm_device *drm)
|
||||
{
|
||||
return container_of(drm, struct st7920_device, drm);
|
||||
}
|
||||
|
||||
static int st7920_store_gdram_address(const void *data, u8 *reg)
|
||||
{
|
||||
const u8 y_addr = *(const u8 *)data;
|
||||
bool bottom_screen = (y_addr >= 32);
|
||||
int i = 0;
|
||||
|
||||
reg[i++] = SYNC_BITS;
|
||||
/* Set vertical address */
|
||||
if (!bottom_screen)
|
||||
reg[i++] = TOP_VERTICAL_ADDRESS + (*(uint8_t *)data & HIGH_DATA_MASK);
|
||||
else
|
||||
reg[i++] = BOTTOM_VERTICAL_ADDRESS + (*(uint8_t *)data & HIGH_DATA_MASK);
|
||||
|
||||
reg[i++] = *(uint8_t *)data << 4;
|
||||
/* Set horizontal address */
|
||||
reg[i++] = SET_GDRAM_ADDRESS;
|
||||
if (!bottom_screen)
|
||||
reg[i++] = TOP_HORIZONTAL_ADDRESS;
|
||||
else
|
||||
reg[i++] = BOTTOM_HORIZONTAL_ADDRESS;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int st7920_store_gdram_data(const void *data, u8 *reg)
|
||||
{
|
||||
const u8 *line_data = data;
|
||||
int i = 0, j = 0;
|
||||
|
||||
reg[i++] = SYNC_BITS | RS_HIGH;
|
||||
|
||||
for (j = 0; j < 16; j++) {
|
||||
reg[i++] = line_data[j] & 0xF0;
|
||||
reg[i++] = (line_data[j] << 4) & 0xF0;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int st7920_store_others(int cmd, const void *data, u8 *reg)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
reg[i++] = SYNC_BITS;
|
||||
reg[i++] = cmd & HIGH_DATA_MASK;
|
||||
reg[i++] = (cmd & LOW_DATA_MASK) << 4;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void st7920_spi_write(struct spi_device *spi, int cmd, const void *data,
|
||||
int delay_us, struct spi7920_error *err)
|
||||
{
|
||||
u8 reg[CMD_SIZE] = {0};
|
||||
int size = 0;
|
||||
int ret;
|
||||
|
||||
if (err->errno)
|
||||
return;
|
||||
|
||||
/*
|
||||
* First the sync bits are sent: 11111WS0.
|
||||
* Where W is the read/write (RW) bit and S is the register/data (RS) bit.
|
||||
* Then, every 8 bits instruction/data will be separated into 2 groups.
|
||||
* Higher 4 bits (DB7~DB4) will be placed in the first section followed by
|
||||
* 4 '0's. And lower 4 bits (DB3~DB0) will be placed in the second section
|
||||
* followed by 4 '0's.
|
||||
*/
|
||||
if (cmd == SET_GDRAM_ADDRESS)
|
||||
size = st7920_store_gdram_address(data, reg);
|
||||
else if (cmd == SET_GDRAM_DATA)
|
||||
size = st7920_store_gdram_data(data, reg);
|
||||
else
|
||||
size = st7920_store_others(cmd, data, reg);
|
||||
|
||||
ret = spi_write(spi, reg, size);
|
||||
if (ret) {
|
||||
err->errno = ret;
|
||||
return;
|
||||
}
|
||||
|
||||
if (delay_us)
|
||||
udelay(delay_us);
|
||||
}
|
||||
|
||||
static const struct regmap_config st7920_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static const struct of_device_id st7920_of_match[] = {
|
||||
/* st7920 family */
|
||||
{
|
||||
.compatible = "sitronix,st7920",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st7920_of_match);
|
||||
|
||||
/*
|
||||
* The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even
|
||||
* if the device was registered via OF. This means that the module will not be
|
||||
* auto loaded, unless it contains an alias that matches the MODALIAS reported.
|
||||
*
|
||||
* To workaround this issue, add a SPI device ID table. Even when this should
|
||||
* not be needed for this driver to match the registered SPI devices.
|
||||
*/
|
||||
static const struct spi_device_id st7920_spi_id[] = {
|
||||
/* st7920 family */
|
||||
{ "st7920", 0 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st7920_spi_id);
|
||||
|
||||
static void st7920_power_on(struct st7920_device *st7920,
|
||||
struct spi7920_error *err)
|
||||
{
|
||||
st7920_spi_write(st7920->spi, SET_DISPLAY_ON, NULL, 72, err);
|
||||
}
|
||||
|
||||
static void st7920_power_off(struct st7920_device *st7920,
|
||||
struct spi7920_error *err)
|
||||
{
|
||||
st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, err);
|
||||
st7920_spi_write(st7920->spi, SET_DISPLAY_OFF, NULL, 72, err);
|
||||
}
|
||||
|
||||
static void st7920_hw_reset(struct st7920_device *st7920)
|
||||
{
|
||||
if (!st7920->reset_gpio)
|
||||
return;
|
||||
|
||||
gpiod_set_value_cansleep(st7920->reset_gpio, 1);
|
||||
usleep_range(15, 20);
|
||||
gpiod_set_value_cansleep(st7920->reset_gpio, 0);
|
||||
msleep(40);
|
||||
}
|
||||
|
||||
static int st7920_init(struct st7920_device *st7920)
|
||||
{
|
||||
struct spi7920_error err = {0};
|
||||
|
||||
st7920_spi_write(st7920->spi, SET_BASIC_INSTRUCTION_SET, NULL, 72, &err);
|
||||
st7920_power_on(st7920, &err);
|
||||
st7920_spi_write(st7920->spi, SET_GRAPHICS_DISPLAY, NULL, 72, &err);
|
||||
st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, &err);
|
||||
|
||||
return err.errno;
|
||||
}
|
||||
|
||||
static int st7920_update_rect(struct st7920_device *st7920,
|
||||
struct drm_rect *rect, u8 *buf,
|
||||
u8 *data_array)
|
||||
{
|
||||
struct spi7920_error err = {0};
|
||||
u32 array_idx = 0;
|
||||
int i, j;
|
||||
|
||||
/*
|
||||
* The screen is divided in 64(Y)x8(X) segments and each segment is
|
||||
* further divided in 2 bytes (D15~D0).
|
||||
*
|
||||
* Segment 0x0 is in the top-right corner, while segment 63x15 is in the
|
||||
* bottom-left. They would be displayed in the screen in the following way:
|
||||
* 0x0 0x1 0x2 ... 0x15
|
||||
* 1x0 1x1 1x2 ... 1x15
|
||||
* ...
|
||||
* 63x0 63x1 63x2 ... 63x15
|
||||
*
|
||||
* The data in each byte is big endian.
|
||||
*/
|
||||
|
||||
for (i = 0; i < ST7920_SCANLINES; i++) {
|
||||
u8 *line_start = buf + (i * ST7920_PITCH);
|
||||
u8 line_buffer[ST7920_PITCH];
|
||||
|
||||
for (j = 0; j < ST7920_PITCH; j++) {
|
||||
line_buffer[j] = bitrev8(line_start[j]);
|
||||
data_array[array_idx++] = line_buffer[j];
|
||||
}
|
||||
|
||||
st7920_spi_write(st7920->spi, SET_GDRAM_ADDRESS, &i, 72, &err);
|
||||
st7920_spi_write(st7920->spi, SET_GDRAM_DATA, line_buffer, 72, &err);
|
||||
}
|
||||
|
||||
return err.errno;
|
||||
}
|
||||
|
||||
static void st7920_clear_screen(struct st7920_device *st7920, u8 *data_array)
|
||||
{
|
||||
struct spi7920_error err = {0};
|
||||
|
||||
memset(data_array, 0, BYTES_IN_DISPLAY);
|
||||
|
||||
st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, &err);
|
||||
}
|
||||
|
||||
static int st7920_fb_blit_rect(struct drm_framebuffer *fb,
|
||||
const struct iosys_map *vmap,
|
||||
struct drm_rect *rect,
|
||||
u8 *buf, u8 *data_array,
|
||||
struct drm_format_conv_state *fmtcnv_state)
|
||||
{
|
||||
struct st7920_device *st7920 = drm_to_st7920(fb->dev);
|
||||
struct iosys_map dst;
|
||||
unsigned int dst_pitch;
|
||||
int ret;
|
||||
|
||||
/* Align y to display page boundaries */
|
||||
rect->y1 = round_down(rect->y1, PIXELS_PER_SEGMENT);
|
||||
rect->y2 = min_t(unsigned int, round_up(rect->y2, PIXELS_PER_SEGMENT), st7920->height);
|
||||
|
||||
dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
|
||||
|
||||
iosys_map_set_vaddr(&dst, buf);
|
||||
drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
|
||||
|
||||
ret = st7920_update_rect(st7920, rect, buf, data_array);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st7920_primary_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm = plane->dev;
|
||||
struct st7920_device *st7920 = drm_to_st7920(drm);
|
||||
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct st7920_plane_state *st7920_state = to_st7920_plane_state(plane_state);
|
||||
struct drm_shadow_plane_state *shadow_plane_state = &st7920_state->base;
|
||||
struct drm_crtc *crtc = plane_state->crtc;
|
||||
struct drm_crtc_state *crtc_state = NULL;
|
||||
const struct drm_format_info *fi;
|
||||
unsigned int pitch;
|
||||
int ret;
|
||||
|
||||
if (crtc)
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
|
||||
ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
DRM_PLANE_NO_SCALING,
|
||||
false, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
else if (!plane_state->visible)
|
||||
return 0;
|
||||
|
||||
fi = drm_format_info(DRM_FORMAT_R1);
|
||||
if (!fi)
|
||||
return -EINVAL;
|
||||
|
||||
pitch = drm_format_info_min_pitch(fi, 0, st7920->width);
|
||||
|
||||
if (plane_state->fb->format != fi) {
|
||||
void *buf;
|
||||
|
||||
/* format conversion necessary; reserve buffer */
|
||||
buf = drm_format_conv_state_reserve(&shadow_plane_state->fmtcnv_state,
|
||||
pitch, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st7920_state->buffer = kcalloc(pitch, st7920->height, GFP_KERNEL);
|
||||
if (!st7920_state->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void st7920_primary_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
|
||||
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
|
||||
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
|
||||
struct st7920_crtc_state *st7920_crtc_state = to_st7920_crtc_state(crtc_state);
|
||||
struct st7920_plane_state *st7920_plane_state = to_st7920_plane_state(plane_state);
|
||||
struct drm_framebuffer *fb = plane_state->fb;
|
||||
struct drm_atomic_helper_damage_iter iter;
|
||||
struct drm_device *drm = plane->dev;
|
||||
struct drm_rect dst_clip;
|
||||
struct drm_rect damage;
|
||||
int idx;
|
||||
int ret;
|
||||
|
||||
if (!drm_dev_enter(drm, &idx))
|
||||
return;
|
||||
|
||||
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
|
||||
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
|
||||
drm_atomic_for_each_plane_damage(&iter, &damage) {
|
||||
dst_clip = plane_state->dst;
|
||||
|
||||
if (!drm_rect_intersect(&dst_clip, &damage))
|
||||
continue;
|
||||
|
||||
ret = st7920_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip,
|
||||
st7920_plane_state->buffer,
|
||||
st7920_crtc_state->data_array,
|
||||
&shadow_plane_state->fmtcnv_state);
|
||||
if (ret)
|
||||
drm_err_once(plane->dev, "Failed to write to device: %d.\n", ret);
|
||||
}
|
||||
|
||||
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static void st7920_primary_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm = plane->dev;
|
||||
struct st7920_device *st7920 = drm_to_st7920(drm);
|
||||
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct st7920_crtc_state *st7920_crtc_state;
|
||||
int idx;
|
||||
|
||||
if (!plane_state->crtc)
|
||||
return;
|
||||
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
|
||||
st7920_crtc_state = to_st7920_crtc_state(crtc_state);
|
||||
|
||||
if (!drm_dev_enter(drm, &idx))
|
||||
return;
|
||||
|
||||
st7920_clear_screen(st7920, st7920_crtc_state->data_array);
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
/* Called during init to allocate the plane's atomic state. */
|
||||
static void st7920_primary_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct st7920_plane_state *st7920_state;
|
||||
|
||||
drm_WARN_ON_ONCE(plane->dev, plane->state);
|
||||
|
||||
st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL);
|
||||
if (!st7920_state)
|
||||
return;
|
||||
|
||||
__drm_gem_reset_shadow_plane(plane, &st7920_state->base);
|
||||
}
|
||||
|
||||
static struct drm_plane_state *st7920_primary_plane_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_shadow_plane_state *new_shadow_plane_state;
|
||||
struct st7920_plane_state *st7920_state;
|
||||
|
||||
if (drm_WARN_ON_ONCE(plane->dev, !plane->state))
|
||||
return NULL;
|
||||
|
||||
st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL);
|
||||
if (!st7920_state)
|
||||
return NULL;
|
||||
|
||||
new_shadow_plane_state = &st7920_state->base;
|
||||
|
||||
__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
|
||||
|
||||
return &new_shadow_plane_state->base;
|
||||
}
|
||||
|
||||
static void st7920_primary_plane_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct st7920_plane_state *st7920_state = to_st7920_plane_state(state);
|
||||
|
||||
kfree(st7920_state->buffer);
|
||||
|
||||
__drm_gem_destroy_shadow_plane_state(&st7920_state->base);
|
||||
|
||||
kfree(st7920_state);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs st7920_primary_plane_helper_funcs = {
|
||||
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
|
||||
.atomic_check = st7920_primary_plane_atomic_check,
|
||||
.atomic_update = st7920_primary_plane_atomic_update,
|
||||
.atomic_disable = st7920_primary_plane_atomic_disable,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs st7920_primary_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.reset = st7920_primary_plane_reset,
|
||||
.atomic_duplicate_state = st7920_primary_plane_duplicate_state,
|
||||
.atomic_destroy_state = st7920_primary_plane_destroy_state,
|
||||
.destroy = drm_plane_cleanup,
|
||||
};
|
||||
|
||||
static enum drm_mode_status st7920_crtc_mode_valid(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct st7920_device *st7920 = drm_to_st7920(crtc->dev);
|
||||
|
||||
return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7920->mode);
|
||||
}
|
||||
|
||||
static int st7920_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
struct st7920_crtc_state *st7920_state = to_st7920_crtc_state(crtc_state);
|
||||
int ret;
|
||||
|
||||
ret = drm_crtc_helper_atomic_check(crtc, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st7920_state->data_array = kmalloc(BYTES_IN_DISPLAY, GFP_KERNEL);
|
||||
if (!st7920_state->data_array)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void st7920_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct st7920_device *st7920 = drm_to_st7920(drm);
|
||||
int idx;
|
||||
int ret;
|
||||
|
||||
if (!drm_dev_enter(drm, &idx))
|
||||
return;
|
||||
|
||||
st7920_hw_reset(st7920);
|
||||
|
||||
ret = st7920_init(st7920);
|
||||
if (ret)
|
||||
drm_err(drm, "Failed to init hardware: %d\n", ret);
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static void st7920_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct spi7920_error err = {0};
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct st7920_device *st7920 = drm_to_st7920(drm);
|
||||
int idx;
|
||||
|
||||
drm_dev_enter(drm, &idx);
|
||||
|
||||
st7920_power_off(st7920, &err);
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
/* Called during init to allocate the CRTC's atomic state. */
|
||||
static void st7920_crtc_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct st7920_crtc_state *st7920_state;
|
||||
|
||||
drm_WARN_ON_ONCE(crtc->dev, crtc->state);
|
||||
|
||||
st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL);
|
||||
if (!st7920_state)
|
||||
return;
|
||||
|
||||
__drm_atomic_helper_crtc_reset(crtc, &st7920_state->base);
|
||||
}
|
||||
|
||||
static struct drm_crtc_state *st7920_crtc_duplicate_state(struct drm_crtc *crtc)
|
||||
{
|
||||
struct st7920_crtc_state *st7920_state;
|
||||
|
||||
if (drm_WARN_ON_ONCE(crtc->dev, !crtc->state))
|
||||
return NULL;
|
||||
|
||||
st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL);
|
||||
if (!st7920_state)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_crtc_duplicate_state(crtc, &st7920_state->base);
|
||||
|
||||
return &st7920_state->base;
|
||||
}
|
||||
|
||||
static void st7920_crtc_destroy_state(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct st7920_crtc_state *st7920_state = to_st7920_crtc_state(state);
|
||||
|
||||
kfree(st7920_state->data_array);
|
||||
|
||||
__drm_atomic_helper_crtc_destroy_state(state);
|
||||
|
||||
kfree(st7920_state);
|
||||
}
|
||||
|
||||
/*
|
||||
* The CRTC is always enabled. Screen updates are performed by
|
||||
* the primary plane's atomic_update function. Disabling clears
|
||||
* the screen in the primary plane's atomic_disable function.
|
||||
*/
|
||||
static const struct drm_crtc_helper_funcs st7920_crtc_helper_funcs = {
|
||||
.mode_valid = st7920_crtc_mode_valid,
|
||||
.atomic_check = st7920_crtc_atomic_check,
|
||||
.atomic_enable = st7920_crtc_atomic_enable,
|
||||
.atomic_disable = st7920_crtc_atomic_disable,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_funcs st7920_crtc_funcs = {
|
||||
.reset = st7920_crtc_reset,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.atomic_duplicate_state = st7920_crtc_duplicate_state,
|
||||
.atomic_destroy_state = st7920_crtc_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs st7920_encoder_funcs = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static int st7920_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct st7920_device *st7920 = drm_to_st7920(connector->dev);
|
||||
|
||||
return drm_connector_helper_get_modes_fixed(connector, &st7920->mode);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs st7920_connector_helper_funcs = {
|
||||
.get_modes = st7920_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs st7920_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 const struct drm_mode_config_funcs st7920_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create_with_dirty,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static const uint32_t st7920_formats[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
};
|
||||
|
||||
DEFINE_DRM_GEM_FOPS(st7920_fops);
|
||||
|
||||
static const struct drm_driver st7920_drm_driver = {
|
||||
DRM_GEM_SHMEM_DRIVER_OPS,
|
||||
DRM_FBDEV_SHMEM_DRIVER_OPS,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
.driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
|
||||
.fops = &st7920_fops,
|
||||
};
|
||||
|
||||
static int st7920_init_modeset(struct st7920_device *st7920)
|
||||
{
|
||||
struct drm_display_mode *mode = &st7920->mode;
|
||||
struct drm_device *drm = &st7920->drm;
|
||||
unsigned long max_width, max_height;
|
||||
struct drm_plane *primary_plane;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Modesetting
|
||||
*/
|
||||
|
||||
ret = drmm_mode_config_init(drm);
|
||||
if (ret) {
|
||||
drm_err(drm, "DRM mode config init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mode->type = DRM_MODE_TYPE_DRIVER;
|
||||
mode->clock = 30;
|
||||
mode->hdisplay = st7920->width;
|
||||
mode->htotal = st7920->width;
|
||||
mode->hsync_start = st7920->width;
|
||||
mode->hsync_end = st7920->width;
|
||||
mode->vdisplay = st7920->height;
|
||||
mode->vtotal = st7920->height;
|
||||
mode->vsync_start = st7920->height;
|
||||
mode->vsync_end = st7920->height;
|
||||
mode->width_mm = 27;
|
||||
mode->height_mm = 27;
|
||||
|
||||
max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH);
|
||||
max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT);
|
||||
|
||||
drm->mode_config.min_width = mode->hdisplay;
|
||||
drm->mode_config.max_width = max_width;
|
||||
drm->mode_config.min_height = mode->vdisplay;
|
||||
drm->mode_config.max_height = max_height;
|
||||
drm->mode_config.preferred_depth = 24;
|
||||
drm->mode_config.funcs = &st7920_mode_config_funcs;
|
||||
|
||||
/* Primary plane */
|
||||
|
||||
primary_plane = &st7920->primary_plane;
|
||||
ret = drm_universal_plane_init(drm, primary_plane, 0, &st7920_primary_plane_funcs,
|
||||
st7920_formats, ARRAY_SIZE(st7920_formats),
|
||||
NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm, "DRM primary plane init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_plane_helper_add(primary_plane, &st7920_primary_plane_helper_funcs);
|
||||
|
||||
drm_plane_enable_fb_damage_clips(primary_plane);
|
||||
|
||||
/* CRTC */
|
||||
|
||||
crtc = &st7920->crtc;
|
||||
ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
|
||||
&st7920_crtc_funcs, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm, "DRM crtc init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(crtc, &st7920_crtc_helper_funcs);
|
||||
|
||||
/* Encoder */
|
||||
|
||||
encoder = &st7920->encoder;
|
||||
ret = drm_encoder_init(drm, encoder, &st7920_encoder_funcs,
|
||||
DRM_MODE_ENCODER_NONE, NULL);
|
||||
if (ret) {
|
||||
drm_err(drm, "DRM encoder init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoder->possible_crtcs = drm_crtc_mask(crtc);
|
||||
|
||||
/* Connector */
|
||||
|
||||
connector = &st7920->connector;
|
||||
ret = drm_connector_init(drm, connector, &st7920_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Unknown);
|
||||
if (ret) {
|
||||
drm_err(drm, "DRM connector init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &st7920_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_attach_encoder(connector, encoder);
|
||||
if (ret) {
|
||||
drm_err(drm, "DRM attach connector to encoder failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st7920_probe(struct spi_device *spi)
|
||||
{
|
||||
struct st7920_device *st7920;
|
||||
struct regmap *regmap;
|
||||
struct device *dev = &spi->dev;
|
||||
struct drm_device *drm;
|
||||
int ret;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &st7920_spi_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
st7920 = devm_drm_dev_alloc(dev, &st7920_drm_driver,
|
||||
struct st7920_device, drm);
|
||||
if (IS_ERR(st7920))
|
||||
return PTR_ERR(st7920);
|
||||
|
||||
drm = &st7920->drm;
|
||||
|
||||
st7920->drm.dev = dev;
|
||||
st7920->regmap = regmap;
|
||||
st7920->spi = spi;
|
||||
st7920->width = ST7920_DEFAULT_WIDTH;
|
||||
st7920->height = ST7920_DEFAULT_HEIGHT;
|
||||
|
||||
st7920->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st7920->reset_gpio)) {
|
||||
ret = PTR_ERR(st7920->reset_gpio);
|
||||
return dev_err_probe(dev, ret, "Unable to retrieve reset GPIO\n");
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, st7920);
|
||||
|
||||
ret = st7920_init_modeset(st7920);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "DRM device register failed\n");
|
||||
|
||||
drm_client_setup(drm, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void st7920_remove(struct spi_device *spi)
|
||||
{
|
||||
struct st7920_device *st7920 = spi_get_drvdata(spi);
|
||||
|
||||
drm_dev_unplug(&st7920->drm);
|
||||
drm_atomic_helper_shutdown(&st7920->drm);
|
||||
}
|
||||
|
||||
static void st7920_shutdown(struct spi_device *spi)
|
||||
{
|
||||
struct st7920_device *st7920 = spi_get_drvdata(spi);
|
||||
|
||||
drm_atomic_helper_shutdown(&st7920->drm);
|
||||
}
|
||||
|
||||
static struct spi_driver st7920_spi_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = st7920_of_match,
|
||||
},
|
||||
.id_table = st7920_spi_id,
|
||||
.probe = st7920_probe,
|
||||
.remove = st7920_remove,
|
||||
.shutdown = st7920_shutdown,
|
||||
};
|
||||
module_spi_driver(st7920_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR("Iker Pedrosa <ipedrosam@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include <uapi/drm/drm.h>
|
||||
|
||||
@@ -316,10 +317,8 @@ struct drm_file {
|
||||
/** @table_lock: Protects @object_idr. */
|
||||
spinlock_t table_lock;
|
||||
|
||||
/** @syncobj_idr: Mapping of sync object handles to object pointers. */
|
||||
struct idr syncobj_idr;
|
||||
/** @syncobj_table_lock: Protects @syncobj_idr. */
|
||||
spinlock_t syncobj_table_lock;
|
||||
/** @syncobj_xa: Mapping of sync object handles to object pointers. */
|
||||
struct xarray syncobj_xa;
|
||||
|
||||
/** @filp: Pointer to the core file structure. */
|
||||
struct file *filp;
|
||||
|
||||
@@ -508,11 +508,11 @@ static inline int drm_gem_huge_mnt_create(struct drm_device *dev,
|
||||
/**
|
||||
* drm_gem_get_huge_mnt - Get the huge tmpfs mountpoint used by a DRM device
|
||||
* @dev: DRM device
|
||||
|
||||
*
|
||||
* This function gets the huge tmpfs mountpoint used by DRM device @dev. A huge
|
||||
* tmpfs mountpoint is used instead of `shm_mnt` after a successful call to
|
||||
* drm_gem_huge_mnt_create() when CONFIG_TRANSPARENT_HUGEPAGE is enabled.
|
||||
|
||||
*
|
||||
* Returns:
|
||||
* The huge tmpfs mountpoint in use, NULL otherwise.
|
||||
*/
|
||||
|
||||
@@ -645,6 +645,7 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad);
|
||||
void drm_sched_start(struct drm_gpu_scheduler *sched, int errno);
|
||||
void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched);
|
||||
void drm_sched_fault(struct drm_gpu_scheduler *sched);
|
||||
bool drm_sched_is_stopped(struct drm_gpu_scheduler *sched);
|
||||
|
||||
struct drm_gpu_scheduler *
|
||||
drm_sched_pick_best(struct drm_gpu_scheduler **sched_list,
|
||||
@@ -674,6 +675,7 @@ bool drm_sched_job_has_dependency(struct drm_sched_job *job,
|
||||
struct dma_fence *fence);
|
||||
void drm_sched_job_cleanup(struct drm_sched_job *job);
|
||||
void drm_sched_increase_karma(struct drm_sched_job *bad);
|
||||
bool drm_sched_job_is_signaled(struct drm_sched_job *job);
|
||||
|
||||
static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job,
|
||||
int threshold)
|
||||
@@ -698,4 +700,54 @@ void drm_sched_entity_modify_sched(struct drm_sched_entity *entity,
|
||||
struct drm_gpu_scheduler **sched_list,
|
||||
unsigned int num_sched_list);
|
||||
|
||||
/**
|
||||
* struct drm_sched_pending_job_iter - DRM scheduler pending job iterator state
|
||||
* @sched: DRM scheduler associated with pending job iterator
|
||||
*/
|
||||
struct drm_sched_pending_job_iter {
|
||||
struct drm_gpu_scheduler *sched;
|
||||
};
|
||||
|
||||
/* Drivers should never call this directly */
|
||||
static inline struct drm_sched_pending_job_iter
|
||||
__drm_sched_pending_job_iter_begin(struct drm_gpu_scheduler *sched)
|
||||
{
|
||||
struct drm_sched_pending_job_iter iter = {
|
||||
.sched = sched,
|
||||
};
|
||||
|
||||
WARN_ON(!drm_sched_is_stopped(sched));
|
||||
return iter;
|
||||
}
|
||||
|
||||
/* Drivers should never call this directly */
|
||||
static inline void
|
||||
__drm_sched_pending_job_iter_end(const struct drm_sched_pending_job_iter iter)
|
||||
{
|
||||
WARN_ON(!drm_sched_is_stopped(iter.sched));
|
||||
}
|
||||
|
||||
DEFINE_CLASS(drm_sched_pending_job_iter, struct drm_sched_pending_job_iter,
|
||||
__drm_sched_pending_job_iter_end(_T),
|
||||
__drm_sched_pending_job_iter_begin(__sched),
|
||||
struct drm_gpu_scheduler *__sched);
|
||||
static inline void *
|
||||
class_drm_sched_pending_job_iter_lock_ptr(class_drm_sched_pending_job_iter_t *_T)
|
||||
{ return _T; }
|
||||
#define class_drm_sched_pending_job_iter_is_conditional false
|
||||
|
||||
/**
|
||||
* drm_sched_for_each_pending_job() - Iterator for each pending job in scheduler
|
||||
* @__job: Current pending job being iterated over
|
||||
* @__sched: DRM scheduler to iterate over pending jobs
|
||||
* @__entity: DRM scheduler entity to filter jobs, NULL indicates no filter
|
||||
*
|
||||
* Iterator for each pending job in scheduler, filtering on an entity, and
|
||||
* enforcing scheduler is fully stopped
|
||||
*/
|
||||
#define drm_sched_for_each_pending_job(__job, __sched, __entity) \
|
||||
scoped_guard(drm_sched_pending_job_iter, (__sched)) \
|
||||
list_for_each_entry((__job), &(__sched)->pending_list, list) \
|
||||
for_each_if(!(__entity) || (__job)->entity == (__entity))
|
||||
|
||||
#endif
|
||||
|
||||
157
include/trace/events/dma_buf.h
Normal file
157
include/trace/events/dma_buf.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM dma_buf
|
||||
|
||||
#if !defined(_TRACE_DMA_BUF_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_DMA_BUF_H
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
DECLARE_EVENT_CLASS(dma_buf,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf),
|
||||
|
||||
TP_ARGS(dmabuf),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(exp_name, dmabuf->exp_name)
|
||||
__field(size_t, size)
|
||||
__field(ino_t, ino)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(exp_name);
|
||||
__entry->size = dmabuf->size;
|
||||
__entry->ino = dmabuf->file->f_inode->i_ino;
|
||||
),
|
||||
|
||||
TP_printk("exp_name=%s size=%zu ino=%lu",
|
||||
__get_str(exp_name),
|
||||
__entry->size,
|
||||
__entry->ino)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dma_buf_attach_dev,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach,
|
||||
bool is_dynamic, struct device *dev),
|
||||
|
||||
TP_ARGS(dmabuf, attach, is_dynamic, dev),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(dev_name, dev_name(dev))
|
||||
__string(exp_name, dmabuf->exp_name)
|
||||
__field(size_t, size)
|
||||
__field(ino_t, ino)
|
||||
__field(struct dma_buf_attachment *, attach)
|
||||
__field(bool, is_dynamic)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(dev_name);
|
||||
__assign_str(exp_name);
|
||||
__entry->size = dmabuf->size;
|
||||
__entry->ino = dmabuf->file->f_inode->i_ino;
|
||||
__entry->is_dynamic = is_dynamic;
|
||||
__entry->attach = attach;
|
||||
),
|
||||
|
||||
TP_printk("exp_name=%s size=%zu ino=%lu attachment:%p is_dynamic=%d dev_name=%s",
|
||||
__get_str(exp_name),
|
||||
__entry->size,
|
||||
__entry->ino,
|
||||
__entry->attach,
|
||||
__entry->is_dynamic,
|
||||
__get_str(dev_name))
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dma_buf_fd,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf, int fd),
|
||||
|
||||
TP_ARGS(dmabuf, fd),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(exp_name, dmabuf->exp_name)
|
||||
__field(size_t, size)
|
||||
__field(ino_t, ino)
|
||||
__field(int, fd)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(exp_name);
|
||||
__entry->size = dmabuf->size;
|
||||
__entry->ino = dmabuf->file->f_inode->i_ino;
|
||||
__entry->fd = fd;
|
||||
),
|
||||
|
||||
TP_printk("exp_name=%s size=%zu ino=%lu fd=%d",
|
||||
__get_str(exp_name),
|
||||
__entry->size,
|
||||
__entry->ino,
|
||||
__entry->fd)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf, dma_buf_export,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf),
|
||||
|
||||
TP_ARGS(dmabuf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf, dma_buf_mmap_internal,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf),
|
||||
|
||||
TP_ARGS(dmabuf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf, dma_buf_mmap,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf),
|
||||
|
||||
TP_ARGS(dmabuf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf, dma_buf_put,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf),
|
||||
|
||||
TP_ARGS(dmabuf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf_attach_dev, dma_buf_dynamic_attach,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach,
|
||||
bool is_dynamic, struct device *dev),
|
||||
|
||||
TP_ARGS(dmabuf, attach, is_dynamic, dev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf_attach_dev, dma_buf_detach,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach,
|
||||
bool is_dynamic, struct device *dev),
|
||||
|
||||
TP_ARGS(dmabuf, attach, is_dynamic, dev)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf_fd, dma_buf_fd,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf, int fd),
|
||||
|
||||
TP_ARGS(dmabuf, fd)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dma_buf_fd, dma_buf_get,
|
||||
|
||||
TP_PROTO(struct dma_buf *dmabuf, int fd),
|
||||
|
||||
TP_ARGS(dmabuf, fd)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_DMA_BUF_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
@@ -19,6 +19,14 @@ extern "C" {
|
||||
#define AMDXDNA_INVALID_BO_HANDLE 0
|
||||
#define AMDXDNA_INVALID_FENCE_HANDLE 0
|
||||
|
||||
/*
|
||||
* Define hardware context priority
|
||||
*/
|
||||
#define AMDXDNA_QOS_REALTIME_PRIORITY 0x100
|
||||
#define AMDXDNA_QOS_HIGH_PRIORITY 0x180
|
||||
#define AMDXDNA_QOS_NORMAL_PRIORITY 0x200
|
||||
#define AMDXDNA_QOS_LOW_PRIORITY 0x280
|
||||
|
||||
enum amdxdna_device_type {
|
||||
AMDXDNA_DEV_TYPE_UNKNOWN = -1,
|
||||
AMDXDNA_DEV_TYPE_KMQ,
|
||||
|
||||
@@ -350,7 +350,7 @@ struct drm_panthor_gpu_info {
|
||||
__u32 as_present;
|
||||
|
||||
/**
|
||||
* @select_coherency: Coherency selected for this device.
|
||||
* @selected_coherency: Coherency selected for this device.
|
||||
*
|
||||
* One of drm_panthor_gpu_coherency.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user