Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'drm-misc-next-2024-12-19' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for 6.14:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
- connector: Add a mutex to protect ELD access, Add a helper to create
a connector in two steps

Driver Changes:
- amdxdna: Add RyzenAI-npu6 Support, various improvements
- rcar-du: Add r8a779h0 Support
- rockchip: various improvements
- zynqmp: Add DP audio support
- bridges:
- ti-sn65dsi83: Add ti,lvds-vod-swing optional properties
- panels:
- new panels: Tianma TM070JDHG34-00, Multi-Inno Technology MI1010Z1T-1CP11

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maxime Ripard <mripard@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20241219-truthful-demonic-hound-598f63@houat

+6446 -1102
+1
Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
··· 19 19 enum: 20 20 - renesas,r8a779a0-dsi-csi2-tx # for V3U 21 21 - renesas,r8a779g0-dsi-csi2-tx # for V4H 22 + - renesas,r8a779h0-dsi-csi2-tx # for V4M 22 23 23 24 reg: 24 25 maxItems: 1
+32 -2
Documentation/devicetree/bindings/display/bridge/ti,sn65dsi83.yaml
··· 80 80 - const: 4 81 81 82 82 port@2: 83 - $ref: /schemas/graph.yaml#/properties/port 84 83 description: Video port for LVDS Channel-A output (panel or bridge). 84 + $ref: '#/$defs/lvds-port' 85 85 86 86 port@3: 87 - $ref: /schemas/graph.yaml#/properties/port 88 87 description: Video port for LVDS Channel-B output (panel or bridge). 88 + $ref: '#/$defs/lvds-port' 89 89 90 90 required: 91 91 - port@0 ··· 95 95 - compatible 96 96 - reg 97 97 - ports 98 + 99 + $defs: 100 + lvds-port: 101 + $ref: /schemas/graph.yaml#/$defs/port-base 102 + unevaluatedProperties: false 103 + 104 + properties: 105 + endpoint: 106 + $ref: /schemas/media/video-interfaces.yaml# 107 + unevaluatedProperties: false 108 + 109 + properties: 110 + ti,lvds-termination-ohms: 111 + description: The value of near end differential termination in ohms. 112 + enum: [100, 200] 113 + default: 200 114 + 115 + ti,lvds-vod-swing-clock-microvolt: 116 + description: LVDS diferential output voltage <min max> for clock 117 + lanes in microvolts. 118 + $ref: /schemas/types.yaml#/definitions/uint32-array 119 + minItems: 2 120 + maxItems: 2 121 + 122 + ti,lvds-vod-swing-data-microvolt: 123 + description: LVDS diferential output voltage <min max> for data 124 + lanes in microvolts. 125 + $ref: /schemas/types.yaml#/definitions/uint32-array 126 + minItems: 2 127 + maxItems: 2 98 128 99 129 allOf: 100 130 - if:
+2
Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
··· 42 42 # Admatec 9904379 10.1" 1024x600 LVDS panel 43 43 - admatec,9904379 44 44 - auo,b101ew05 45 + # AUO G084SN05 V9 8.4" 800x600 LVDS panel 46 + - auo,g084sn05 45 47 # Chunghwa Picture Tubes Ltd. 7" WXGA (800x1280) TFT LCD LVDS panel 46 48 - chunghwa,claa070wp03xg 47 49 # EDT ETML0700Z9NDHA 7.0" WSVGA (1024x600) color TFT LCD LVDS panel
+6
Documentation/devicetree/bindings/display/panel/panel-simple.yaml
··· 206 206 - mitsubishi,aa070mc01-ca1 207 207 # Mitsubishi AA084XE01 8.4" XGA TFT LCD panel 208 208 - mitsubishi,aa084xe01 209 + # Multi-Inno Technology Co.,Ltd MI0700A2T-30 7" 800x480 TFT Resistive Touch Module 210 + - multi-inno,mi0700a2t-30 209 211 # Multi-Inno Technology Co.,Ltd MI0700S4T-6 7" 800x480 TFT Resistive Touch Module 210 212 - multi-inno,mi0700s4t-6 211 213 # Multi-Inno Technology Co.,Ltd MI0800FT-9 8" 800x600 TFT Resistive Touch Module 212 214 - multi-inno,mi0800ft-9 213 215 # Multi-Inno Technology Co.,Ltd MI1010AIT-1CP 10.1" 1280x800 LVDS IPS Cap Touch Mod. 214 216 - multi-inno,mi1010ait-1cp 217 + # Multi-Inno Technology Co.,Ltd MI1010Z1T-1CP11 10.1" 1024x600 TFT Resistive Touch Module 218 + - multi-inno,mi1010z1t-1cp11 215 219 # NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel 216 220 - nec,nl12880bc20-05 217 221 # NEC LCD Technologies,Ltd. WQVGA TFT LCD panel ··· 284 280 - team-source-display,tst043015cmhx 285 281 # Tianma Micro-electronics TM070JDHG30 7.0" WXGA TFT LCD panel 286 282 - tianma,tm070jdhg30 283 + # Tianma Micro-electronics TM070JDHG34-00 7.0" WXGA (1280x800) LVDS TFT LCD panel 284 + - tianma,tm070jdhg34-00 287 285 # Tianma Micro-electronics TM070JVHG33 7.0" WXGA TFT LCD panel 288 286 - tianma,tm070jvhg33 289 287 # Tianma Micro-electronics TM070RVHG71 7.0" WXGA TFT LCD panel
+62 -5
Documentation/devicetree/bindings/display/renesas,du.yaml
··· 41 41 - renesas,du-r8a77995 # for R-Car D3 compatible DU 42 42 - renesas,du-r8a779a0 # for R-Car V3U compatible DU 43 43 - renesas,du-r8a779g0 # for R-Car V4H compatible DU 44 + - renesas,du-r8a779h0 # for R-Car V4M compatible DU 44 45 45 46 reg: 46 47 maxItems: 1 ··· 70 69 $ref: /schemas/graph.yaml#/properties/port 71 70 unevaluatedProperties: false 72 71 73 - required: 74 - - port@0 75 - - port@1 76 - 77 72 unevaluatedProperties: false 78 73 79 74 renesas,cmms: 80 75 $ref: /schemas/types.yaml#/definitions/phandle-array 76 + minItems: 1 77 + maxItems: 4 81 78 items: 82 79 maxItems: 1 83 80 description: ··· 84 85 85 86 renesas,vsps: 86 87 $ref: /schemas/types.yaml#/definitions/phandle-array 88 + minItems: 1 89 + maxItems: 4 87 90 items: 88 91 items: 89 92 - description: phandle to VSP instance that serves the DU channel ··· 490 489 491 490 renesas,cmms: 492 491 minItems: 4 492 + maxItems: 4 493 493 494 494 renesas,vsps: 495 495 minItems: 4 496 + maxItems: 4 496 497 497 498 required: 498 499 - clock-names ··· 561 558 562 559 renesas,cmms: 563 560 minItems: 3 561 + maxItems: 3 564 562 565 563 renesas,vsps: 566 564 minItems: 3 565 + maxItems: 3 567 566 568 567 required: 569 568 - clock-names ··· 632 627 633 628 renesas,cmms: 634 629 minItems: 3 630 + maxItems: 3 635 631 636 632 renesas,vsps: 637 633 minItems: 3 634 + maxItems: 3 638 635 639 636 required: 640 637 - clock-names ··· 690 683 - port@1 691 684 692 685 renesas,vsps: 693 - minItems: 1 686 + maxItems: 1 694 687 695 688 required: 696 689 - clock-names ··· 753 746 754 747 renesas,cmms: 755 748 minItems: 2 749 + maxItems: 2 756 750 757 751 renesas,vsps: 758 752 minItems: 2 753 + maxItems: 2 759 754 760 755 required: 761 756 - clock-names ··· 808 799 809 800 renesas,vsps: 810 801 minItems: 2 802 + maxItems: 2 803 + 804 + required: 805 + - clock-names 806 + - interrupts 807 + - resets 808 + - reset-names 809 + - renesas,vsps 810 + 811 + - if: 812 + properties: 813 + compatible: 814 + contains: 815 + enum: 816 + - renesas,du-r8a779h0 817 + then: 818 + properties: 819 + clocks: 820 + items: 821 + - description: Functional clock 822 + 823 + clock-names: 824 + items: 825 + - const: du.0 826 + 827 + interrupts: 828 + maxItems: 1 829 + 830 + resets: 831 + maxItems: 1 832 + 833 + reset-names: 834 + items: 835 + - const: du.0 836 + 837 + ports: 838 + properties: 839 + port@0: 840 + description: DSI 0 841 + port@1: false 842 + port@2: false 843 + port@3: false 844 + 845 + required: 846 + - port@0 847 + 848 + renesas,vsps: 849 + maxItems: 1 811 850 812 851 required: 813 852 - clock-names
+120
Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-mipi-dsi2.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/display/rockchip/rockchip,rk3588-mipi-dsi2.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Rockchip specific extensions to the Synopsys Designware MIPI DSI2 8 + 9 + maintainers: 10 + - Heiko Stuebner <heiko@sntech.de> 11 + 12 + properties: 13 + compatible: 14 + enum: 15 + - rockchip,rk3588-mipi-dsi2 16 + 17 + reg: 18 + maxItems: 1 19 + 20 + interrupts: 21 + maxItems: 1 22 + 23 + clocks: 24 + maxItems: 2 25 + 26 + clock-names: 27 + items: 28 + - const: pclk 29 + - const: sys 30 + 31 + rockchip,grf: 32 + $ref: /schemas/types.yaml#/definitions/phandle 33 + description: 34 + This SoC uses GRF regs to switch between vopl/vopb. 35 + 36 + phys: 37 + maxItems: 1 38 + 39 + phy-names: 40 + const: dcphy 41 + 42 + power-domains: 43 + maxItems: 1 44 + 45 + resets: 46 + maxItems: 1 47 + 48 + reset-names: 49 + const: apb 50 + 51 + ports: 52 + $ref: /schemas/graph.yaml#/properties/ports 53 + 54 + properties: 55 + port@0: 56 + $ref: /schemas/graph.yaml#/properties/port 57 + description: Input node to receive pixel data. 58 + 59 + port@1: 60 + $ref: /schemas/graph.yaml#/properties/port 61 + description: DSI output node to panel. 62 + 63 + required: 64 + - port@0 65 + - port@1 66 + 67 + required: 68 + - compatible 69 + - clocks 70 + - clock-names 71 + - rockchip,grf 72 + - phys 73 + - phy-names 74 + - ports 75 + - reg 76 + 77 + allOf: 78 + - $ref: /schemas/display/dsi-controller.yaml# 79 + 80 + unevaluatedProperties: false 81 + 82 + examples: 83 + - | 84 + #include <dt-bindings/clock/rockchip,rk3588-cru.h> 85 + #include <dt-bindings/interrupt-controller/arm-gic.h> 86 + #include <dt-bindings/interrupt-controller/irq.h> 87 + #include <dt-bindings/phy/phy.h> 88 + #include <dt-bindings/power/rk3588-power.h> 89 + #include <dt-bindings/reset/rockchip,rk3588-cru.h> 90 + 91 + soc { 92 + #address-cells = <2>; 93 + #size-cells = <2>; 94 + 95 + dsi@fde20000 { 96 + compatible = "rockchip,rk3588-mipi-dsi2"; 97 + reg = <0x0 0xfde20000 0x0 0x10000>; 98 + interrupts = <GIC_SPI 167 IRQ_TYPE_LEVEL_HIGH 0>; 99 + clocks = <&cru PCLK_DSIHOST0>, <&cru CLK_DSIHOST0>; 100 + clock-names = "pclk", "sys"; 101 + resets = <&cru SRST_P_DSIHOST0>; 102 + reset-names = "apb"; 103 + power-domains = <&power RK3588_PD_VOP>; 104 + phys = <&mipidcphy0 PHY_TYPE_DPHY>; 105 + phy-names = "dcphy"; 106 + rockchip,grf = <&vop_grf>; 107 + 108 + ports { 109 + #address-cells = <1>; 110 + #size-cells = <0>; 111 + dsi0_in: port@0 { 112 + reg = <0>; 113 + }; 114 + 115 + dsi0_out: port@1 { 116 + reg = <1>; 117 + }; 118 + }; 119 + }; 120 + };
+8 -2
Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml
··· 100 100 - description: Video layer, plane 1 (U/V or U) 101 101 - description: Video layer, plane 2 (V) 102 102 - description: Graphics layer 103 + - description: Audio channel 0 104 + - description: Audio channel 1 103 105 dma-names: 104 106 items: 105 107 - const: vid0 106 108 - const: vid1 107 109 - const: vid2 108 110 - const: gfx0 111 + - const: aud0 112 + - const: aud1 109 113 110 114 phys: 111 115 description: PHYs for the DP data lanes ··· 198 194 power-domains = <&pd_dp>; 199 195 resets = <&reset ZYNQMP_RESET_DP>; 200 196 201 - dma-names = "vid0", "vid1", "vid2", "gfx0"; 197 + dma-names = "vid0", "vid1", "vid2", "gfx0", "aud0", "aud1"; 202 198 dmas = <&xlnx_dpdma 0>, 203 199 <&xlnx_dpdma 1>, 204 200 <&xlnx_dpdma 2>, 205 - <&xlnx_dpdma 3>; 201 + <&xlnx_dpdma 3>, 202 + <&xlnx_dpdma 4>, 203 + <&xlnx_dpdma 5>; 206 204 207 205 phys = <&psgtr 1 PHY_TYPE_DP 0 3>, 208 206 <&psgtr 0 PHY_TYPE_DP 1 3>;
+5 -2
arch/arm64/boot/dts/xilinx/zynqmp.dtsi
··· 1306 1306 "dp_vtc_pixel_clk_in"; 1307 1307 power-domains = <&zynqmp_firmware PD_DP>; 1308 1308 resets = <&zynqmp_reset ZYNQMP_RESET_DP>; 1309 - dma-names = "vid0", "vid1", "vid2", "gfx0"; 1309 + dma-names = "vid0", "vid1", "vid2", "gfx0", 1310 + "aud0", "aud1"; 1310 1311 dmas = <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO0>, 1311 1312 <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO1>, 1312 1313 <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO2>, 1313 - <&zynqmp_dpdma ZYNQMP_DPDMA_GRAPHICS>; 1314 + <&zynqmp_dpdma ZYNQMP_DPDMA_GRAPHICS>, 1315 + <&zynqmp_dpdma ZYNQMP_DPDMA_AUDIO0>, 1316 + <&zynqmp_dpdma ZYNQMP_DPDMA_AUDIO1>; 1314 1317 1315 1318 ports { 1316 1319 #address-cells = <1>;
+3 -1
drivers/accel/amdxdna/Makefile
··· 5 5 aie2_error.o \ 6 6 aie2_message.o \ 7 7 aie2_pci.o \ 8 + aie2_pm.o \ 8 9 aie2_psp.o \ 9 10 aie2_smu.o \ 10 11 aie2_solver.o \ ··· 18 17 npu1_regs.o \ 19 18 npu2_regs.o \ 20 19 npu4_regs.o \ 21 - npu5_regs.o 20 + npu5_regs.o \ 21 + npu6_regs.o 22 22 obj-$(CONFIG_DRM_ACCEL_AMDXDNA) = amdxdna.o
-2
drivers/accel/amdxdna/TODO
··· 1 - - Replace idr with xa 2 1 - Add import and export BO support 3 2 - Add debugfs support 4 3 - Add debug BO support 5 - - Improve power management
+13 -3
drivers/accel/amdxdna/aie2_ctx.c
··· 11 11 #include <drm/drm_syncobj.h> 12 12 #include <linux/hmm.h> 13 13 #include <linux/types.h> 14 + #include <linux/xarray.h> 14 15 #include <trace/events/amdxdna.h> 15 16 16 17 #include "aie2_msg_priv.h" ··· 91 90 { 92 91 struct amdxdna_dev *xdna = client->xdna; 93 92 struct amdxdna_hwctx *hwctx; 94 - int next = 0; 93 + unsigned long hwctx_id; 95 94 96 95 drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); 97 96 mutex_lock(&client->hwctx_lock); 98 - idr_for_each_entry_continue(&client->hwctx_idr, hwctx, next) { 97 + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { 99 98 if (hwctx->status != HWCTX_STAT_STOP) 100 99 continue; 101 100 ··· 180 179 up(&job->hwctx->priv->job_sem); 181 180 job->job_done = true; 182 181 dma_fence_put(fence); 183 - mmput(job->mm); 182 + mmput_async(job->mm); 184 183 aie2_job_put(job); 185 184 } 186 185 ··· 519 518 struct drm_gpu_scheduler *sched; 520 519 struct amdxdna_hwctx_priv *priv; 521 520 struct amdxdna_gem_obj *heap; 521 + struct amdxdna_dev_hdl *ndev; 522 522 int i, ret; 523 523 524 524 priv = kzalloc(sizeof(*hwctx->priv), GFP_KERNEL); ··· 614 612 } 615 613 616 614 hwctx->status = HWCTX_STAT_INIT; 615 + ndev = xdna->dev_handle; 616 + ndev->hwctx_num++; 617 617 618 618 XDNA_DBG(xdna, "hwctx %s init completed", hwctx->name); 619 619 ··· 645 641 646 642 void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx) 647 643 { 644 + struct amdxdna_dev_hdl *ndev; 648 645 struct amdxdna_dev *xdna; 649 646 int idx; 650 647 651 648 xdna = hwctx->client->xdna; 649 + ndev = xdna->dev_handle; 650 + ndev->hwctx_num--; 652 651 drm_sched_wqueue_stop(&hwctx->priv->sched); 653 652 654 653 /* Now, scheduler will not send command to device. */ ··· 690 683 int ret; 691 684 692 685 XDNA_DBG(xdna, "Config %d CU to %s", config->num_cus, hwctx->name); 686 + if (XDNA_MBZ_DBG(xdna, config->pad, sizeof(config->pad))) 687 + return -EINVAL; 688 + 693 689 if (hwctx->status != HWCTX_STAT_INIT) { 694 690 XDNA_ERR(xdna, "Not support re-config CU"); 695 691 return -EINVAL;
+14 -29
drivers/accel/amdxdna/aie2_message.c
··· 14 14 #include <linux/errno.h> 15 15 #include <linux/pci.h> 16 16 #include <linux/types.h> 17 + #include <linux/xarray.h> 17 18 18 19 #include "aie2_msg_priv.h" 19 20 #include "aie2_pci.h" ··· 71 70 int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value) 72 71 { 73 72 DECLARE_AIE2_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG); 73 + int ret; 74 74 75 75 req.type = type; 76 76 req.value = value; 77 77 78 - return aie2_send_mgmt_msg_wait(ndev, &msg); 78 + ret = aie2_send_mgmt_msg_wait(ndev, &msg); 79 + if (ret) { 80 + XDNA_ERR(ndev->xdna, "Failed to set runtime config, ret %d", ret); 81 + return ret; 82 + } 83 + 84 + return 0; 79 85 } 80 86 81 87 int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value) ··· 98 90 } 99 91 100 92 *value = resp.value; 101 - return 0; 102 - } 103 - 104 - int aie2_check_protocol_version(struct amdxdna_dev_hdl *ndev) 105 - { 106 - DECLARE_AIE2_MSG(protocol_version, MSG_OP_GET_PROTOCOL_VERSION); 107 - struct amdxdna_dev *xdna = ndev->xdna; 108 - int ret; 109 - 110 - ret = aie2_send_mgmt_msg_wait(ndev, &msg); 111 - if (ret) { 112 - XDNA_ERR(xdna, "Failed to get protocol version, ret %d", ret); 113 - return ret; 114 - } 115 - 116 - if (resp.major != ndev->priv->protocol_major) { 117 - XDNA_ERR(xdna, "Incompatible firmware protocol version major %d minor %d", 118 - resp.major, resp.minor); 119 - return -EINVAL; 120 - } 121 - 122 - if (resp.minor < ndev->priv->protocol_minor) { 123 - XDNA_ERR(xdna, "Firmware minor version smaller than supported"); 124 - return -EINVAL; 125 - } 126 - 127 93 return 0; 128 94 } 129 95 ··· 297 315 struct amdxdna_dev *xdna = ndev->xdna; 298 316 struct amdxdna_client *client; 299 317 struct amdxdna_hwctx *hwctx; 318 + unsigned long hwctx_id; 300 319 dma_addr_t dma_addr; 301 320 u32 aie_bitmap = 0; 302 321 u8 *buff_addr; 303 - int next = 0; 304 322 int ret, idx; 305 323 306 324 buff_addr = dma_alloc_noncoherent(xdna->ddev.dev, size, &dma_addr, ··· 311 329 /* Go through each hardware context and mark the AIE columns that are active */ 312 330 list_for_each_entry(client, &xdna->client_list, node) { 313 331 idx = srcu_read_lock(&client->hwctx_srcu); 314 - idr_for_each_entry_continue(&client->hwctx_idr, hwctx, next) 332 + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) 315 333 aie_bitmap |= amdxdna_hwctx_col_map(hwctx); 316 334 srcu_read_unlock(&client->hwctx_srcu, idx); 317 335 } ··· 394 412 395 413 for (i = 0; i < hwctx->cus->num_cus; i++) { 396 414 struct amdxdna_cu_config *cu = &hwctx->cus->cu_configs[i]; 415 + 416 + if (XDNA_MBZ_DBG(xdna, cu->pad, sizeof(cu->pad))) 417 + return -EINVAL; 397 418 398 419 gobj = drm_gem_object_lookup(hwctx->client->filp, cu->cu_bo); 399 420 if (!gobj) {
+211 -45
drivers/accel/amdxdna/aie2_pci.c
··· 15 15 #include <linux/iommu.h> 16 16 #include <linux/iopoll.h> 17 17 #include <linux/pci.h> 18 + #include <linux/xarray.h> 18 19 19 20 #include "aie2_msg_priv.h" 20 21 #include "aie2_pci.h" ··· 34 33 * The related register and ring buffer information is on SRAM BAR. 35 34 * This struct is the register layout. 36 35 */ 36 + #define MGMT_MBOX_MAGIC 0x55504e5f /* _NPU */ 37 37 struct mgmt_mbox_chann_info { 38 - u32 x2i_tail; 39 - u32 x2i_head; 40 - u32 x2i_buf; 41 - u32 x2i_buf_sz; 42 - u32 i2x_tail; 43 - u32 i2x_head; 44 - u32 i2x_buf; 45 - u32 i2x_buf_sz; 38 + __u32 x2i_tail; 39 + __u32 x2i_head; 40 + __u32 x2i_buf; 41 + __u32 x2i_buf_sz; 42 + __u32 i2x_tail; 43 + __u32 i2x_head; 44 + __u32 i2x_buf; 45 + __u32 i2x_buf_sz; 46 + __u32 magic; 47 + __u32 msi_id; 48 + __u32 prot_major; 49 + __u32 prot_minor; 50 + __u32 rsvd[4]; 46 51 }; 52 + 53 + static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 fw_minor) 54 + { 55 + struct amdxdna_dev *xdna = ndev->xdna; 56 + 57 + /* 58 + * The driver supported mailbox behavior is defined by 59 + * ndev->priv->protocol_major and protocol_minor. 60 + * 61 + * When protocol_major and fw_major are different, it means driver 62 + * and firmware are incompatible. 63 + */ 64 + if (ndev->priv->protocol_major != fw_major) { 65 + XDNA_ERR(xdna, "Incompatible firmware protocol major %d minor %d", 66 + fw_major, fw_minor); 67 + return -EINVAL; 68 + } 69 + 70 + /* 71 + * When protocol_minor is greater then fw_minor, that means driver 72 + * relies on operation the installed firmware does not support. 73 + */ 74 + if (ndev->priv->protocol_minor > fw_minor) { 75 + XDNA_ERR(xdna, "Firmware minor version smaller than supported"); 76 + return -EINVAL; 77 + } 78 + return 0; 79 + } 47 80 48 81 static void aie2_dump_chann_info_debug(struct amdxdna_dev_hdl *ndev) 49 82 { ··· 92 57 XDNA_DBG(xdna, "x2i ringbuf 0x%x", ndev->mgmt_x2i.rb_start_addr); 93 58 XDNA_DBG(xdna, "x2i rsize 0x%x", ndev->mgmt_x2i.rb_size); 94 59 XDNA_DBG(xdna, "x2i chann index 0x%x", ndev->mgmt_chan_idx); 60 + XDNA_DBG(xdna, "mailbox protocol major 0x%x", ndev->mgmt_prot_major); 61 + XDNA_DBG(xdna, "mailbox protocol minor 0x%x", ndev->mgmt_prot_minor); 95 62 } 96 63 97 64 static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev) ··· 124 87 for (i = 0; i < sizeof(info_regs) / sizeof(u32); i++) 125 88 reg[i] = readl(ndev->sram_base + off + i * sizeof(u32)); 126 89 90 + if (info_regs.magic != MGMT_MBOX_MAGIC) { 91 + XDNA_ERR(ndev->xdna, "Invalid mbox magic 0x%x", info_regs.magic); 92 + ret = -EINVAL; 93 + goto done; 94 + } 95 + 127 96 i2x = &ndev->mgmt_i2x; 128 97 x2i = &ndev->mgmt_x2i; 129 98 ··· 142 99 x2i->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.x2i_tail); 143 100 x2i->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.x2i_buf); 144 101 x2i->rb_size = info_regs.x2i_buf_sz; 145 - ndev->mgmt_chan_idx = CHANN_INDEX(ndev, x2i->rb_start_addr); 146 102 103 + ndev->mgmt_chan_idx = info_regs.msi_id; 104 + ndev->mgmt_prot_major = info_regs.prot_major; 105 + ndev->mgmt_prot_minor = info_regs.prot_minor; 106 + 107 + ret = aie2_check_protocol(ndev, ndev->mgmt_prot_major, ndev->mgmt_prot_minor); 108 + 109 + done: 147 110 aie2_dump_chann_info_debug(ndev); 148 111 149 112 /* Must clear address at FW_ALIVE_OFF */ 150 113 writel(0, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF)); 151 114 152 - return 0; 115 + return ret; 153 116 } 154 117 155 - static int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev) 118 + int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev, 119 + enum rt_config_category category, u32 *val) 156 120 { 157 - const struct rt_config *cfg = &ndev->priv->rt_config; 158 - u64 value; 121 + const struct rt_config *cfg; 122 + u32 value; 159 123 int ret; 160 124 161 - ret = aie2_set_runtime_cfg(ndev, cfg->type, cfg->value); 162 - if (ret) { 163 - XDNA_ERR(ndev->xdna, "Set runtime type %d value %d failed", 164 - cfg->type, cfg->value); 165 - return ret; 166 - } 125 + for (cfg = ndev->priv->rt_config; cfg->type; cfg++) { 126 + if (cfg->category != category) 127 + continue; 167 128 168 - ret = aie2_get_runtime_cfg(ndev, cfg->type, &value); 169 - if (ret) { 170 - XDNA_ERR(ndev->xdna, "Get runtime cfg failed"); 171 - return ret; 129 + value = val ? *val : cfg->value; 130 + ret = aie2_set_runtime_cfg(ndev, cfg->type, value); 131 + if (ret) { 132 + XDNA_ERR(ndev->xdna, "Set type %d value %d failed", 133 + cfg->type, value); 134 + return ret; 135 + } 172 136 } 173 - 174 - if (value != cfg->value) 175 - return -EINVAL; 176 137 177 138 return 0; 178 139 } ··· 204 157 { 205 158 int ret; 206 159 207 - ret = aie2_check_protocol_version(ndev); 208 - if (ret) { 209 - XDNA_ERR(ndev->xdna, "Check header hash failed"); 210 - return ret; 211 - } 212 - 213 - ret = aie2_runtime_cfg(ndev); 160 + ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_INIT, NULL); 214 161 if (ret) { 215 162 XDNA_ERR(ndev->xdna, "Runtime config failed"); 216 163 return ret; ··· 298 257 return ret; 299 258 } 300 259 260 + static int aie2_xrs_set_dft_dpm_level(struct drm_device *ddev, u32 dpm_level) 261 + { 262 + struct amdxdna_dev *xdna = to_xdna_dev(ddev); 263 + struct amdxdna_dev_hdl *ndev; 264 + 265 + drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); 266 + 267 + ndev = xdna->dev_handle; 268 + ndev->dft_dpm_level = dpm_level; 269 + if (ndev->pw_mode != POWER_MODE_DEFAULT || ndev->dpm_level == dpm_level) 270 + return 0; 271 + 272 + return ndev->priv->hw_ops.set_dpm(ndev, dpm_level); 273 + } 274 + 301 275 static struct xrs_action_ops aie2_xrs_actions = { 302 276 .load = aie2_xrs_load, 303 277 .unload = aie2_xrs_unload, 278 + .set_dft_dpm_level = aie2_xrs_set_dft_dpm_level, 304 279 }; 305 280 306 281 static void aie2_hw_stop(struct amdxdna_dev *xdna) ··· 324 267 struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev); 325 268 struct amdxdna_dev_hdl *ndev = xdna->dev_handle; 326 269 270 + if (ndev->dev_status <= AIE2_DEV_INIT) { 271 + XDNA_ERR(xdna, "device is already stopped"); 272 + return; 273 + } 274 + 327 275 aie2_mgmt_fw_fini(ndev); 328 276 xdna_mailbox_stop_channel(ndev->mgmt_chann); 329 277 xdna_mailbox_destroy_channel(ndev->mgmt_chann); 278 + ndev->mgmt_chann = NULL; 279 + drmm_kfree(&xdna->ddev, ndev->mbox); 280 + ndev->mbox = NULL; 330 281 aie2_psp_stop(ndev->psp_hdl); 331 282 aie2_smu_fini(ndev); 332 283 pci_disable_device(pdev); 284 + 285 + ndev->dev_status = AIE2_DEV_INIT; 333 286 } 334 287 335 288 static int aie2_hw_start(struct amdxdna_dev *xdna) ··· 349 282 struct xdna_mailbox_res mbox_res; 350 283 u32 xdna_mailbox_intr_reg; 351 284 int mgmt_mb_irq, ret; 285 + 286 + if (ndev->dev_status >= AIE2_DEV_START) { 287 + XDNA_INFO(xdna, "device is already started"); 288 + return 0; 289 + } 352 290 353 291 ret = pci_enable_device(pdev); 354 292 if (ret) { ··· 411 339 goto stop_psp; 412 340 } 413 341 342 + ret = aie2_pm_init(ndev); 343 + if (ret) { 344 + XDNA_ERR(xdna, "failed to init pm, ret %d", ret); 345 + goto destroy_mgmt_chann; 346 + } 347 + 414 348 ret = aie2_mgmt_fw_init(ndev); 415 349 if (ret) { 416 350 XDNA_ERR(xdna, "initial mgmt firmware failed, ret %d", ret); 417 351 goto destroy_mgmt_chann; 418 352 } 353 + 354 + ndev->dev_status = AIE2_DEV_START; 419 355 420 356 return 0; 421 357 ··· 543 463 } 544 464 ndev->total_col = min(aie2_max_col, ndev->metadata.cols); 545 465 546 - xrs_cfg.clk_list.num_levels = 3; 547 - xrs_cfg.clk_list.cu_clk_list[0] = 0; 548 - xrs_cfg.clk_list.cu_clk_list[1] = 800; 549 - xrs_cfg.clk_list.cu_clk_list[2] = 1000; 466 + xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1; 467 + for (i = 0; i < xrs_cfg.clk_list.num_levels; i++) 468 + xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk; 550 469 xrs_cfg.sys_eff_factor = 1; 551 470 xrs_cfg.ddev = &xdna->ddev; 552 471 xrs_cfg.actions = &aie2_xrs_actions; ··· 702 623 return 0; 703 624 } 704 625 626 + static int aie2_get_firmware_version(struct amdxdna_client *client, 627 + struct amdxdna_drm_get_info *args) 628 + { 629 + struct amdxdna_drm_query_firmware_version version; 630 + struct amdxdna_dev *xdna = client->xdna; 631 + 632 + version.major = xdna->fw_ver.major; 633 + version.minor = xdna->fw_ver.minor; 634 + version.patch = xdna->fw_ver.sub; 635 + version.build = xdna->fw_ver.build; 636 + 637 + if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version))) 638 + return -EFAULT; 639 + 640 + return 0; 641 + } 642 + 643 + static int aie2_get_power_mode(struct amdxdna_client *client, 644 + struct amdxdna_drm_get_info *args) 645 + { 646 + struct amdxdna_drm_get_power_mode mode = {}; 647 + struct amdxdna_dev *xdna = client->xdna; 648 + struct amdxdna_dev_hdl *ndev; 649 + 650 + ndev = xdna->dev_handle; 651 + mode.power_mode = ndev->pw_mode; 652 + 653 + if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, sizeof(mode))) 654 + return -EFAULT; 655 + 656 + return 0; 657 + } 658 + 705 659 static int aie2_get_clock_metadata(struct amdxdna_client *client, 706 660 struct amdxdna_drm_get_info *args) 707 661 { ··· 748 636 if (!clock) 749 637 return -ENOMEM; 750 638 751 - memcpy(clock->mp_npu_clock.name, ndev->mp_npu_clock.name, 752 - sizeof(clock->mp_npu_clock.name)); 753 - clock->mp_npu_clock.freq_mhz = ndev->mp_npu_clock.freq_mhz; 754 - memcpy(clock->h_clock.name, ndev->h_clock.name, sizeof(clock->h_clock.name)); 755 - clock->h_clock.freq_mhz = ndev->h_clock.freq_mhz; 639 + snprintf(clock->mp_npu_clock.name, sizeof(clock->mp_npu_clock.name), 640 + "MP-NPU Clock"); 641 + clock->mp_npu_clock.freq_mhz = ndev->npuclk_freq; 642 + snprintf(clock->h_clock.name, sizeof(clock->h_clock.name), "H Clock"); 643 + clock->h_clock.freq_mhz = ndev->hclk_freq; 756 644 757 645 if (copy_to_user(u64_to_user_ptr(args->buffer), clock, sizeof(*clock))) 758 646 ret = -EFAULT; ··· 769 657 struct amdxdna_drm_query_hwctx *tmp; 770 658 struct amdxdna_client *tmp_client; 771 659 struct amdxdna_hwctx *hwctx; 660 + unsigned long hwctx_id; 772 661 bool overflow = false; 773 662 u32 req_bytes = 0; 774 663 u32 hw_i = 0; 775 664 int ret = 0; 776 - int next; 777 665 int idx; 778 666 779 667 drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); ··· 785 673 buf = u64_to_user_ptr(args->buffer); 786 674 list_for_each_entry(tmp_client, &xdna->client_list, node) { 787 675 idx = srcu_read_lock(&tmp_client->hwctx_srcu); 788 - next = 0; 789 - idr_for_each_entry_continue(&tmp_client->hwctx_idr, hwctx, next) { 676 + amdxdna_for_each_hwctx(tmp_client, hwctx_id, hwctx) { 790 677 req_bytes += sizeof(*tmp); 791 678 if (args->buffer_size < req_bytes) { 792 679 /* Continue iterating to get the required size */ ··· 847 736 case DRM_AMDXDNA_QUERY_HW_CONTEXTS: 848 737 ret = aie2_get_hwctx_status(client, args); 849 738 break; 739 + case DRM_AMDXDNA_QUERY_FIRMWARE_VERSION: 740 + ret = aie2_get_firmware_version(client, args); 741 + break; 742 + case DRM_AMDXDNA_GET_POWER_MODE: 743 + ret = aie2_get_power_mode(client, args); 744 + break; 850 745 default: 851 746 XDNA_ERR(xdna, "Not supported request parameter %u", args->param); 852 747 ret = -EOPNOTSUPP; 853 748 } 854 749 XDNA_DBG(xdna, "Got param %d", args->param); 750 + 751 + drm_dev_exit(idx); 752 + return ret; 753 + } 754 + 755 + static int aie2_set_power_mode(struct amdxdna_client *client, 756 + struct amdxdna_drm_set_state *args) 757 + { 758 + struct amdxdna_drm_set_power_mode power_state; 759 + enum amdxdna_power_mode_type power_mode; 760 + struct amdxdna_dev *xdna = client->xdna; 761 + 762 + if (copy_from_user(&power_state, u64_to_user_ptr(args->buffer), 763 + sizeof(power_state))) { 764 + XDNA_ERR(xdna, "Failed to copy power mode request into kernel"); 765 + return -EFAULT; 766 + } 767 + 768 + if (XDNA_MBZ_DBG(xdna, power_state.pad, sizeof(power_state.pad))) 769 + return -EINVAL; 770 + 771 + power_mode = power_state.power_mode; 772 + if (power_mode > POWER_MODE_TURBO) { 773 + XDNA_ERR(xdna, "Invalid power mode %d", power_mode); 774 + return -EINVAL; 775 + } 776 + 777 + return aie2_pm_set_mode(xdna->dev_handle, power_mode); 778 + } 779 + 780 + static int aie2_set_state(struct amdxdna_client *client, 781 + struct amdxdna_drm_set_state *args) 782 + { 783 + struct amdxdna_dev *xdna = client->xdna; 784 + int ret, idx; 785 + 786 + if (!drm_dev_enter(&xdna->ddev, &idx)) 787 + return -ENODEV; 788 + 789 + switch (args->param) { 790 + case DRM_AMDXDNA_SET_POWER_MODE: 791 + ret = aie2_set_power_mode(client, args); 792 + break; 793 + default: 794 + XDNA_ERR(xdna, "Not supported request parameter %u", args->param); 795 + ret = -EOPNOTSUPP; 796 + break; 797 + } 855 798 856 799 drm_dev_exit(idx); 857 800 return ret; ··· 917 752 .resume = aie2_hw_start, 918 753 .suspend = aie2_hw_stop, 919 754 .get_aie_info = aie2_get_info, 755 + .set_aie_state = aie2_set_state, 920 756 .hwctx_init = aie2_hwctx_init, 921 757 .hwctx_fini = aie2_hwctx_fini, 922 758 .hwctx_config = aie2_hwctx_config,
+53 -15
drivers/accel/amdxdna/aie2_pci.h
··· 6 6 #ifndef _AIE2_PCI_H_ 7 7 #define _AIE2_PCI_H_ 8 8 9 + #include <drm/amdxdna_accel.h> 9 10 #include <linux/semaphore.h> 10 11 11 12 #include "amdxdna_mailbox.h" ··· 39 38 }) 40 39 41 40 #define CHAN_SLOT_SZ SZ_8K 42 - #define CHANN_INDEX(ndev, rbuf_off) \ 43 - (((rbuf_off) - SRAM_REG_OFF((ndev), MBOX_CHANN_OFF)) / CHAN_SLOT_SZ) 44 - 45 41 #define MBOX_SIZE(ndev) \ 46 42 ({ \ 47 43 typeof(ndev) _ndev = (ndev); \ 48 44 ((_ndev)->priv->mbox_size) ? (_ndev)->priv->mbox_size : \ 49 45 pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->xdna->dev_info->mbox_bar); \ 50 46 }) 51 - 52 - #define SMU_MPNPUCLK_FREQ_MAX(ndev) ((ndev)->priv->smu_mpnpuclk_freq_max) 53 - #define SMU_HCLK_FREQ_MAX(ndev) ((ndev)->priv->smu_hclk_freq_max) 54 47 55 48 enum aie2_smu_reg_idx { 56 49 SMU_CMD_REG = 0, ··· 107 112 struct aie_tile_metadata shim; 108 113 }; 109 114 110 - struct clock_entry { 111 - char name[16]; 112 - u32 freq_mhz; 115 + enum rt_config_category { 116 + AIE2_RT_CFG_INIT, 117 + AIE2_RT_CFG_CLK_GATING, 113 118 }; 114 119 115 120 struct rt_config { 116 121 u32 type; 117 122 u32 value; 123 + u32 category; 124 + }; 125 + 126 + struct dpm_clk_freq { 127 + u32 npuclk; 128 + u32 hclk; 118 129 }; 119 130 120 131 /* ··· 150 149 struct drm_syncobj *syncobj; 151 150 }; 152 151 152 + enum aie2_dev_status { 153 + AIE2_DEV_UNINIT, 154 + AIE2_DEV_INIT, 155 + AIE2_DEV_START, 156 + }; 157 + 153 158 struct amdxdna_dev_hdl { 154 159 struct amdxdna_dev *xdna; 155 160 const struct amdxdna_dev_priv *priv; ··· 167 160 struct xdna_mailbox_chann_res mgmt_x2i; 168 161 struct xdna_mailbox_chann_res mgmt_i2x; 169 162 u32 mgmt_chan_idx; 163 + u32 mgmt_prot_major; 164 + u32 mgmt_prot_minor; 170 165 171 166 u32 total_col; 172 167 struct aie_version version; 173 168 struct aie_metadata metadata; 174 - struct clock_entry mp_npu_clock; 175 - struct clock_entry h_clock; 169 + 170 + /* power management and clock*/ 171 + enum amdxdna_power_mode_type pw_mode; 172 + u32 dpm_level; 173 + u32 dft_dpm_level; 174 + u32 max_dpm_level; 175 + u32 clk_gating; 176 + u32 npuclk_freq; 177 + u32 hclk_freq; 176 178 177 179 /* Mailbox and the management channel */ 178 180 struct mailbox *mbox; 179 181 struct mailbox_channel *mgmt_chann; 180 182 struct async_events *async_events; 183 + 184 + enum aie2_dev_status dev_status; 185 + u32 hwctx_num; 181 186 }; 182 187 183 188 #define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \ ··· 200 181 u32 offset; 201 182 }; 202 183 184 + struct aie2_hw_ops { 185 + int (*set_dpm)(struct amdxdna_dev_hdl *ndev, u32 dpm_level); 186 + }; 187 + 203 188 struct amdxdna_dev_priv { 204 189 const char *fw_path; 205 190 u64 protocol_major; 206 191 u64 protocol_minor; 207 - struct rt_config rt_config; 192 + const struct rt_config *rt_config; 193 + const struct dpm_clk_freq *dpm_clk_tbl; 194 + 208 195 #define COL_ALIGN_NONE 0 209 196 #define COL_ALIGN_NATURE 1 210 197 u32 col_align; ··· 221 196 struct aie2_bar_off_pair sram_offs[SRAM_MAX_INDEX]; 222 197 struct aie2_bar_off_pair psp_regs_off[PSP_MAX_REGS]; 223 198 struct aie2_bar_off_pair smu_regs_off[SMU_MAX_REGS]; 224 - u32 smu_mpnpuclk_freq_max; 225 - u32 smu_hclk_freq_max; 199 + struct aie2_hw_ops hw_ops; 226 200 }; 227 201 228 202 extern const struct amdxdna_dev_ops aie2_ops; 229 203 204 + int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev, 205 + enum rt_config_category category, u32 *val); 206 + 207 + /* aie2 npu hw config */ 208 + extern const struct dpm_clk_freq npu1_dpm_clk_table[]; 209 + extern const struct dpm_clk_freq npu4_dpm_clk_table[]; 210 + extern const struct rt_config npu1_default_rt_cfg[]; 211 + extern const struct rt_config npu4_default_rt_cfg[]; 212 + 230 213 /* aie2_smu.c */ 231 214 int aie2_smu_init(struct amdxdna_dev_hdl *ndev); 232 215 void aie2_smu_fini(struct amdxdna_dev_hdl *ndev); 216 + int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level); 217 + int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level); 218 + 219 + /* aie2_pm.c */ 220 + int aie2_pm_init(struct amdxdna_dev_hdl *ndev); 221 + int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target); 233 222 234 223 /* aie2_psp.c */ 235 224 struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf); ··· 261 222 int aie2_resume_fw(struct amdxdna_dev_hdl *ndev); 262 223 int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value); 263 224 int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value); 264 - int aie2_check_protocol_version(struct amdxdna_dev_hdl *ndev); 265 225 int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid); 266 226 int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *version); 267 227 int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata);
+108
drivers/accel/amdxdna/aie2_pm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024, Advanced Micro Devices, Inc. 4 + */ 5 + 6 + #include <drm/amdxdna_accel.h> 7 + #include <drm/drm_device.h> 8 + #include <drm/drm_print.h> 9 + #include <drm/gpu_scheduler.h> 10 + 11 + #include "aie2_pci.h" 12 + #include "amdxdna_pci_drv.h" 13 + 14 + #define AIE2_CLK_GATING_ENABLE 1 15 + #define AIE2_CLK_GATING_DISABLE 0 16 + 17 + static int aie2_pm_set_clk_gating(struct amdxdna_dev_hdl *ndev, u32 val) 18 + { 19 + int ret; 20 + 21 + ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_CLK_GATING, &val); 22 + if (ret) 23 + return ret; 24 + 25 + ndev->clk_gating = val; 26 + return 0; 27 + } 28 + 29 + int aie2_pm_init(struct amdxdna_dev_hdl *ndev) 30 + { 31 + int ret; 32 + 33 + if (ndev->dev_status != AIE2_DEV_UNINIT) { 34 + /* Resume device */ 35 + ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->dpm_level); 36 + if (ret) 37 + return ret; 38 + 39 + ret = aie2_pm_set_clk_gating(ndev, ndev->clk_gating); 40 + if (ret) 41 + return ret; 42 + 43 + return 0; 44 + } 45 + 46 + while (ndev->priv->dpm_clk_tbl[ndev->max_dpm_level].hclk) 47 + ndev->max_dpm_level++; 48 + ndev->max_dpm_level--; 49 + 50 + ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->max_dpm_level); 51 + if (ret) 52 + return ret; 53 + 54 + ret = aie2_pm_set_clk_gating(ndev, AIE2_CLK_GATING_ENABLE); 55 + if (ret) 56 + return ret; 57 + 58 + ndev->pw_mode = POWER_MODE_DEFAULT; 59 + ndev->dft_dpm_level = ndev->max_dpm_level; 60 + 61 + return 0; 62 + } 63 + 64 + int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target) 65 + { 66 + struct amdxdna_dev *xdna = ndev->xdna; 67 + u32 clk_gating, dpm_level; 68 + int ret; 69 + 70 + drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); 71 + 72 + if (ndev->pw_mode == target) 73 + return 0; 74 + 75 + switch (target) { 76 + case POWER_MODE_TURBO: 77 + if (ndev->hwctx_num) { 78 + XDNA_ERR(xdna, "Can not set turbo when there is active hwctx"); 79 + return -EINVAL; 80 + } 81 + 82 + clk_gating = AIE2_CLK_GATING_DISABLE; 83 + dpm_level = ndev->max_dpm_level; 84 + break; 85 + case POWER_MODE_HIGH: 86 + clk_gating = AIE2_CLK_GATING_ENABLE; 87 + dpm_level = ndev->max_dpm_level; 88 + break; 89 + case POWER_MODE_DEFAULT: 90 + clk_gating = AIE2_CLK_GATING_ENABLE; 91 + dpm_level = ndev->dft_dpm_level; 92 + break; 93 + default: 94 + return -EOPNOTSUPP; 95 + } 96 + 97 + ret = ndev->priv->hw_ops.set_dpm(ndev, dpm_level); 98 + if (ret) 99 + return ret; 100 + 101 + ret = aie2_pm_set_clk_gating(ndev, clk_gating); 102 + if (ret) 103 + return ret; 104 + 105 + ndev->pw_mode = target; 106 + 107 + return 0; 108 + }
+50 -35
drivers/accel/amdxdna/aie2_smu.c
··· 19 19 #define AIE2_SMU_POWER_OFF 0x4 20 20 #define AIE2_SMU_SET_MPNPUCLK_FREQ 0x5 21 21 #define AIE2_SMU_SET_HCLK_FREQ 0x6 22 + #define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7 23 + #define AIE2_SMU_SET_HARD_DPMLEVEL 0x8 22 24 23 - static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd, u32 reg_arg) 25 + static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd, 26 + u32 reg_arg, u32 *out) 24 27 { 25 28 u32 resp; 26 29 int ret; ··· 43 40 return ret; 44 41 } 45 42 43 + if (out) 44 + *out = readl(SMU_REG(ndev, SMU_OUT_REG)); 45 + 46 46 if (resp != SMU_RESULT_OK) { 47 47 XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp); 48 48 return -EINVAL; ··· 54 48 return 0; 55 49 } 56 50 57 - static int aie2_smu_set_mpnpu_clock_freq(struct amdxdna_dev_hdl *ndev, u32 freq_mhz) 51 + int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) 58 52 { 53 + u32 freq; 59 54 int ret; 60 55 61 - if (!freq_mhz || freq_mhz > SMU_MPNPUCLK_FREQ_MAX(ndev)) { 62 - XDNA_ERR(ndev->xdna, "invalid mpnpu clock freq %d", freq_mhz); 63 - return -EINVAL; 56 + ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ, 57 + ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq); 58 + if (ret) { 59 + XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n", 60 + ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret); 64 61 } 62 + ndev->npuclk_freq = freq; 65 63 66 - ndev->mp_npu_clock.freq_mhz = freq_mhz; 67 - ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ, freq_mhz); 68 - if (!ret) 69 - XDNA_INFO_ONCE(ndev->xdna, "set mpnpu_clock = %d mhz", freq_mhz); 64 + ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ, 65 + ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq); 66 + if (ret) { 67 + XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n", 68 + ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret); 69 + } 70 + ndev->hclk_freq = freq; 71 + ndev->dpm_level = dpm_level; 70 72 71 - return ret; 73 + XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", 74 + ndev->npuclk_freq, ndev->hclk_freq); 75 + 76 + return 0; 72 77 } 73 78 74 - static int aie2_smu_set_hclock_freq(struct amdxdna_dev_hdl *ndev, u32 freq_mhz) 79 + int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) 75 80 { 76 81 int ret; 77 82 78 - if (!freq_mhz || freq_mhz > SMU_HCLK_FREQ_MAX(ndev)) { 79 - XDNA_ERR(ndev->xdna, "invalid hclock freq %d", freq_mhz); 80 - return -EINVAL; 83 + ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL); 84 + if (ret) { 85 + XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ", 86 + dpm_level, ret); 87 + return ret; 81 88 } 82 89 83 - ndev->h_clock.freq_mhz = freq_mhz; 84 - ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ, freq_mhz); 85 - if (!ret) 86 - XDNA_INFO_ONCE(ndev->xdna, "set npu_hclock = %d mhz", freq_mhz); 90 + ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL); 91 + if (ret) { 92 + XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d", 93 + dpm_level, ret); 94 + return ret; 95 + } 87 96 88 - return ret; 97 + ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk; 98 + ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk; 99 + ndev->dpm_level = dpm_level; 100 + 101 + XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n", 102 + ndev->npuclk_freq, ndev->hclk_freq); 103 + 104 + return 0; 89 105 } 90 106 91 107 int aie2_smu_init(struct amdxdna_dev_hdl *ndev) 92 108 { 93 109 int ret; 94 110 95 - ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0); 111 + ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL); 96 112 if (ret) { 97 113 XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret); 98 114 return ret; 99 115 } 100 - 101 - ret = aie2_smu_set_mpnpu_clock_freq(ndev, SMU_MPNPUCLK_FREQ_MAX(ndev)); 102 - if (ret) { 103 - XDNA_ERR(ndev->xdna, "Set mpnpu clk freq failed, ret %d", ret); 104 - return ret; 105 - } 106 - snprintf(ndev->mp_npu_clock.name, sizeof(ndev->mp_npu_clock.name), "MP-NPU Clock"); 107 - 108 - ret = aie2_smu_set_hclock_freq(ndev, SMU_HCLK_FREQ_MAX(ndev)); 109 - if (ret) { 110 - XDNA_ERR(ndev->xdna, "Set hclk freq failed, ret %d", ret); 111 - return ret; 112 - } 113 - snprintf(ndev->h_clock.name, sizeof(ndev->h_clock.name), "H Clock"); 114 116 115 117 return 0; 116 118 } ··· 127 113 { 128 114 int ret; 129 115 130 - ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0); 116 + ndev->priv->hw_ops.set_dpm(ndev, 0); 117 + ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL); 131 118 if (ret) 132 119 XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret); 133 120 }
+55 -5
drivers/accel/amdxdna/aie2_solver.c
··· 8 8 #include <drm/drm_print.h> 9 9 #include <linux/bitops.h> 10 10 #include <linux/bitmap.h> 11 + #include <linux/slab.h> 11 12 12 13 #include "aie2_solver.h" 13 14 ··· 26 25 27 26 struct partition_node *pt_node; 28 27 void *cb_arg; 28 + u32 dpm_level; 29 29 u32 cols_len; 30 30 u32 start_cols[] __counted_by(cols_len); 31 31 }; ··· 97 95 return 0; 98 96 } 99 97 98 + static bool is_valid_qos_dpm_params(struct aie_qos *rqos) 99 + { 100 + /* 101 + * gops is retrieved from the xmodel, so it's always set 102 + * fps and latency are the configurable params from the application 103 + */ 104 + if (rqos->gops > 0 && (rqos->fps > 0 || rqos->latency > 0)) 105 + return true; 106 + 107 + return false; 108 + } 109 + 110 + static int set_dpm_level(struct solver_state *xrs, struct alloc_requests *req, u32 *dpm_level) 111 + { 112 + struct solver_rgroup *rgp = &xrs->rgp; 113 + struct cdo_parts *cdop = &req->cdo; 114 + struct aie_qos *rqos = &req->rqos; 115 + u32 freq, max_dpm_level, level; 116 + struct solver_node *node; 117 + 118 + max_dpm_level = xrs->cfg.clk_list.num_levels - 1; 119 + /* If no QoS parameters are passed, set it to the max DPM level */ 120 + if (!is_valid_qos_dpm_params(rqos)) { 121 + level = max_dpm_level; 122 + goto set_dpm; 123 + } 124 + 125 + /* Find one CDO group that meet the GOPs requirement. */ 126 + for (level = 0; level < max_dpm_level; level++) { 127 + freq = xrs->cfg.clk_list.cu_clk_list[level]; 128 + if (!qos_meet(xrs, rqos, cdop->qos_cap.opc * freq / 1000)) 129 + break; 130 + } 131 + 132 + /* set the dpm level which fits all the sessions */ 133 + list_for_each_entry(node, &rgp->node_list, list) { 134 + if (node->dpm_level > level) 135 + level = node->dpm_level; 136 + } 137 + 138 + set_dpm: 139 + *dpm_level = level; 140 + return xrs->cfg.actions->set_dft_dpm_level(xrs->cfg.ddev, level); 141 + } 142 + 100 143 static struct solver_node *rg_search_node(struct solver_rgroup *rgp, u64 rid) 101 144 { 102 145 struct solver_node *node; ··· 206 159 pt_node->ncols = ncols; 207 160 208 161 /* 209 - * Before fully support latency in QoS, if a request 210 - * specifies a non-zero latency value, it will not share 211 - * the partition with other requests. 162 + * Always set exclusive to false for now. 212 163 */ 213 - if (req->rqos.latency) 214 - pt_node->exclusive = true; 164 + pt_node->exclusive = false; 215 165 216 166 list_add_tail(&pt_node->list, &xrs->rgp.pt_node_list); 217 167 xrs->rgp.npartition_node++; ··· 301 257 struct xrs_action_load load_act; 302 258 struct solver_node *snode; 303 259 struct solver_state *xrs; 260 + u32 dpm_level; 304 261 int ret; 305 262 306 263 xrs = (struct solver_state *)hdl; ··· 326 281 if (ret) 327 282 goto free_node; 328 283 284 + ret = set_dpm_level(xrs, req, &dpm_level); 285 + if (ret) 286 + goto free_node; 287 + 288 + snode->dpm_level = dpm_level; 329 289 snode->cb_arg = cb_arg; 330 290 331 291 drm_dbg(xrs->cfg.ddev, "start col %d ncols %d\n",
+1
drivers/accel/amdxdna/aie2_solver.h
··· 99 99 struct xrs_action_ops { 100 100 int (*load)(void *cb_arg, struct xrs_action_load *action); 101 101 int (*unload)(void *cb_arg); 102 + int (*set_dft_dpm_level)(struct drm_device *ddev, u32 level); 102 103 }; 103 104 104 105 /*
+25 -28
drivers/accel/amdxdna/amdxdna_ctx.c
··· 11 11 #include <drm/drm_gem_shmem_helper.h> 12 12 #include <drm/drm_print.h> 13 13 #include <drm/gpu_scheduler.h> 14 + #include <linux/xarray.h> 14 15 #include <trace/events/amdxdna.h> 15 16 16 17 #include "amdxdna_ctx.h" ··· 64 63 { 65 64 struct amdxdna_dev *xdna = client->xdna; 66 65 struct amdxdna_hwctx *hwctx; 67 - int next = 0; 66 + unsigned long hwctx_id; 68 67 69 68 drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); 70 69 mutex_lock(&client->hwctx_lock); 71 - idr_for_each_entry_continue(&client->hwctx_idr, hwctx, next) 70 + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) 72 71 xdna->dev_info->ops->hwctx_suspend(hwctx); 73 72 mutex_unlock(&client->hwctx_lock); 74 73 } ··· 77 76 { 78 77 struct amdxdna_dev *xdna = client->xdna; 79 78 struct amdxdna_hwctx *hwctx; 80 - int next = 0; 79 + unsigned long hwctx_id; 81 80 82 81 drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock)); 83 82 mutex_lock(&client->hwctx_lock); 84 - idr_for_each_entry_continue(&client->hwctx_idr, hwctx, next) 83 + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) 85 84 xdna->dev_info->ops->hwctx_resume(hwctx); 86 85 mutex_unlock(&client->hwctx_lock); 87 86 } ··· 150 149 void amdxdna_hwctx_remove_all(struct amdxdna_client *client) 151 150 { 152 151 struct amdxdna_hwctx *hwctx; 153 - int next = 0; 152 + unsigned long hwctx_id; 154 153 155 154 mutex_lock(&client->hwctx_lock); 156 - idr_for_each_entry_continue(&client->hwctx_idr, hwctx, next) { 155 + amdxdna_for_each_hwctx(client, hwctx_id, hwctx) { 157 156 XDNA_DBG(client->xdna, "PID %d close HW context %d", 158 157 client->pid, hwctx->id); 159 - idr_remove(&client->hwctx_idr, hwctx->id); 158 + xa_erase(&client->hwctx_xa, hwctx->id); 160 159 mutex_unlock(&client->hwctx_lock); 161 160 amdxdna_hwctx_destroy_rcu(hwctx, &client->hwctx_srcu); 162 161 mutex_lock(&client->hwctx_lock); ··· 195 194 hwctx->num_tiles = args->num_tiles; 196 195 hwctx->mem_size = args->mem_size; 197 196 hwctx->max_opc = args->max_opc; 198 - mutex_lock(&client->hwctx_lock); 199 - ret = idr_alloc_cyclic(&client->hwctx_idr, hwctx, 0, MAX_HWCTX_ID, GFP_KERNEL); 197 + ret = xa_alloc_cyclic(&client->hwctx_xa, &hwctx->id, hwctx, 198 + XA_LIMIT(AMDXDNA_INVALID_CTX_HANDLE + 1, MAX_HWCTX_ID), 199 + &client->next_hwctxid, GFP_KERNEL); 200 200 if (ret < 0) { 201 - mutex_unlock(&client->hwctx_lock); 202 201 XDNA_ERR(xdna, "Allocate hwctx ID failed, ret %d", ret); 203 202 goto free_hwctx; 204 203 } 205 - hwctx->id = ret; 206 - mutex_unlock(&client->hwctx_lock); 207 204 208 205 hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->id); 209 206 if (!hwctx->name) { ··· 227 228 free_name: 228 229 kfree(hwctx->name); 229 230 rm_id: 230 - mutex_lock(&client->hwctx_lock); 231 - idr_remove(&client->hwctx_idr, hwctx->id); 232 - mutex_unlock(&client->hwctx_lock); 231 + xa_erase(&client->hwctx_xa, hwctx->id); 233 232 free_hwctx: 234 233 kfree(hwctx); 235 234 exit: ··· 243 246 struct amdxdna_hwctx *hwctx; 244 247 int ret = 0, idx; 245 248 249 + if (XDNA_MBZ_DBG(xdna, &args->pad, sizeof(args->pad))) 250 + return -EINVAL; 251 + 246 252 if (!drm_dev_enter(dev, &idx)) 247 253 return -ENODEV; 248 254 249 - /* 250 - * Use hwctx_lock to achieve exclusion with other hwctx writers, 251 - * SRCU to synchronize with exec/wait command ioctls. 252 - * 253 - * The pushed jobs are handled by DRM scheduler during destroy. 254 - */ 255 - mutex_lock(&client->hwctx_lock); 256 - hwctx = idr_find(&client->hwctx_idr, args->handle); 255 + hwctx = xa_erase(&client->hwctx_xa, args->handle); 257 256 if (!hwctx) { 258 - mutex_unlock(&client->hwctx_lock); 259 257 ret = -EINVAL; 260 258 XDNA_DBG(xdna, "PID %d HW context %d not exist", 261 259 client->pid, args->handle); 262 260 goto out; 263 261 } 264 - idr_remove(&client->hwctx_idr, hwctx->id); 265 - mutex_unlock(&client->hwctx_lock); 266 262 263 + /* 264 + * The pushed jobs are handled by DRM scheduler during destroy. 265 + * SRCU to synchronize with exec command ioctls. 266 + */ 267 267 amdxdna_hwctx_destroy_rcu(hwctx, &client->hwctx_srcu); 268 268 269 269 XDNA_DBG(xdna, "PID %d destroyed HW context %d", client->pid, args->handle); ··· 279 285 u32 buf_size; 280 286 void *buf; 281 287 u64 val; 288 + 289 + if (XDNA_MBZ_DBG(xdna, &args->pad, sizeof(args->pad))) 290 + return -EINVAL; 282 291 283 292 if (!xdna->dev_info->ops->hwctx_config) 284 293 return -EOPNOTSUPP; ··· 321 324 322 325 mutex_lock(&xdna->dev_lock); 323 326 idx = srcu_read_lock(&client->hwctx_srcu); 324 - hwctx = idr_find(&client->hwctx_idr, args->handle); 327 + hwctx = xa_load(&client->hwctx_xa, args->handle); 325 328 if (!hwctx) { 326 329 XDNA_DBG(xdna, "PID %d failed to get hwctx %d", client->pid, args->handle); 327 330 ret = -EINVAL; ··· 433 436 } 434 437 435 438 idx = srcu_read_lock(&client->hwctx_srcu); 436 - hwctx = idr_find(&client->hwctx_idr, hwctx_hdl); 439 + hwctx = xa_load(&client->hwctx_xa, hwctx_hdl); 437 440 if (!hwctx) { 438 441 XDNA_DBG(xdna, "PID %d failed to get hwctx %d", 439 442 client->pid, hwctx_hdl);
+1 -1
drivers/accel/amdxdna/amdxdna_gem.c
··· 552 552 struct drm_gem_object *gobj; 553 553 int ret = 0; 554 554 555 - if (args->ext || args->ext_flags) 555 + if (args->ext || args->ext_flags || args->pad) 556 556 return -EINVAL; 557 557 558 558 gobj = drm_gem_object_lookup(filp, args->handle);
+23 -38
drivers/accel/amdxdna/amdxdna_mailbox.c
··· 6 6 #include <drm/drm_device.h> 7 7 #include <drm/drm_managed.h> 8 8 #include <linux/bitfield.h> 9 + #include <linux/interrupt.h> 9 10 #include <linux/iopoll.h> 11 + #include <linux/xarray.h> 10 12 11 13 #define CREATE_TRACE_POINTS 12 14 #include <trace/events/amdxdna.h> ··· 56 54 struct xdna_mailbox_chann_res res[CHAN_RES_NUM]; 57 55 int msix_irq; 58 56 u32 iohub_int_addr; 59 - struct idr chan_idr; 60 - spinlock_t chan_idr_lock; /* protect chan_idr */ 57 + struct xarray chan_xa; 58 + u32 next_msgid; 61 59 u32 x2i_tail; 62 60 63 61 /* Received msg related fields */ ··· 166 164 167 165 static int mailbox_acquire_msgid(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg) 168 166 { 169 - unsigned long flags; 170 - int msg_id; 167 + u32 msg_id; 168 + int ret; 171 169 172 - spin_lock_irqsave(&mb_chann->chan_idr_lock, flags); 173 - msg_id = idr_alloc_cyclic(&mb_chann->chan_idr, mb_msg, 0, 174 - MAX_MSG_ID_ENTRIES, GFP_NOWAIT); 175 - spin_unlock_irqrestore(&mb_chann->chan_idr_lock, flags); 176 - if (msg_id < 0) 177 - return msg_id; 170 + ret = xa_alloc_cyclic_irq(&mb_chann->chan_xa, &msg_id, mb_msg, 171 + XA_LIMIT(0, MAX_MSG_ID_ENTRIES - 1), 172 + &mb_chann->next_msgid, GFP_NOWAIT); 173 + if (ret < 0) 174 + return ret; 178 175 179 176 /* 180 - * The IDR becomes less efficient when dealing with larger IDs. 181 - * Thus, add MAGIC_VAL to the higher bits. 177 + * Add MAGIC_VAL to the higher bits. 182 178 */ 183 179 msg_id |= MAGIC_VAL; 184 180 return msg_id; ··· 184 184 185 185 static void mailbox_release_msgid(struct mailbox_channel *mb_chann, int msg_id) 186 186 { 187 - unsigned long flags; 188 - 189 187 msg_id &= ~MAGIC_VAL_MASK; 190 - spin_lock_irqsave(&mb_chann->chan_idr_lock, flags); 191 - idr_remove(&mb_chann->chan_idr, msg_id); 192 - spin_unlock_irqrestore(&mb_chann->chan_idr_lock, flags); 188 + xa_erase_irq(&mb_chann->chan_xa, msg_id); 193 189 } 194 190 195 - static int mailbox_release_msg(int id, void *p, void *data) 191 + static void mailbox_release_msg(struct mailbox_channel *mb_chann, 192 + struct mailbox_msg *mb_msg) 196 193 { 197 - struct mailbox_channel *mb_chann = data; 198 - struct mailbox_msg *mb_msg = p; 199 - 200 194 MB_DBG(mb_chann, "msg_id 0x%x msg opcode 0x%x", 201 195 mb_msg->pkg.header.id, mb_msg->pkg.header.opcode); 202 196 mb_msg->notify_cb(mb_msg->handle, NULL, 0); 203 197 kfree(mb_msg); 204 - 205 - return 0; 206 198 } 207 199 208 200 static int ··· 246 254 void *data) 247 255 { 248 256 struct mailbox_msg *mb_msg; 249 - unsigned long flags; 250 257 int msg_id; 251 258 int ret; 252 259 ··· 256 265 } 257 266 258 267 msg_id &= ~MAGIC_VAL_MASK; 259 - spin_lock_irqsave(&mb_chann->chan_idr_lock, flags); 260 - mb_msg = idr_find(&mb_chann->chan_idr, msg_id); 268 + mb_msg = xa_erase_irq(&mb_chann->chan_xa, msg_id); 261 269 if (!mb_msg) { 262 270 MB_ERR(mb_chann, "Cannot find msg 0x%x", msg_id); 263 - spin_unlock_irqrestore(&mb_chann->chan_idr_lock, flags); 264 271 return -EINVAL; 265 272 } 266 - idr_remove(&mb_chann->chan_idr, msg_id); 267 - spin_unlock_irqrestore(&mb_chann->chan_idr_lock, flags); 268 273 269 274 MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x", 270 275 header->opcode, header->total_size, header->id); ··· 484 497 memcpy(&mb_chann->res[CHAN_RES_X2I], x2i, sizeof(*x2i)); 485 498 memcpy(&mb_chann->res[CHAN_RES_I2X], i2x, sizeof(*i2x)); 486 499 487 - spin_lock_init(&mb_chann->chan_idr_lock); 488 - idr_init(&mb_chann->chan_idr); 500 + xa_init_flags(&mb_chann->chan_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ); 489 501 mb_chann->x2i_tail = mailbox_get_tailptr(mb_chann, CHAN_RES_X2I); 490 502 mb_chann->i2x_head = mailbox_get_headptr(mb_chann, CHAN_RES_I2X); 491 503 ··· 516 530 517 531 int xdna_mailbox_destroy_channel(struct mailbox_channel *mb_chann) 518 532 { 519 - if (!mb_chann) 520 - return 0; 533 + struct mailbox_msg *mb_msg; 534 + unsigned long msg_id; 521 535 522 536 MB_DBG(mb_chann, "IRQ disabled and RX work cancelled"); 523 537 free_irq(mb_chann->msix_irq, mb_chann); 524 538 destroy_workqueue(mb_chann->work_q); 525 539 /* We can clean up and release resources */ 526 540 527 - idr_for_each(&mb_chann->chan_idr, mailbox_release_msg, mb_chann); 528 - idr_destroy(&mb_chann->chan_idr); 541 + xa_for_each(&mb_chann->chan_xa, msg_id, mb_msg) 542 + mailbox_release_msg(mb_chann, mb_msg); 543 + 544 + xa_destroy(&mb_chann->chan_xa); 529 545 530 546 MB_DBG(mb_chann, "Mailbox channel destroyed, irq: %d", mb_chann->msix_irq); 531 547 kfree(mb_chann); ··· 536 548 537 549 void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann) 538 550 { 539 - if (!mb_chann) 540 - return; 541 - 542 551 /* Disable an irq and wait. This might sleep. */ 543 552 disable_irq(mb_chann->msix_irq); 544 553
+24 -4
drivers/accel/amdxdna/amdxdna_pci_drv.c
··· 39 39 { 0x17f0, 0x0, &dev_npu2_info }, 40 40 { 0x17f0, 0x10, &dev_npu4_info }, 41 41 { 0x17f0, 0x11, &dev_npu5_info }, 42 + { 0x17f0, 0x20, &dev_npu6_info }, 42 43 {0} 43 44 }; 44 45 ··· 78 77 } 79 78 mutex_init(&client->hwctx_lock); 80 79 init_srcu_struct(&client->hwctx_srcu); 81 - idr_init_base(&client->hwctx_idr, AMDXDNA_INVALID_CTX_HANDLE + 1); 80 + xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC); 82 81 mutex_init(&client->mm_lock); 83 82 84 83 mutex_lock(&xdna->dev_lock); ··· 109 108 110 109 XDNA_DBG(xdna, "closing pid %d", client->pid); 111 110 112 - idr_destroy(&client->hwctx_idr); 111 + xa_destroy(&client->hwctx_xa); 113 112 cleanup_srcu_struct(&client->hwctx_srcu); 114 113 mutex_destroy(&client->hwctx_lock); 115 114 mutex_destroy(&client->mm_lock); ··· 161 160 return ret; 162 161 } 163 162 163 + static int amdxdna_drm_set_state_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) 164 + { 165 + struct amdxdna_client *client = filp->driver_priv; 166 + struct amdxdna_dev *xdna = to_xdna_dev(dev); 167 + struct amdxdna_drm_set_state *args = data; 168 + int ret; 169 + 170 + if (!xdna->dev_info->ops->set_aie_state) 171 + return -EOPNOTSUPP; 172 + 173 + XDNA_DBG(xdna, "Request parameter %u", args->param); 174 + mutex_lock(&xdna->dev_lock); 175 + ret = xdna->dev_info->ops->set_aie_state(client, args); 176 + mutex_unlock(&xdna->dev_lock); 177 + 178 + return ret; 179 + } 180 + 164 181 static const struct drm_ioctl_desc amdxdna_drm_ioctls[] = { 165 182 /* Context */ 166 183 DRM_IOCTL_DEF_DRV(AMDXDNA_CREATE_HWCTX, amdxdna_drm_create_hwctx_ioctl, 0), ··· 192 173 DRM_IOCTL_DEF_DRV(AMDXDNA_EXEC_CMD, amdxdna_drm_submit_cmd_ioctl, 0), 193 174 /* AIE hardware */ 194 175 DRM_IOCTL_DEF_DRV(AMDXDNA_GET_INFO, amdxdna_drm_get_info_ioctl, 0), 176 + DRM_IOCTL_DEF_DRV(AMDXDNA_SET_STATE, amdxdna_drm_set_state_ioctl, DRM_ROOT_ONLY), 195 177 }; 196 178 197 179 static const struct file_operations amdxdna_fops = { ··· 410 390 } 411 391 412 392 static const struct dev_pm_ops amdxdna_pm_ops = { 413 - SET_SYSTEM_SLEEP_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume) 414 - SET_RUNTIME_PM_OPS(amdxdna_rpmops_suspend, amdxdna_rpmops_resume, NULL) 393 + SYSTEM_SLEEP_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume) 394 + RUNTIME_PM_OPS(amdxdna_rpmops_suspend, amdxdna_rpmops_resume, NULL) 415 395 }; 416 396 417 397 static struct pci_driver amdxdna_pci_driver = {
+25 -1
drivers/accel/amdxdna/amdxdna_pci_drv.h
··· 6 6 #ifndef _AMDXDNA_PCI_DRV_H_ 7 7 #define _AMDXDNA_PCI_DRV_H_ 8 8 9 + #include <linux/xarray.h> 10 + 9 11 #define XDNA_INFO(xdna, fmt, args...) drm_info(&(xdna)->ddev, fmt, ##args) 10 12 #define XDNA_WARN(xdna, fmt, args...) drm_warn(&(xdna)->ddev, "%s: "fmt, __func__, ##args) 11 13 #define XDNA_ERR(xdna, fmt, args...) drm_err(&(xdna)->ddev, "%s: "fmt, __func__, ##args) 12 14 #define XDNA_DBG(xdna, fmt, args...) drm_dbg(&(xdna)->ddev, fmt, ##args) 13 15 #define XDNA_INFO_ONCE(xdna, fmt, args...) drm_info_once(&(xdna)->ddev, fmt, ##args) 16 + 17 + #define XDNA_MBZ_DBG(xdna, ptr, sz) \ 18 + ({ \ 19 + int __i; \ 20 + int __ret = 0; \ 21 + u8 *__ptr = (u8 *)(ptr); \ 22 + for (__i = 0; __i < (sz); __i++) { \ 23 + if (__ptr[__i]) { \ 24 + XDNA_DBG(xdna, "MBZ check failed"); \ 25 + __ret = -EINVAL; \ 26 + break; \ 27 + } \ 28 + } \ 29 + __ret; \ 30 + }) 14 31 15 32 #define to_xdna_dev(drm_dev) \ 16 33 ((struct amdxdna_dev *)container_of(drm_dev, struct amdxdna_dev, ddev)) ··· 37 20 struct amdxdna_client; 38 21 struct amdxdna_dev; 39 22 struct amdxdna_drm_get_info; 23 + struct amdxdna_drm_set_state; 40 24 struct amdxdna_gem_obj; 41 25 struct amdxdna_hwctx; 42 26 struct amdxdna_sched_job; ··· 58 40 void (*hwctx_resume)(struct amdxdna_hwctx *hwctx); 59 41 int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq); 60 42 int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args); 43 + int (*set_aie_state)(struct amdxdna_client *client, struct amdxdna_drm_set_state *args); 61 44 }; 62 45 63 46 /* ··· 119 100 struct mutex hwctx_lock; /* protect hwctx */ 120 101 /* do NOT wait this srcu when hwctx_lock is held */ 121 102 struct srcu_struct hwctx_srcu; 122 - struct idr hwctx_idr; 103 + struct xarray hwctx_xa; 104 + u32 next_hwctxid; 123 105 struct amdxdna_dev *xdna; 124 106 struct drm_file *filp; 125 107 ··· 131 111 int pasid; 132 112 }; 133 113 114 + #define amdxdna_for_each_hwctx(client, hwctx_id, entry) \ 115 + xa_for_each(&(client)->hwctx_xa, hwctx_id, entry) 116 + 134 117 /* Add device info below */ 135 118 extern const struct amdxdna_dev_info dev_npu1_info; 136 119 extern const struct amdxdna_dev_info dev_npu2_info; 137 120 extern const struct amdxdna_dev_info dev_npu4_info; 138 121 extern const struct amdxdna_dev_info dev_npu5_info; 122 + extern const struct amdxdna_dev_info dev_npu6_info; 139 123 140 124 int amdxdna_sysfs_init(struct amdxdna_dev *xdna); 141 125 void amdxdna_sysfs_fini(struct amdxdna_dev *xdna);
+22 -9
drivers/accel/amdxdna/npu1_regs.c
··· 44 44 #define NPU1_SMU_BAR_BASE MPNPU_APERTURE0_BASE 45 45 #define NPU1_SRAM_BAR_BASE MPNPU_APERTURE1_BASE 46 46 47 - #define NPU1_RT_CFG_TYPE_PDI_LOAD 2 48 - #define NPU1_RT_CFG_VAL_PDI_LOAD_MGMT 0 49 - #define NPU1_RT_CFG_VAL_PDI_LOAD_APP 1 47 + const struct rt_config npu1_default_rt_cfg[] = { 48 + { 2, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */ 49 + { 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */ 50 + { 0 }, 51 + }; 50 52 51 - #define NPU1_MPNPUCLK_FREQ_MAX 600 52 - #define NPU1_HCLK_FREQ_MAX 1024 53 + const struct dpm_clk_freq npu1_dpm_clk_table[] = { 54 + {400, 800}, 55 + {600, 1024}, 56 + {600, 1024}, 57 + {600, 1024}, 58 + {600, 1024}, 59 + {720, 1309}, 60 + {720, 1309}, 61 + {847, 1600}, 62 + { 0 } 63 + }; 53 64 54 65 const struct amdxdna_dev_priv npu1_dev_priv = { 55 66 .fw_path = "amdnpu/1502_00/npu.sbin", 56 67 .protocol_major = 0x5, 57 - .protocol_minor = 0x1, 58 - .rt_config = {NPU1_RT_CFG_TYPE_PDI_LOAD, NPU1_RT_CFG_VAL_PDI_LOAD_APP}, 68 + .protocol_minor = 0x7, 69 + .rt_config = npu1_default_rt_cfg, 70 + .dpm_clk_tbl = npu1_dpm_clk_table, 59 71 .col_align = COL_ALIGN_NONE, 60 72 .mbox_dev_addr = NPU1_MBOX_BAR_BASE, 61 73 .mbox_size = 0, /* Use BAR size */ ··· 92 80 DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU1_SMU, MPNPU_PUB_SCRATCH6), 93 81 DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU1_SMU, MPNPU_PUB_SCRATCH7), 94 82 }, 95 - .smu_mpnpuclk_freq_max = NPU1_MPNPUCLK_FREQ_MAX, 96 - .smu_hclk_freq_max = NPU1_HCLK_FREQ_MAX, 83 + .hw_ops = { 84 + .set_dpm = npu1_set_dpm, 85 + }, 97 86 }; 98 87 99 88 const struct amdxdna_dev_info dev_npu1_info = {
+6 -11
drivers/accel/amdxdna/npu2_regs.c
··· 61 61 #define NPU2_SMU_BAR_BASE MMNPU_APERTURE4_BASE 62 62 #define NPU2_SRAM_BAR_BASE MMNPU_APERTURE1_BASE 63 63 64 - #define NPU2_RT_CFG_TYPE_PDI_LOAD 5 65 - #define NPU2_RT_CFG_VAL_PDI_LOAD_MGMT 0 66 - #define NPU2_RT_CFG_VAL_PDI_LOAD_APP 1 67 - 68 - #define NPU2_MPNPUCLK_FREQ_MAX 1267 69 - #define NPU2_HCLK_FREQ_MAX 1800 70 - 71 64 const struct amdxdna_dev_priv npu2_dev_priv = { 72 65 .fw_path = "amdnpu/17f0_00/npu.sbin", 73 66 .protocol_major = 0x6, 74 - .protocol_minor = 0x1, 75 - .rt_config = {NPU2_RT_CFG_TYPE_PDI_LOAD, NPU2_RT_CFG_VAL_PDI_LOAD_APP}, 67 + .protocol_minor = 0x6, 68 + .rt_config = npu4_default_rt_cfg, 69 + .dpm_clk_tbl = npu4_dpm_clk_table, 76 70 .col_align = COL_ALIGN_NATURE, 77 71 .mbox_dev_addr = NPU2_MBOX_BAR_BASE, 78 72 .mbox_size = 0, /* Use BAR size */ ··· 91 97 DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU2_SMU, MP1_C2PMSG_61), 92 98 DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU2_SMU, MP1_C2PMSG_60), 93 99 }, 94 - .smu_mpnpuclk_freq_max = NPU2_MPNPUCLK_FREQ_MAX, 95 - .smu_hclk_freq_max = NPU2_HCLK_FREQ_MAX, 100 + .hw_ops = { 101 + .set_dpm = npu4_set_dpm, 102 + }, 96 103 }; 97 104 98 105 const struct amdxdna_dev_info dev_npu2_info = {
+25 -9
drivers/accel/amdxdna/npu4_regs.c
··· 61 61 #define NPU4_SMU_BAR_BASE MMNPU_APERTURE4_BASE 62 62 #define NPU4_SRAM_BAR_BASE MMNPU_APERTURE1_BASE 63 63 64 - #define NPU4_RT_CFG_TYPE_PDI_LOAD 5 65 - #define NPU4_RT_CFG_VAL_PDI_LOAD_MGMT 0 66 - #define NPU4_RT_CFG_VAL_PDI_LOAD_APP 1 64 + const struct rt_config npu4_default_rt_cfg[] = { 65 + { 5, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */ 66 + { 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */ 67 + { 2, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */ 68 + { 3, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */ 69 + { 4, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */ 70 + { 0 }, 71 + }; 67 72 68 - #define NPU4_MPNPUCLK_FREQ_MAX 1267 69 - #define NPU4_HCLK_FREQ_MAX 1800 73 + const struct dpm_clk_freq npu4_dpm_clk_table[] = { 74 + {396, 792}, 75 + {600, 1056}, 76 + {792, 1152}, 77 + {975, 1267}, 78 + {975, 1267}, 79 + {1056, 1408}, 80 + {1152, 1584}, 81 + {1267, 1800}, 82 + { 0 } 83 + }; 70 84 71 85 const struct amdxdna_dev_priv npu4_dev_priv = { 72 86 .fw_path = "amdnpu/17f0_10/npu.sbin", 73 87 .protocol_major = 0x6, 74 - .protocol_minor = 0x1, 75 - .rt_config = {NPU4_RT_CFG_TYPE_PDI_LOAD, NPU4_RT_CFG_VAL_PDI_LOAD_APP}, 88 + .protocol_minor = 12, 89 + .rt_config = npu4_default_rt_cfg, 90 + .dpm_clk_tbl = npu4_dpm_clk_table, 76 91 .col_align = COL_ALIGN_NATURE, 77 92 .mbox_dev_addr = NPU4_MBOX_BAR_BASE, 78 93 .mbox_size = 0, /* Use BAR size */ ··· 112 97 DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU4_SMU, MP1_C2PMSG_61), 113 98 DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU4_SMU, MP1_C2PMSG_60), 114 99 }, 115 - .smu_mpnpuclk_freq_max = NPU4_MPNPUCLK_FREQ_MAX, 116 - .smu_hclk_freq_max = NPU4_HCLK_FREQ_MAX, 100 + .hw_ops = { 101 + .set_dpm = npu4_set_dpm, 102 + }, 117 103 }; 118 104 119 105 const struct amdxdna_dev_info dev_npu4_info = {
+6 -11
drivers/accel/amdxdna/npu5_regs.c
··· 61 61 #define NPU5_SMU_BAR_BASE MMNPU_APERTURE4_BASE 62 62 #define NPU5_SRAM_BAR_BASE MMNPU_APERTURE1_BASE 63 63 64 - #define NPU5_RT_CFG_TYPE_PDI_LOAD 5 65 - #define NPU5_RT_CFG_VAL_PDI_LOAD_MGMT 0 66 - #define NPU5_RT_CFG_VAL_PDI_LOAD_APP 1 67 - 68 - #define NPU5_MPNPUCLK_FREQ_MAX 1267 69 - #define NPU5_HCLK_FREQ_MAX 1800 70 - 71 64 const struct amdxdna_dev_priv npu5_dev_priv = { 72 65 .fw_path = "amdnpu/17f0_11/npu.sbin", 73 66 .protocol_major = 0x6, 74 - .protocol_minor = 0x1, 75 - .rt_config = {NPU5_RT_CFG_TYPE_PDI_LOAD, NPU5_RT_CFG_VAL_PDI_LOAD_APP}, 67 + .protocol_minor = 12, 68 + .rt_config = npu4_default_rt_cfg, 69 + .dpm_clk_tbl = npu4_dpm_clk_table, 76 70 .col_align = COL_ALIGN_NATURE, 77 71 .mbox_dev_addr = NPU5_MBOX_BAR_BASE, 78 72 .mbox_size = 0, /* Use BAR size */ ··· 91 97 DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU5_SMU, MP1_C2PMSG_61), 92 98 DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU5_SMU, MP1_C2PMSG_60), 93 99 }, 94 - .smu_mpnpuclk_freq_max = NPU5_MPNPUCLK_FREQ_MAX, 95 - .smu_hclk_freq_max = NPU5_HCLK_FREQ_MAX, 100 + .hw_ops = { 101 + .set_dpm = npu4_set_dpm, 102 + }, 96 103 }; 97 104 98 105 const struct amdxdna_dev_info dev_npu5_info = {
+114
drivers/accel/amdxdna/npu6_regs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024, Advanced Micro Devices, Inc. 4 + */ 5 + 6 + #include <drm/amdxdna_accel.h> 7 + #include <drm/drm_device.h> 8 + #include <drm/gpu_scheduler.h> 9 + #include <linux/sizes.h> 10 + 11 + #include "aie2_pci.h" 12 + #include "amdxdna_mailbox.h" 13 + #include "amdxdna_pci_drv.h" 14 + 15 + /* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */ 16 + #define MPNPU_PUB_SEC_INTR 0x3010060 17 + #define MPNPU_PUB_PWRMGMT_INTR 0x3010064 18 + #define MPNPU_PUB_SCRATCH0 0x301006C 19 + #define MPNPU_PUB_SCRATCH1 0x3010070 20 + #define MPNPU_PUB_SCRATCH2 0x3010074 21 + #define MPNPU_PUB_SCRATCH3 0x3010078 22 + #define MPNPU_PUB_SCRATCH4 0x301007C 23 + #define MPNPU_PUB_SCRATCH5 0x3010080 24 + #define MPNPU_PUB_SCRATCH6 0x3010084 25 + #define MPNPU_PUB_SCRATCH7 0x3010088 26 + #define MPNPU_PUB_SCRATCH8 0x301008C 27 + #define MPNPU_PUB_SCRATCH9 0x3010090 28 + #define MPNPU_PUB_SCRATCH10 0x3010094 29 + #define MPNPU_PUB_SCRATCH11 0x3010098 30 + #define MPNPU_PUB_SCRATCH12 0x301009C 31 + #define MPNPU_PUB_SCRATCH13 0x30100A0 32 + #define MPNPU_PUB_SCRATCH14 0x30100A4 33 + #define MPNPU_PUB_SCRATCH15 0x30100A8 34 + #define MP0_C2PMSG_73 0x3810A24 35 + #define MP0_C2PMSG_123 0x3810AEC 36 + 37 + #define MP1_C2PMSG_0 0x3B10900 38 + #define MP1_C2PMSG_60 0x3B109F0 39 + #define MP1_C2PMSG_61 0x3B109F4 40 + 41 + #define MPNPU_SRAM_X2I_MAILBOX_0 0x3600000 42 + #define MPNPU_SRAM_X2I_MAILBOX_15 0x361E000 43 + #define MPNPU_SRAM_X2I_MAILBOX_31 0x363E000 44 + #define MPNPU_SRAM_I2X_MAILBOX_31 0x363F000 45 + 46 + #define MMNPU_APERTURE0_BASE 0x3000000 47 + #define MMNPU_APERTURE1_BASE 0x3600000 48 + #define MMNPU_APERTURE3_BASE 0x3810000 49 + #define MMNPU_APERTURE4_BASE 0x3B10000 50 + 51 + /* PCIe BAR Index for NPU6 */ 52 + #define NPU6_REG_BAR_INDEX 0 53 + #define NPU6_MBOX_BAR_INDEX 0 54 + #define NPU6_PSP_BAR_INDEX 4 55 + #define NPU6_SMU_BAR_INDEX 5 56 + #define NPU6_SRAM_BAR_INDEX 2 57 + /* Associated BARs and Apertures */ 58 + #define NPU6_REG_BAR_BASE MMNPU_APERTURE0_BASE 59 + #define NPU6_MBOX_BAR_BASE MMNPU_APERTURE0_BASE 60 + #define NPU6_PSP_BAR_BASE MMNPU_APERTURE3_BASE 61 + #define NPU6_SMU_BAR_BASE MMNPU_APERTURE4_BASE 62 + #define NPU6_SRAM_BAR_BASE MMNPU_APERTURE1_BASE 63 + 64 + const struct amdxdna_dev_priv npu6_dev_priv = { 65 + .fw_path = "amdnpu/17f0_10/npu.sbin", 66 + .protocol_major = 0x6, 67 + .protocol_minor = 12, 68 + .rt_config = npu4_default_rt_cfg, 69 + .dpm_clk_tbl = npu4_dpm_clk_table, 70 + .col_align = COL_ALIGN_NATURE, 71 + .mbox_dev_addr = NPU6_MBOX_BAR_BASE, 72 + .mbox_size = 0, /* Use BAR size */ 73 + .sram_dev_addr = NPU6_SRAM_BAR_BASE, 74 + .sram_offs = { 75 + DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_0), 76 + DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_15), 77 + }, 78 + .psp_regs_off = { 79 + DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU6_PSP, MP0_C2PMSG_123), 80 + DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU6_REG, MPNPU_PUB_SCRATCH3), 81 + DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU6_REG, MPNPU_PUB_SCRATCH4), 82 + DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU6_REG, MPNPU_PUB_SCRATCH9), 83 + DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU6_PSP, MP0_C2PMSG_73), 84 + DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU6_PSP, MP0_C2PMSG_123), 85 + DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU6_REG, MPNPU_PUB_SCRATCH3), 86 + }, 87 + .smu_regs_off = { 88 + DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU6_SMU, MP1_C2PMSG_0), 89 + DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU6_SMU, MP1_C2PMSG_60), 90 + DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU6_SMU, MMNPU_APERTURE4_BASE), 91 + DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU6_SMU, MP1_C2PMSG_61), 92 + DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU6_SMU, MP1_C2PMSG_60), 93 + }, 94 + .hw_ops = { 95 + .set_dpm = npu4_set_dpm, 96 + }, 97 + 98 + }; 99 + 100 + const struct amdxdna_dev_info dev_npu6_info = { 101 + .reg_bar = NPU6_REG_BAR_INDEX, 102 + .mbox_bar = NPU6_MBOX_BAR_INDEX, 103 + .sram_bar = NPU6_SRAM_BAR_INDEX, 104 + .psp_bar = NPU6_PSP_BAR_INDEX, 105 + .smu_bar = NPU6_SMU_BAR_INDEX, 106 + .first_col = 0, 107 + .dev_mem_buf_shift = 15, /* 32 KiB aligned */ 108 + .dev_mem_base = AIE2_DEVM_BASE, 109 + .dev_mem_size = AIE2_DEVM_SIZE, 110 + .vbnv = "RyzenAI-npu6", 111 + .device_type = AMDXDNA_DEV_TYPE_KMQ, 112 + .dev_priv = &npu6_dev_priv, 113 + .ops = &aie2_ops, 114 + };
+5
drivers/gpu/drm/Kconfig
··· 102 102 help 103 103 CRTC helpers for KMS drivers. 104 104 105 + config DRM_DRAW 106 + bool 107 + depends on DRM 108 + 105 109 config DRM_PANIC 106 110 bool "Display a user-friendly message when a kernel panic occurs" 107 111 depends on DRM 108 112 select FONT_SUPPORT 113 + select DRM_DRAW 109 114 help 110 115 Enable a drm panic handler, which will display a user-friendly message 111 116 when a kernel panic occurs. It's useful when using a user-space
+1
drivers/gpu/drm/Makefile
··· 91 91 drm_privacy_screen_x86.o 92 92 drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o 93 93 drm-$(CONFIG_DRM_PANIC) += drm_panic.o 94 + drm-$(CONFIG_DRM_DRAW) += drm_draw.o 94 95 drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o 95 96 obj-$(CONFIG_DRM) += drm.o 96 97
+2
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
··· 1037 1037 continue; 1038 1038 1039 1039 *enabled = true; 1040 + mutex_lock(&connector->eld_mutex); 1040 1041 ret = drm_eld_size(connector->eld); 1041 1042 memcpy(buf, connector->eld, min(max_bytes, ret)); 1043 + mutex_unlock(&connector->eld_mutex); 1042 1044 1043 1045 break; 1044 1046 }
+3 -2
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
··· 590 590 amdgpu_dm_set_mst_status(&aconnector->mst_status, 591 591 MST_PROBE, true); 592 592 593 - if (drm_connector_init( 593 + if (drm_connector_dynamic_init( 594 594 dev, 595 595 connector, 596 596 &dm_dp_mst_connector_funcs, 597 - DRM_MODE_CONNECTOR_DisplayPort)) { 597 + DRM_MODE_CONNECTOR_DisplayPort, 598 + NULL)) { 598 599 kfree(aconnector); 599 600 return NULL; 600 601 }
+1 -1
drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
··· 793 793 } 794 794 795 795 static const struct i2c_device_id anx6345_id[] = { 796 - { "anx6345", 0 }, 796 + { "anx6345" }, 797 797 { /* sentinel */ } 798 798 }; 799 799 MODULE_DEVICE_TABLE(i2c, anx6345_id);
+3 -1
drivers/gpu/drm/bridge/analogix/anx7625.c
··· 2002 2002 memset(buf, 0, len); 2003 2003 } else { 2004 2004 dev_dbg(dev, "audio copy eld\n"); 2005 + mutex_lock(&ctx->connector->eld_mutex); 2005 2006 memcpy(buf, ctx->connector->eld, 2006 2007 min(sizeof(ctx->connector->eld), len)); 2008 + mutex_unlock(&ctx->connector->eld_mutex); 2007 2009 } 2008 2010 2009 2011 return 0; ··· 2797 2795 } 2798 2796 2799 2797 static const struct i2c_device_id anx7625_id[] = { 2800 - {"anx7625", 0}, 2798 + { "anx7625" }, 2801 2799 {} 2802 2800 }; 2803 2801
+1 -1
drivers/gpu/drm/bridge/chrontel-ch7033.c
··· 597 597 MODULE_DEVICE_TABLE(of, ch7033_dt_ids); 598 598 599 599 static const struct i2c_device_id ch7033_ids[] = { 600 - { "ch7033", 0 }, 600 + { "ch7033" }, 601 601 { } 602 602 }; 603 603 MODULE_DEVICE_TABLE(i2c, ch7033_ids);
+23 -4
drivers/gpu/drm/bridge/ite-it6263.c
··· 48 48 #define REG_COL_DEP GENMASK(1, 0) 49 49 #define BIT8 FIELD_PREP(REG_COL_DEP, 1) 50 50 #define OUT_MAP BIT(4) 51 + #define VESA BIT(4) 51 52 #define JEIDA 0 52 53 #define REG_DESSC_ENB BIT(6) 53 54 #define DMODE BIT(7) ··· 429 428 fsleep(10000); 430 429 } 431 430 431 + static inline bool it6263_is_input_bus_fmt_valid(int input_fmt) 432 + { 433 + switch (input_fmt) { 434 + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: 435 + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: 436 + return true; 437 + } 438 + return false; 439 + } 440 + 432 441 static inline void it6263_lvds_set_interface(struct it6263 *it) 433 442 { 443 + u8 fmt; 444 + 434 445 /* color depth */ 435 446 regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, REG_COL_DEP, BIT8); 447 + 448 + if (it->lvds_data_mapping == MEDIA_BUS_FMT_RGB888_1X7X4_SPWG) 449 + fmt = VESA; 450 + else 451 + fmt = JEIDA; 452 + 436 453 /* output mapping */ 437 - regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, OUT_MAP, JEIDA); 454 + regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, OUT_MAP, fmt); 438 455 439 456 if (it->lvds_dual_link) { 440 457 regmap_write_bits(it->lvds_regmap, LVDS_REG_2C, DMODE, DISO); ··· 733 714 734 715 *num_input_fmts = 0; 735 716 736 - if (it->lvds_data_mapping != MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA) 717 + if (!it6263_is_input_bus_fmt_valid(it->lvds_data_mapping)) 737 718 return NULL; 738 719 739 720 input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL); 740 721 if (!input_fmts) 741 722 return NULL; 742 723 743 - input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; 724 + input_fmts[0] = it->lvds_data_mapping; 744 725 *num_input_fmts = 1; 745 726 746 727 return input_fmts; ··· 897 878 MODULE_DEVICE_TABLE(of, it6263_of_match); 898 879 899 880 static const struct i2c_device_id it6263_i2c_ids[] = { 900 - { "it6263", 0 }, 881 + { "it6263" }, 901 882 { } 902 883 }; 903 884 MODULE_DEVICE_TABLE(i2c, it6263_i2c_ids);
+1 -1
drivers/gpu/drm/bridge/ite-it6505.c
··· 3497 3497 } 3498 3498 3499 3499 static const struct i2c_device_id it6505_id[] = { 3500 - { "it6505", 0 }, 3500 + { "it6505" }, 3501 3501 { } 3502 3502 }; 3503 3503
+2
drivers/gpu/drm/bridge/ite-it66121.c
··· 1450 1450 dev_dbg(dev, "No connector present, passing empty EDID data"); 1451 1451 memset(buf, 0, len); 1452 1452 } else { 1453 + mutex_lock(&ctx->connector->eld_mutex); 1453 1454 memcpy(buf, ctx->connector->eld, 1454 1455 min(sizeof(ctx->connector->eld), len)); 1456 + mutex_unlock(&ctx->connector->eld_mutex); 1455 1457 } 1456 1458 mutex_unlock(&ctx->lock); 1457 1459
+2 -2
drivers/gpu/drm/bridge/lontium-lt8912b.c
··· 815 815 MODULE_DEVICE_TABLE(of, lt8912_dt_match); 816 816 817 817 static const struct i2c_device_id lt8912_id[] = { 818 - {"lt8912", 0}, 819 - {}, 818 + { "lt8912" }, 819 + {} 820 820 }; 821 821 MODULE_DEVICE_TABLE(i2c, lt8912_id); 822 822
+2 -4
drivers/gpu/drm/bridge/lontium-lt9611.c
··· 757 757 const struct drm_display_mode *mode) 758 758 { 759 759 struct lt9611 *lt9611 = bridge_to_lt9611(bridge); 760 - unsigned long long rate; 761 760 762 761 if (mode->hdisplay > 3840) 763 762 return MODE_BAD_HVALUE; ··· 764 765 if (mode->hdisplay > 2000 && !lt9611->dsi1_node) 765 766 return MODE_PANEL; 766 767 767 - rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); 768 - return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, rate); 768 + return MODE_OK; 769 769 } 770 770 771 771 static int lt9611_bridge_atomic_check(struct drm_bridge *bridge, ··· 1234 1236 } 1235 1237 1236 1238 static const struct i2c_device_id lt9611_id[] = { 1237 - { "lontium,lt9611", 0 }, 1239 + { "lontium,lt9611" }, 1238 1240 {} 1239 1241 }; 1240 1242 MODULE_DEVICE_TABLE(i2c, lt9611_id);
+1 -1
drivers/gpu/drm/bridge/lontium-lt9611uxc.c
··· 914 914 } 915 915 916 916 static const struct i2c_device_id lt9611uxc_id[] = { 917 - { "lontium,lt9611uxc", 0 }, 917 + { "lontium,lt9611uxc" }, 918 918 { /* sentinel */ } 919 919 }; 920 920
+4 -4
drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
··· 318 318 } 319 319 320 320 static const struct i2c_device_id stdp4028_ge_b850v3_fw_i2c_table[] = { 321 - {"stdp4028_ge_fw", 0}, 322 - {}, 321 + { "stdp4028_ge_fw" }, 322 + {} 323 323 }; 324 324 MODULE_DEVICE_TABLE(i2c, stdp4028_ge_b850v3_fw_i2c_table); 325 325 ··· 365 365 } 366 366 367 367 static const struct i2c_device_id stdp2690_ge_b850v3_fw_i2c_table[] = { 368 - {"stdp2690_ge_fw", 0}, 369 - {}, 368 + { "stdp2690_ge_fw" }, 369 + {} 370 370 }; 371 371 MODULE_DEVICE_TABLE(i2c, stdp2690_ge_b850v3_fw_i2c_table); 372 372
+2 -2
drivers/gpu/drm/bridge/nxp-ptn3460.c
··· 319 319 } 320 320 321 321 static const struct i2c_device_id ptn3460_i2c_table[] = { 322 - {"ptn3460", 0}, 323 - {}, 322 + { "ptn3460" }, 323 + {} 324 324 }; 325 325 MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table); 326 326
+2 -2
drivers/gpu/drm/bridge/sii902x.c
··· 1239 1239 MODULE_DEVICE_TABLE(of, sii902x_dt_ids); 1240 1240 1241 1241 static const struct i2c_device_id sii902x_i2c_ids[] = { 1242 - { "sii9022", 0 }, 1243 - { }, 1242 + { "sii9022" }, 1243 + { } 1244 1244 }; 1245 1245 MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids); 1246 1246
+2 -2
drivers/gpu/drm/bridge/sii9234.c
··· 945 945 MODULE_DEVICE_TABLE(of, sii9234_dt_match); 946 946 947 947 static const struct i2c_device_id sii9234_id[] = { 948 - { "SII9234", 0 }, 949 - { }, 948 + { "SII9234" }, 949 + { } 950 950 }; 951 951 MODULE_DEVICE_TABLE(i2c, sii9234_id); 952 952
+2 -2
drivers/gpu/drm/bridge/sil-sii8620.c
··· 2368 2368 MODULE_DEVICE_TABLE(of, sii8620_dt_match); 2369 2369 2370 2370 static const struct i2c_device_id sii8620_id[] = { 2371 - { "sii8620", 0 }, 2372 - { }, 2371 + { "sii8620" }, 2372 + { } 2373 2373 }; 2374 2374 2375 2375 MODULE_DEVICE_TABLE(i2c, sii8620_id);
+6
drivers/gpu/drm/bridge/synopsys/Kconfig
··· 59 59 select DRM_KMS_HELPER 60 60 select DRM_MIPI_DSI 61 61 select DRM_PANEL_BRIDGE 62 + 63 + config DRM_DW_MIPI_DSI2 64 + tristate 65 + select DRM_KMS_HELPER 66 + select DRM_MIPI_DSI 67 + select DRM_PANEL_BRIDGE
+1
drivers/gpu/drm/bridge/synopsys/Makefile
··· 8 8 obj-$(CONFIG_DRM_DW_HDMI_QP) += dw-hdmi-qp.o 9 9 10 10 obj-$(CONFIG_DRM_DW_MIPI_DSI) += dw-mipi-dsi.o 11 + obj-$(CONFIG_DRM_DW_MIPI_DSI2) += dw-mipi-dsi2.o
+5 -7
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
··· 442 442 } 443 443 444 444 static enum drm_mode_status 445 - dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge, 446 - const struct drm_display_info *info, 447 - const struct drm_display_mode *mode) 445 + dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge, 446 + const struct drm_display_mode *mode, 447 + unsigned long long rate) 448 448 { 449 449 struct dw_hdmi_qp *hdmi = bridge->driver_private; 450 - unsigned long long rate; 451 450 452 - rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); 453 451 if (rate > HDMI14_MAX_TMDSCLK) { 454 - dev_dbg(hdmi->dev, "Unsupported mode clock: %d\n", mode->clock); 452 + dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate); 455 453 return MODE_CLOCK_HIGH; 456 454 } 457 455 ··· 508 510 .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, 509 511 .detect = dw_hdmi_qp_bridge_detect, 510 512 .edid_read = dw_hdmi_qp_bridge_edid_read, 511 - .mode_valid = dw_hdmi_qp_bridge_mode_valid, 513 + .hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid, 512 514 .hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe, 513 515 .hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe, 514 516 };
+1 -1
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* 3 - * Copyright (C) Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: 5 5 * Algea Cao <algea.cao@rock-chips.com> 6 6 */
+1030
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright (c) 2024, Fuzhou Rockchip Electronics Co., Ltd 4 + * 5 + * Modified by Heiko Stuebner <heiko.stuebner@cherry.de> 6 + * This generic Synopsys DesignWare MIPI DSI2 host driver is based on the 7 + * Rockchip version from rockchip/dw-mipi-dsi2.c converted to use bridge APIs. 8 + */ 9 + 10 + #include <linux/bitfield.h> 11 + #include <linux/clk.h> 12 + #include <linux/iopoll.h> 13 + #include <linux/media-bus-format.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/pm_runtime.h> 17 + #include <linux/reset.h> 18 + 19 + #include <video/mipi_display.h> 20 + 21 + #include <drm/bridge/dw_mipi_dsi2.h> 22 + #include <drm/drm_atomic_helper.h> 23 + #include <drm/drm_bridge.h> 24 + #include <drm/drm_mipi_dsi.h> 25 + #include <drm/drm_of.h> 26 + #include <drm/drm_print.h> 27 + 28 + #define DSI2_PWR_UP 0x000c 29 + #define RESET 0 30 + #define POWER_UP BIT(0) 31 + #define CMD_TX_MODE(x) FIELD_PREP(BIT(24), x) 32 + #define DSI2_SOFT_RESET 0x0010 33 + #define SYS_RSTN BIT(2) 34 + #define PHY_RSTN BIT(1) 35 + #define IPI_RSTN BIT(0) 36 + #define INT_ST_MAIN 0x0014 37 + #define DSI2_MODE_CTRL 0x0018 38 + #define DSI2_MODE_STATUS 0x001c 39 + #define DSI2_CORE_STATUS 0x0020 40 + #define PRI_RD_DATA_AVAIL BIT(26) 41 + #define PRI_FIFOS_NOT_EMPTY BIT(25) 42 + #define PRI_BUSY BIT(24) 43 + #define CRI_RD_DATA_AVAIL BIT(18) 44 + #define CRT_FIFOS_NOT_EMPTY BIT(17) 45 + #define CRI_BUSY BIT(16) 46 + #define IPI_FIFOS_NOT_EMPTY BIT(9) 47 + #define IPI_BUSY BIT(8) 48 + #define CORE_FIFOS_NOT_EMPTY BIT(1) 49 + #define CORE_BUSY BIT(0) 50 + #define MANUAL_MODE_CFG 0x0024 51 + #define MANUAL_MODE_EN BIT(0) 52 + #define DSI2_TIMEOUT_HSTX_CFG 0x0048 53 + #define TO_HSTX(x) FIELD_PREP(GENMASK(15, 0), x) 54 + #define DSI2_TIMEOUT_HSTXRDY_CFG 0x004c 55 + #define TO_HSTXRDY(x) FIELD_PREP(GENMASK(15, 0), x) 56 + #define DSI2_TIMEOUT_LPRX_CFG 0x0050 57 + #define TO_LPRXRDY(x) FIELD_PREP(GENMASK(15, 0), x) 58 + #define DSI2_TIMEOUT_LPTXRDY_CFG 0x0054 59 + #define TO_LPTXRDY(x) FIELD_PREP(GENMASK(15, 0), x) 60 + #define DSI2_TIMEOUT_LPTXTRIG_CFG 0x0058 61 + #define TO_LPTXTRIG(x) FIELD_PREP(GENMASK(15, 0), x) 62 + #define DSI2_TIMEOUT_LPTXULPS_CFG 0x005c 63 + #define TO_LPTXULPS(x) FIELD_PREP(GENMASK(15, 0), x) 64 + #define DSI2_TIMEOUT_BTA_CFG 0x60 65 + #define TO_BTA(x) FIELD_PREP(GENMASK(15, 0), x) 66 + 67 + #define DSI2_PHY_MODE_CFG 0x0100 68 + #define PPI_WIDTH(x) FIELD_PREP(GENMASK(9, 8), x) 69 + #define PHY_LANES(x) FIELD_PREP(GENMASK(5, 4), (x) - 1) 70 + #define PHY_TYPE(x) FIELD_PREP(BIT(0), x) 71 + #define DSI2_PHY_CLK_CFG 0X0104 72 + #define PHY_LPTX_CLK_DIV(x) FIELD_PREP(GENMASK(12, 8), x) 73 + #define CLK_TYPE_MASK BIT(0) 74 + #define NON_CONTINUOUS_CLK BIT(0) 75 + #define CONTINUOUS_CLK 0 76 + #define DSI2_PHY_LP2HS_MAN_CFG 0x010c 77 + #define PHY_LP2HS_TIME(x) FIELD_PREP(GENMASK(28, 0), x) 78 + #define DSI2_PHY_HS2LP_MAN_CFG 0x0114 79 + #define PHY_HS2LP_TIME(x) FIELD_PREP(GENMASK(28, 0), x) 80 + #define DSI2_PHY_MAX_RD_T_MAN_CFG 0x011c 81 + #define PHY_MAX_RD_TIME(x) FIELD_PREP(GENMASK(26, 0), x) 82 + #define DSI2_PHY_ESC_CMD_T_MAN_CFG 0x0124 83 + #define PHY_ESC_CMD_TIME(x) FIELD_PREP(GENMASK(28, 0), x) 84 + #define DSI2_PHY_ESC_BYTE_T_MAN_CFG 0x012c 85 + #define PHY_ESC_BYTE_TIME(x) FIELD_PREP(GENMASK(28, 0), x) 86 + 87 + #define DSI2_PHY_IPI_RATIO_MAN_CFG 0x0134 88 + #define PHY_IPI_RATIO(x) FIELD_PREP(GENMASK(21, 0), x) 89 + #define DSI2_PHY_SYS_RATIO_MAN_CFG 0x013C 90 + #define PHY_SYS_RATIO(x) FIELD_PREP(GENMASK(16, 0), x) 91 + 92 + #define DSI2_DSI_GENERAL_CFG 0x0200 93 + #define BTA_EN BIT(1) 94 + #define EOTP_TX_EN BIT(0) 95 + #define DSI2_DSI_VCID_CFG 0x0204 96 + #define TX_VCID(x) FIELD_PREP(GENMASK(1, 0), x) 97 + #define DSI2_DSI_SCRAMBLING_CFG 0x0208 98 + #define SCRAMBLING_SEED(x) FIELD_PREP(GENMASK(31, 16), x) 99 + #define SCRAMBLING_EN BIT(0) 100 + #define DSI2_DSI_VID_TX_CFG 0x020c 101 + #define LPDT_DISPLAY_CMD_EN BIT(20) 102 + #define BLK_VFP_HS_EN BIT(14) 103 + #define BLK_VBP_HS_EN BIT(13) 104 + #define BLK_VSA_HS_EN BIT(12) 105 + #define BLK_HFP_HS_EN BIT(6) 106 + #define BLK_HBP_HS_EN BIT(5) 107 + #define BLK_HSA_HS_EN BIT(4) 108 + #define VID_MODE_TYPE(x) FIELD_PREP(GENMASK(1, 0), x) 109 + #define DSI2_CRI_TX_HDR 0x02c0 110 + #define CMD_TX_MODE(x) FIELD_PREP(BIT(24), x) 111 + #define DSI2_CRI_TX_PLD 0x02c4 112 + #define DSI2_CRI_RX_HDR 0x02c8 113 + #define DSI2_CRI_RX_PLD 0x02cc 114 + 115 + #define DSI2_IPI_COLOR_MAN_CFG 0x0300 116 + #define IPI_DEPTH(x) FIELD_PREP(GENMASK(7, 4), x) 117 + #define IPI_DEPTH_5_6_5_BITS 0x02 118 + #define IPI_DEPTH_6_BITS 0x03 119 + #define IPI_DEPTH_8_BITS 0x05 120 + #define IPI_DEPTH_10_BITS 0x06 121 + #define IPI_FORMAT(x) FIELD_PREP(GENMASK(3, 0), x) 122 + #define IPI_FORMAT_RGB 0x0 123 + #define IPI_FORMAT_DSC 0x0b 124 + #define DSI2_IPI_VID_HSA_MAN_CFG 0x0304 125 + #define VID_HSA_TIME(x) FIELD_PREP(GENMASK(29, 0), x) 126 + #define DSI2_IPI_VID_HBP_MAN_CFG 0x030c 127 + #define VID_HBP_TIME(x) FIELD_PREP(GENMASK(29, 0), x) 128 + #define DSI2_IPI_VID_HACT_MAN_CFG 0x0314 129 + #define VID_HACT_TIME(x) FIELD_PREP(GENMASK(29, 0), x) 130 + #define DSI2_IPI_VID_HLINE_MAN_CFG 0x031c 131 + #define VID_HLINE_TIME(x) FIELD_PREP(GENMASK(29, 0), x) 132 + #define DSI2_IPI_VID_VSA_MAN_CFG 0x0324 133 + #define VID_VSA_LINES(x) FIELD_PREP(GENMASK(9, 0), x) 134 + #define DSI2_IPI_VID_VBP_MAN_CFG 0X032C 135 + #define VID_VBP_LINES(x) FIELD_PREP(GENMASK(9, 0), x) 136 + #define DSI2_IPI_VID_VACT_MAN_CFG 0X0334 137 + #define VID_VACT_LINES(x) FIELD_PREP(GENMASK(13, 0), x) 138 + #define DSI2_IPI_VID_VFP_MAN_CFG 0X033C 139 + #define VID_VFP_LINES(x) FIELD_PREP(GENMASK(9, 0), x) 140 + #define DSI2_IPI_PIX_PKT_CFG 0x0344 141 + #define MAX_PIX_PKT(x) FIELD_PREP(GENMASK(15, 0), x) 142 + 143 + #define DSI2_INT_ST_PHY 0x0400 144 + #define DSI2_INT_MASK_PHY 0x0404 145 + #define DSI2_INT_ST_TO 0x0410 146 + #define DSI2_INT_MASK_TO 0x0414 147 + #define DSI2_INT_ST_ACK 0x0420 148 + #define DSI2_INT_MASK_ACK 0x0424 149 + #define DSI2_INT_ST_IPI 0x0430 150 + #define DSI2_INT_MASK_IPI 0x0434 151 + #define DSI2_INT_ST_FIFO 0x0440 152 + #define DSI2_INT_MASK_FIFO 0x0444 153 + #define DSI2_INT_ST_PRI 0x0450 154 + #define DSI2_INT_MASK_PRI 0x0454 155 + #define DSI2_INT_ST_CRI 0x0460 156 + #define DSI2_INT_MASK_CRI 0x0464 157 + #define DSI2_INT_FORCE_CRI 0x0468 158 + #define DSI2_MAX_REGISGER DSI2_INT_FORCE_CRI 159 + 160 + #define MODE_STATUS_TIMEOUT_US 10000 161 + #define CMD_PKT_STATUS_TIMEOUT_US 20000 162 + 163 + enum vid_mode_type { 164 + VID_MODE_TYPE_NON_BURST_SYNC_PULSES, 165 + VID_MODE_TYPE_NON_BURST_SYNC_EVENTS, 166 + VID_MODE_TYPE_BURST, 167 + }; 168 + 169 + enum mode_ctrl { 170 + IDLE_MODE, 171 + AUTOCALC_MODE, 172 + COMMAND_MODE, 173 + VIDEO_MODE, 174 + DATA_STREAM_MODE, 175 + VIDEO_TEST_MODE, 176 + DATA_STREAM_TEST_MODE, 177 + }; 178 + 179 + enum ppi_width { 180 + PPI_WIDTH_8_BITS, 181 + PPI_WIDTH_16_BITS, 182 + PPI_WIDTH_32_BITS, 183 + }; 184 + 185 + struct cmd_header { 186 + u8 cmd_type; 187 + u8 delay; 188 + u8 payload_length; 189 + }; 190 + 191 + struct dw_mipi_dsi2 { 192 + struct drm_bridge bridge; 193 + struct mipi_dsi_host dsi_host; 194 + struct drm_bridge *panel_bridge; 195 + struct device *dev; 196 + struct regmap *regmap; 197 + struct clk *pclk; 198 + struct clk *sys_clk; 199 + 200 + unsigned int lane_mbps; /* per lane */ 201 + u32 channel; 202 + u32 lanes; 203 + u32 format; 204 + unsigned long mode_flags; 205 + 206 + struct drm_display_mode mode; 207 + const struct dw_mipi_dsi2_plat_data *plat_data; 208 + }; 209 + 210 + static inline struct dw_mipi_dsi2 *host_to_dsi2(struct mipi_dsi_host *host) 211 + { 212 + return container_of(host, struct dw_mipi_dsi2, dsi_host); 213 + } 214 + 215 + static inline struct dw_mipi_dsi2 *bridge_to_dsi2(struct drm_bridge *bridge) 216 + { 217 + return container_of(bridge, struct dw_mipi_dsi2, bridge); 218 + } 219 + 220 + static int cri_fifos_wait_avail(struct dw_mipi_dsi2 *dsi2) 221 + { 222 + u32 sts, mask; 223 + int ret; 224 + 225 + mask = CRI_BUSY | CRT_FIFOS_NOT_EMPTY; 226 + ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_CORE_STATUS, sts, 227 + !(sts & mask), 0, CMD_PKT_STATUS_TIMEOUT_US); 228 + if (ret < 0) { 229 + dev_err(dsi2->dev, "command interface is busy\n"); 230 + return ret; 231 + } 232 + 233 + return 0; 234 + } 235 + 236 + static void dw_mipi_dsi2_set_vid_mode(struct dw_mipi_dsi2 *dsi2) 237 + { 238 + u32 val = 0, mode; 239 + int ret; 240 + 241 + if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP) 242 + val |= BLK_HFP_HS_EN; 243 + 244 + if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP) 245 + val |= BLK_HBP_HS_EN; 246 + 247 + if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA) 248 + val |= BLK_HSA_HS_EN; 249 + 250 + if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 251 + val |= VID_MODE_TYPE_BURST; 252 + else if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) 253 + val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES; 254 + else 255 + val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS; 256 + 257 + regmap_write(dsi2->regmap, DSI2_DSI_VID_TX_CFG, val); 258 + 259 + regmap_write(dsi2->regmap, DSI2_MODE_CTRL, VIDEO_MODE); 260 + ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_MODE_STATUS, 261 + mode, mode & VIDEO_MODE, 262 + 1000, MODE_STATUS_TIMEOUT_US); 263 + if (ret < 0) 264 + dev_err(dsi2->dev, "failed to enter video mode\n"); 265 + } 266 + 267 + static void dw_mipi_dsi2_set_data_stream_mode(struct dw_mipi_dsi2 *dsi2) 268 + { 269 + u32 mode; 270 + int ret; 271 + 272 + regmap_write(dsi2->regmap, DSI2_MODE_CTRL, DATA_STREAM_MODE); 273 + ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_MODE_STATUS, 274 + mode, mode & DATA_STREAM_MODE, 275 + 1000, MODE_STATUS_TIMEOUT_US); 276 + if (ret < 0) 277 + dev_err(dsi2->dev, "failed to enter data stream mode\n"); 278 + } 279 + 280 + static void dw_mipi_dsi2_set_cmd_mode(struct dw_mipi_dsi2 *dsi2) 281 + { 282 + u32 mode; 283 + int ret; 284 + 285 + regmap_write(dsi2->regmap, DSI2_MODE_CTRL, COMMAND_MODE); 286 + ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_MODE_STATUS, 287 + mode, mode & COMMAND_MODE, 288 + 1000, MODE_STATUS_TIMEOUT_US); 289 + if (ret < 0) 290 + dev_err(dsi2->dev, "failed to enter data stream mode\n"); 291 + } 292 + 293 + static void dw_mipi_dsi2_host_softrst(struct dw_mipi_dsi2 *dsi2) 294 + { 295 + regmap_write(dsi2->regmap, DSI2_SOFT_RESET, 0x0); 296 + usleep_range(50, 100); 297 + regmap_write(dsi2->regmap, DSI2_SOFT_RESET, 298 + SYS_RSTN | PHY_RSTN | IPI_RSTN); 299 + } 300 + 301 + static void dw_mipi_dsi2_phy_clk_mode_cfg(struct dw_mipi_dsi2 *dsi2) 302 + { 303 + u32 sys_clk, esc_clk_div; 304 + u32 val = 0; 305 + 306 + /* 307 + * clk_type should be NON_CONTINUOUS_CLK before 308 + * initial deskew calibration be sent. 309 + */ 310 + val |= NON_CONTINUOUS_CLK; 311 + 312 + /* The maximum value of the escape clock frequency is 20MHz */ 313 + sys_clk = clk_get_rate(dsi2->sys_clk) / USEC_PER_SEC; 314 + esc_clk_div = DIV_ROUND_UP(sys_clk, 20 * 2); 315 + val |= PHY_LPTX_CLK_DIV(esc_clk_div); 316 + 317 + regmap_write(dsi2->regmap, DSI2_PHY_CLK_CFG, val); 318 + } 319 + 320 + static void dw_mipi_dsi2_phy_ratio_cfg(struct dw_mipi_dsi2 *dsi2) 321 + { 322 + struct drm_display_mode *mode = &dsi2->mode; 323 + u64 sys_clk = clk_get_rate(dsi2->sys_clk); 324 + u64 pixel_clk, ipi_clk, phy_hsclk; 325 + u64 tmp; 326 + 327 + /* 328 + * in DPHY mode, the phy_hstx_clk is exactly 1/16 the Lane high-speed 329 + * data rate; In CPHY mode, the phy_hstx_clk is exactly 1/7 the trio 330 + * high speed symbol rate. 331 + */ 332 + phy_hsclk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16); 333 + 334 + /* IPI_RATIO_MAN_CFG = PHY_HSTX_CLK / IPI_CLK */ 335 + pixel_clk = mode->crtc_clock * MSEC_PER_SEC; 336 + ipi_clk = pixel_clk / 4; 337 + 338 + tmp = DIV_ROUND_CLOSEST_ULL(phy_hsclk << 16, ipi_clk); 339 + regmap_write(dsi2->regmap, DSI2_PHY_IPI_RATIO_MAN_CFG, 340 + PHY_IPI_RATIO(tmp)); 341 + 342 + /* 343 + * SYS_RATIO_MAN_CFG = MIPI_DCPHY_HSCLK_Freq / MIPI_DCPHY_HSCLK_Freq 344 + */ 345 + tmp = DIV_ROUND_CLOSEST_ULL(phy_hsclk << 16, sys_clk); 346 + regmap_write(dsi2->regmap, DSI2_PHY_SYS_RATIO_MAN_CFG, 347 + PHY_SYS_RATIO(tmp)); 348 + } 349 + 350 + static void dw_mipi_dsi2_lp2hs_or_hs2lp_cfg(struct dw_mipi_dsi2 *dsi2) 351 + { 352 + const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; 353 + struct dw_mipi_dsi2_phy_timing timing; 354 + int ret; 355 + 356 + ret = phy_ops->get_timing(dsi2->plat_data->priv_data, 357 + dsi2->lane_mbps, &timing); 358 + if (ret) 359 + dev_err(dsi2->dev, "Retrieving phy timings failed\n"); 360 + 361 + regmap_write(dsi2->regmap, DSI2_PHY_LP2HS_MAN_CFG, PHY_LP2HS_TIME(timing.data_lp2hs)); 362 + regmap_write(dsi2->regmap, DSI2_PHY_HS2LP_MAN_CFG, PHY_HS2LP_TIME(timing.data_hs2lp)); 363 + } 364 + 365 + static void dw_mipi_dsi2_phy_init(struct dw_mipi_dsi2 *dsi2) 366 + { 367 + const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; 368 + struct dw_mipi_dsi2_phy_iface iface; 369 + u32 val = 0; 370 + 371 + phy_ops->get_interface(dsi2->plat_data->priv_data, &iface); 372 + 373 + switch (iface.ppi_width) { 374 + case 8: 375 + val |= PPI_WIDTH(PPI_WIDTH_8_BITS); 376 + break; 377 + case 16: 378 + val |= PPI_WIDTH(PPI_WIDTH_16_BITS); 379 + break; 380 + case 32: 381 + val |= PPI_WIDTH(PPI_WIDTH_32_BITS); 382 + break; 383 + default: 384 + /* Caught in probe */ 385 + break; 386 + } 387 + 388 + val |= PHY_LANES(dsi2->lanes); 389 + val |= PHY_TYPE(DW_MIPI_DSI2_DPHY); 390 + regmap_write(dsi2->regmap, DSI2_PHY_MODE_CFG, val); 391 + 392 + dw_mipi_dsi2_phy_clk_mode_cfg(dsi2); 393 + dw_mipi_dsi2_phy_ratio_cfg(dsi2); 394 + dw_mipi_dsi2_lp2hs_or_hs2lp_cfg(dsi2); 395 + 396 + /* phy configuration 8 - 10 */ 397 + } 398 + 399 + static void dw_mipi_dsi2_tx_option_set(struct dw_mipi_dsi2 *dsi2) 400 + { 401 + u32 val; 402 + 403 + val = BTA_EN | EOTP_TX_EN; 404 + 405 + if (dsi2->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) 406 + val &= ~EOTP_TX_EN; 407 + 408 + regmap_write(dsi2->regmap, DSI2_DSI_GENERAL_CFG, val); 409 + regmap_write(dsi2->regmap, DSI2_DSI_VCID_CFG, TX_VCID(dsi2->channel)); 410 + } 411 + 412 + static void dw_mipi_dsi2_ipi_color_coding_cfg(struct dw_mipi_dsi2 *dsi2) 413 + { 414 + u32 val, color_depth; 415 + 416 + switch (dsi2->format) { 417 + case MIPI_DSI_FMT_RGB666: 418 + case MIPI_DSI_FMT_RGB666_PACKED: 419 + color_depth = IPI_DEPTH_6_BITS; 420 + break; 421 + case MIPI_DSI_FMT_RGB565: 422 + color_depth = IPI_DEPTH_5_6_5_BITS; 423 + break; 424 + case MIPI_DSI_FMT_RGB888: 425 + default: 426 + color_depth = IPI_DEPTH_8_BITS; 427 + break; 428 + } 429 + 430 + val = IPI_DEPTH(color_depth) | 431 + IPI_FORMAT(IPI_FORMAT_RGB); 432 + regmap_write(dsi2->regmap, DSI2_IPI_COLOR_MAN_CFG, val); 433 + } 434 + 435 + static void dw_mipi_dsi2_vertical_timing_config(struct dw_mipi_dsi2 *dsi2, 436 + const struct drm_display_mode *mode) 437 + { 438 + u32 vactive, vsa, vfp, vbp; 439 + 440 + vactive = mode->vdisplay; 441 + vsa = mode->vsync_end - mode->vsync_start; 442 + vfp = mode->vsync_start - mode->vdisplay; 443 + vbp = mode->vtotal - mode->vsync_end; 444 + 445 + regmap_write(dsi2->regmap, DSI2_IPI_VID_VSA_MAN_CFG, VID_VSA_LINES(vsa)); 446 + regmap_write(dsi2->regmap, DSI2_IPI_VID_VBP_MAN_CFG, VID_VBP_LINES(vbp)); 447 + regmap_write(dsi2->regmap, DSI2_IPI_VID_VACT_MAN_CFG, VID_VACT_LINES(vactive)); 448 + regmap_write(dsi2->regmap, DSI2_IPI_VID_VFP_MAN_CFG, VID_VFP_LINES(vfp)); 449 + } 450 + 451 + static void dw_mipi_dsi2_ipi_set(struct dw_mipi_dsi2 *dsi2) 452 + { 453 + struct drm_display_mode *mode = &dsi2->mode; 454 + u32 hline, hsa, hbp, hact; 455 + u64 hline_time, hsa_time, hbp_time, hact_time, tmp; 456 + u64 pixel_clk, phy_hs_clk; 457 + u16 val; 458 + 459 + val = mode->hdisplay; 460 + 461 + regmap_write(dsi2->regmap, DSI2_IPI_PIX_PKT_CFG, MAX_PIX_PKT(val)); 462 + 463 + dw_mipi_dsi2_ipi_color_coding_cfg(dsi2); 464 + 465 + /* 466 + * if the controller is intended to operate in data stream mode, 467 + * no more steps are required. 468 + */ 469 + if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) 470 + return; 471 + 472 + hact = mode->hdisplay; 473 + hsa = mode->hsync_end - mode->hsync_start; 474 + hbp = mode->htotal - mode->hsync_end; 475 + hline = mode->htotal; 476 + 477 + pixel_clk = mode->crtc_clock * MSEC_PER_SEC; 478 + 479 + phy_hs_clk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16); 480 + 481 + tmp = hsa * phy_hs_clk; 482 + hsa_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); 483 + regmap_write(dsi2->regmap, DSI2_IPI_VID_HSA_MAN_CFG, VID_HSA_TIME(hsa_time)); 484 + 485 + tmp = hbp * phy_hs_clk; 486 + hbp_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); 487 + regmap_write(dsi2->regmap, DSI2_IPI_VID_HBP_MAN_CFG, VID_HBP_TIME(hbp_time)); 488 + 489 + tmp = hact * phy_hs_clk; 490 + hact_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); 491 + regmap_write(dsi2->regmap, DSI2_IPI_VID_HACT_MAN_CFG, VID_HACT_TIME(hact_time)); 492 + 493 + tmp = hline * phy_hs_clk; 494 + hline_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); 495 + regmap_write(dsi2->regmap, DSI2_IPI_VID_HLINE_MAN_CFG, VID_HLINE_TIME(hline_time)); 496 + 497 + dw_mipi_dsi2_vertical_timing_config(dsi2, mode); 498 + } 499 + 500 + static void 501 + dw_mipi_dsi2_work_mode(struct dw_mipi_dsi2 *dsi2, u32 mode) 502 + { 503 + /* 504 + * select controller work in Manual mode 505 + * Manual: MANUAL_MODE_EN 506 + * Automatic: 0 507 + */ 508 + regmap_write(dsi2->regmap, MANUAL_MODE_CFG, mode); 509 + } 510 + 511 + static int dw_mipi_dsi2_host_attach(struct mipi_dsi_host *host, 512 + struct mipi_dsi_device *device) 513 + { 514 + struct dw_mipi_dsi2 *dsi2 = host_to_dsi2(host); 515 + const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; 516 + struct drm_bridge *bridge; 517 + int ret; 518 + 519 + if (device->lanes > dsi2->plat_data->max_data_lanes) { 520 + dev_err(dsi2->dev, "the number of data lanes(%u) is too many\n", 521 + device->lanes); 522 + return -EINVAL; 523 + } 524 + 525 + dsi2->lanes = device->lanes; 526 + dsi2->channel = device->channel; 527 + dsi2->format = device->format; 528 + dsi2->mode_flags = device->mode_flags; 529 + 530 + bridge = devm_drm_of_get_bridge(dsi2->dev, dsi2->dev->of_node, 1, 0); 531 + if (IS_ERR(bridge)) 532 + return PTR_ERR(bridge); 533 + 534 + bridge->pre_enable_prev_first = true; 535 + dsi2->panel_bridge = bridge; 536 + 537 + drm_bridge_add(&dsi2->bridge); 538 + 539 + if (pdata->host_ops && pdata->host_ops->attach) { 540 + ret = pdata->host_ops->attach(pdata->priv_data, device); 541 + if (ret < 0) 542 + return ret; 543 + } 544 + 545 + return 0; 546 + } 547 + 548 + static int dw_mipi_dsi2_host_detach(struct mipi_dsi_host *host, 549 + struct mipi_dsi_device *device) 550 + { 551 + struct dw_mipi_dsi2 *dsi2 = host_to_dsi2(host); 552 + const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; 553 + int ret; 554 + 555 + if (pdata->host_ops && pdata->host_ops->detach) { 556 + ret = pdata->host_ops->detach(pdata->priv_data, device); 557 + if (ret < 0) 558 + return ret; 559 + } 560 + 561 + drm_bridge_remove(&dsi2->bridge); 562 + 563 + drm_of_panel_bridge_remove(host->dev->of_node, 1, 0); 564 + 565 + return 0; 566 + } 567 + 568 + static int dw_mipi_dsi2_gen_pkt_hdr_write(struct dw_mipi_dsi2 *dsi2, 569 + u32 hdr_val, bool lpm) 570 + { 571 + int ret; 572 + 573 + regmap_write(dsi2->regmap, DSI2_CRI_TX_HDR, hdr_val | CMD_TX_MODE(lpm)); 574 + 575 + ret = cri_fifos_wait_avail(dsi2); 576 + if (ret) { 577 + dev_err(dsi2->dev, "failed to write command header\n"); 578 + return ret; 579 + } 580 + 581 + return 0; 582 + } 583 + 584 + static int dw_mipi_dsi2_write(struct dw_mipi_dsi2 *dsi2, 585 + const struct mipi_dsi_packet *packet, bool lpm) 586 + { 587 + const u8 *tx_buf = packet->payload; 588 + int len = packet->payload_length, pld_data_bytes = sizeof(u32); 589 + __le32 word; 590 + 591 + /* Send payload */ 592 + while (len) { 593 + if (len < pld_data_bytes) { 594 + word = 0; 595 + memcpy(&word, tx_buf, len); 596 + regmap_write(dsi2->regmap, DSI2_CRI_TX_PLD, le32_to_cpu(word)); 597 + len = 0; 598 + } else { 599 + memcpy(&word, tx_buf, pld_data_bytes); 600 + regmap_write(dsi2->regmap, DSI2_CRI_TX_PLD, le32_to_cpu(word)); 601 + tx_buf += pld_data_bytes; 602 + len -= pld_data_bytes; 603 + } 604 + } 605 + 606 + word = 0; 607 + memcpy(&word, packet->header, sizeof(packet->header)); 608 + return dw_mipi_dsi2_gen_pkt_hdr_write(dsi2, le32_to_cpu(word), lpm); 609 + } 610 + 611 + static int dw_mipi_dsi2_read(struct dw_mipi_dsi2 *dsi2, 612 + const struct mipi_dsi_msg *msg) 613 + { 614 + u8 *payload = msg->rx_buf; 615 + int i, j, ret, len = msg->rx_len; 616 + u8 data_type; 617 + u16 wc; 618 + u32 val; 619 + 620 + ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_CORE_STATUS, 621 + val, val & CRI_RD_DATA_AVAIL, 622 + 100, CMD_PKT_STATUS_TIMEOUT_US); 623 + if (ret) { 624 + dev_err(dsi2->dev, "CRI has no available read data\n"); 625 + return ret; 626 + } 627 + 628 + regmap_read(dsi2->regmap, DSI2_CRI_RX_HDR, &val); 629 + data_type = val & 0x3f; 630 + 631 + if (mipi_dsi_packet_format_is_short(data_type)) { 632 + for (i = 0; i < len && i < 2; i++) 633 + payload[i] = (val >> (8 * (i + 1))) & 0xff; 634 + 635 + return 0; 636 + } 637 + 638 + wc = (val >> 8) & 0xffff; 639 + /* Receive payload */ 640 + for (i = 0; i < len && i < wc; i += 4) { 641 + regmap_read(dsi2->regmap, DSI2_CRI_RX_PLD, &val); 642 + for (j = 0; j < 4 && j + i < len && j + i < wc; j++) 643 + payload[i + j] = val >> (8 * j); 644 + } 645 + 646 + return 0; 647 + } 648 + 649 + static ssize_t dw_mipi_dsi2_host_transfer(struct mipi_dsi_host *host, 650 + const struct mipi_dsi_msg *msg) 651 + { 652 + struct dw_mipi_dsi2 *dsi2 = host_to_dsi2(host); 653 + bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM; 654 + struct mipi_dsi_packet packet; 655 + int ret, nb_bytes; 656 + 657 + regmap_update_bits(dsi2->regmap, DSI2_DSI_VID_TX_CFG, 658 + LPDT_DISPLAY_CMD_EN, 659 + lpm ? LPDT_DISPLAY_CMD_EN : 0); 660 + 661 + /* create a packet to the DSI protocol */ 662 + ret = mipi_dsi_create_packet(&packet, msg); 663 + if (ret) { 664 + dev_err(dsi2->dev, "failed to create packet: %d\n", ret); 665 + return ret; 666 + } 667 + 668 + ret = cri_fifos_wait_avail(dsi2); 669 + if (ret) 670 + return ret; 671 + 672 + ret = dw_mipi_dsi2_write(dsi2, &packet, lpm); 673 + if (ret) 674 + return ret; 675 + 676 + if (msg->rx_buf && msg->rx_len) { 677 + ret = dw_mipi_dsi2_read(dsi2, msg); 678 + if (ret < 0) 679 + return ret; 680 + nb_bytes = msg->rx_len; 681 + } else { 682 + nb_bytes = packet.size; 683 + } 684 + 685 + return nb_bytes; 686 + } 687 + 688 + static const struct mipi_dsi_host_ops dw_mipi_dsi2_host_ops = { 689 + .attach = dw_mipi_dsi2_host_attach, 690 + .detach = dw_mipi_dsi2_host_detach, 691 + .transfer = dw_mipi_dsi2_host_transfer, 692 + }; 693 + 694 + static u32 * 695 + dw_mipi_dsi2_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, 696 + struct drm_bridge_state *bridge_state, 697 + struct drm_crtc_state *crtc_state, 698 + struct drm_connector_state *conn_state, 699 + u32 output_fmt, 700 + unsigned int *num_input_fmts) 701 + { 702 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 703 + const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; 704 + u32 *input_fmts; 705 + 706 + if (pdata->get_input_bus_fmts) 707 + return pdata->get_input_bus_fmts(pdata->priv_data, 708 + bridge, bridge_state, 709 + crtc_state, conn_state, 710 + output_fmt, num_input_fmts); 711 + 712 + /* Fall back to MEDIA_BUS_FMT_FIXED as the only input format. */ 713 + input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL); 714 + if (!input_fmts) 715 + return NULL; 716 + input_fmts[0] = MEDIA_BUS_FMT_FIXED; 717 + *num_input_fmts = 1; 718 + 719 + return input_fmts; 720 + } 721 + 722 + static int dw_mipi_dsi2_bridge_atomic_check(struct drm_bridge *bridge, 723 + struct drm_bridge_state *bridge_state, 724 + struct drm_crtc_state *crtc_state, 725 + struct drm_connector_state *conn_state) 726 + { 727 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 728 + const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; 729 + bool ret; 730 + 731 + bridge_state->input_bus_cfg.flags = 732 + DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE; 733 + 734 + if (pdata->mode_fixup) { 735 + ret = pdata->mode_fixup(pdata->priv_data, &crtc_state->mode, 736 + &crtc_state->adjusted_mode); 737 + if (!ret) { 738 + DRM_DEBUG_DRIVER("failed to fixup mode " DRM_MODE_FMT "\n", 739 + DRM_MODE_ARG(&crtc_state->mode)); 740 + return -EINVAL; 741 + } 742 + } 743 + 744 + return 0; 745 + } 746 + 747 + static void dw_mipi_dsi2_bridge_post_atomic_disable(struct drm_bridge *bridge, 748 + struct drm_bridge_state *old_bridge_state) 749 + { 750 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 751 + const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; 752 + 753 + regmap_write(dsi2->regmap, DSI2_IPI_PIX_PKT_CFG, 0); 754 + 755 + /* 756 + * Switch to command mode before panel-bridge post_disable & 757 + * panel unprepare. 758 + * Note: panel-bridge disable & panel disable has been called 759 + * before by the drm framework. 760 + */ 761 + dw_mipi_dsi2_set_cmd_mode(dsi2); 762 + 763 + regmap_write(dsi2->regmap, DSI2_PWR_UP, RESET); 764 + 765 + if (phy_ops->power_off) 766 + phy_ops->power_off(dsi2->plat_data->priv_data); 767 + 768 + clk_disable_unprepare(dsi2->sys_clk); 769 + clk_disable_unprepare(dsi2->pclk); 770 + pm_runtime_put(dsi2->dev); 771 + } 772 + 773 + static unsigned int dw_mipi_dsi2_get_lanes(struct dw_mipi_dsi2 *dsi2) 774 + { 775 + /* single-dsi, so no other instance to consider */ 776 + return dsi2->lanes; 777 + } 778 + 779 + static void dw_mipi_dsi2_mode_set(struct dw_mipi_dsi2 *dsi2, 780 + const struct drm_display_mode *adjusted_mode) 781 + { 782 + const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; 783 + void *priv_data = dsi2->plat_data->priv_data; 784 + u32 lanes = dw_mipi_dsi2_get_lanes(dsi2); 785 + int ret; 786 + 787 + clk_prepare_enable(dsi2->pclk); 788 + clk_prepare_enable(dsi2->sys_clk); 789 + 790 + ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi2->mode_flags, 791 + lanes, dsi2->format, &dsi2->lane_mbps); 792 + if (ret) 793 + DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n"); 794 + 795 + pm_runtime_get_sync(dsi2->dev); 796 + 797 + dw_mipi_dsi2_host_softrst(dsi2); 798 + regmap_write(dsi2->regmap, DSI2_PWR_UP, RESET); 799 + 800 + dw_mipi_dsi2_work_mode(dsi2, MANUAL_MODE_EN); 801 + dw_mipi_dsi2_phy_init(dsi2); 802 + 803 + if (phy_ops->power_on) 804 + phy_ops->power_on(dsi2->plat_data->priv_data); 805 + 806 + dw_mipi_dsi2_tx_option_set(dsi2); 807 + 808 + /* 809 + * initial deskew calibration is send after phy_power_on, 810 + * then we can configure clk_type. 811 + */ 812 + 813 + regmap_update_bits(dsi2->regmap, DSI2_PHY_CLK_CFG, CLK_TYPE_MASK, 814 + dsi2->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS ? NON_CONTINUOUS_CLK : 815 + CONTINUOUS_CLK); 816 + 817 + regmap_write(dsi2->regmap, DSI2_PWR_UP, POWER_UP); 818 + dw_mipi_dsi2_set_cmd_mode(dsi2); 819 + 820 + dw_mipi_dsi2_ipi_set(dsi2); 821 + } 822 + 823 + static void dw_mipi_dsi2_bridge_atomic_pre_enable(struct drm_bridge *bridge, 824 + struct drm_bridge_state *old_bridge_state) 825 + { 826 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 827 + 828 + /* Power up the dsi ctl into a command mode */ 829 + dw_mipi_dsi2_mode_set(dsi2, &dsi2->mode); 830 + } 831 + 832 + static void dw_mipi_dsi2_bridge_mode_set(struct drm_bridge *bridge, 833 + const struct drm_display_mode *mode, 834 + const struct drm_display_mode *adjusted_mode) 835 + { 836 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 837 + 838 + /* Store the display mode for later use in pre_enable callback */ 839 + drm_mode_copy(&dsi2->mode, adjusted_mode); 840 + } 841 + 842 + static void dw_mipi_dsi2_bridge_atomic_enable(struct drm_bridge *bridge, 843 + struct drm_bridge_state *old_bridge_state) 844 + { 845 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 846 + 847 + /* Switch to video mode for panel-bridge enable & panel enable */ 848 + if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO) 849 + dw_mipi_dsi2_set_vid_mode(dsi2); 850 + else 851 + dw_mipi_dsi2_set_data_stream_mode(dsi2); 852 + } 853 + 854 + static enum drm_mode_status 855 + dw_mipi_dsi2_bridge_mode_valid(struct drm_bridge *bridge, 856 + const struct drm_display_info *info, 857 + const struct drm_display_mode *mode) 858 + { 859 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 860 + const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; 861 + enum drm_mode_status mode_status = MODE_OK; 862 + 863 + if (pdata->mode_valid) 864 + mode_status = pdata->mode_valid(pdata->priv_data, mode, 865 + dsi2->mode_flags, 866 + dw_mipi_dsi2_get_lanes(dsi2), 867 + dsi2->format); 868 + 869 + return mode_status; 870 + } 871 + 872 + static int dw_mipi_dsi2_bridge_attach(struct drm_bridge *bridge, 873 + enum drm_bridge_attach_flags flags) 874 + { 875 + struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); 876 + 877 + /* Set the encoder type as caller does not know it */ 878 + bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI; 879 + 880 + /* Attach the panel-bridge to the dsi bridge */ 881 + return drm_bridge_attach(bridge->encoder, dsi2->panel_bridge, bridge, 882 + flags); 883 + } 884 + 885 + static const struct drm_bridge_funcs dw_mipi_dsi2_bridge_funcs = { 886 + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 887 + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 888 + .atomic_get_input_bus_fmts = dw_mipi_dsi2_bridge_atomic_get_input_bus_fmts, 889 + .atomic_check = dw_mipi_dsi2_bridge_atomic_check, 890 + .atomic_reset = drm_atomic_helper_bridge_reset, 891 + .atomic_pre_enable = dw_mipi_dsi2_bridge_atomic_pre_enable, 892 + .atomic_enable = dw_mipi_dsi2_bridge_atomic_enable, 893 + .atomic_post_disable = dw_mipi_dsi2_bridge_post_atomic_disable, 894 + .mode_set = dw_mipi_dsi2_bridge_mode_set, 895 + .mode_valid = dw_mipi_dsi2_bridge_mode_valid, 896 + .attach = dw_mipi_dsi2_bridge_attach, 897 + }; 898 + 899 + static const struct regmap_config dw_mipi_dsi2_regmap_config = { 900 + .name = "dsi2-host", 901 + .reg_bits = 32, 902 + .val_bits = 32, 903 + .reg_stride = 4, 904 + .fast_io = true, 905 + }; 906 + 907 + static struct dw_mipi_dsi2 * 908 + __dw_mipi_dsi2_probe(struct platform_device *pdev, 909 + const struct dw_mipi_dsi2_plat_data *plat_data) 910 + { 911 + struct device *dev = &pdev->dev; 912 + struct reset_control *apb_rst; 913 + struct dw_mipi_dsi2 *dsi2; 914 + int ret; 915 + 916 + dsi2 = devm_kzalloc(dev, sizeof(*dsi2), GFP_KERNEL); 917 + if (!dsi2) 918 + return ERR_PTR(-ENOMEM); 919 + 920 + dsi2->dev = dev; 921 + dsi2->plat_data = plat_data; 922 + 923 + if (!plat_data->phy_ops->init || !plat_data->phy_ops->get_lane_mbps || 924 + !plat_data->phy_ops->get_timing) 925 + return dev_err_ptr_probe(dev, -ENODEV, "Phy not properly configured\n"); 926 + 927 + if (!plat_data->regmap) { 928 + void __iomem *base = devm_platform_ioremap_resource(pdev, 0); 929 + 930 + if (IS_ERR(base)) 931 + return dev_err_cast_probe(dev, base, "failed to registers\n"); 932 + 933 + dsi2->regmap = devm_regmap_init_mmio(dev, base, 934 + &dw_mipi_dsi2_regmap_config); 935 + if (IS_ERR(dsi2->regmap)) 936 + return dev_err_cast_probe(dev, dsi2->regmap, "failed to init regmap\n"); 937 + } else { 938 + dsi2->regmap = plat_data->regmap; 939 + } 940 + 941 + dsi2->pclk = devm_clk_get(dev, "pclk"); 942 + if (IS_ERR(dsi2->pclk)) 943 + return dev_err_cast_probe(dev, dsi2->pclk, "Unable to get pclk\n"); 944 + 945 + dsi2->sys_clk = devm_clk_get(dev, "sys"); 946 + if (IS_ERR(dsi2->sys_clk)) 947 + return dev_err_cast_probe(dev, dsi2->sys_clk, "Unable to get sys_clk\n"); 948 + 949 + /* 950 + * Note that the reset was not defined in the initial device tree, so 951 + * we have to be prepared for it not being found. 952 + */ 953 + apb_rst = devm_reset_control_get_optional_exclusive(dev, "apb"); 954 + if (IS_ERR(apb_rst)) 955 + return dev_err_cast_probe(dev, apb_rst, "Unable to get reset control\n"); 956 + 957 + if (apb_rst) { 958 + ret = clk_prepare_enable(dsi2->pclk); 959 + if (ret) { 960 + dev_err(dev, "%s: Failed to enable pclk\n", __func__); 961 + return ERR_PTR(ret); 962 + } 963 + 964 + reset_control_assert(apb_rst); 965 + usleep_range(10, 20); 966 + reset_control_deassert(apb_rst); 967 + 968 + clk_disable_unprepare(dsi2->pclk); 969 + } 970 + 971 + devm_pm_runtime_enable(dev); 972 + 973 + dsi2->dsi_host.ops = &dw_mipi_dsi2_host_ops; 974 + dsi2->dsi_host.dev = dev; 975 + ret = mipi_dsi_host_register(&dsi2->dsi_host); 976 + if (ret) { 977 + dev_err(dev, "Failed to register MIPI host: %d\n", ret); 978 + pm_runtime_disable(dev); 979 + return ERR_PTR(ret); 980 + } 981 + 982 + dsi2->bridge.driver_private = dsi2; 983 + dsi2->bridge.funcs = &dw_mipi_dsi2_bridge_funcs; 984 + dsi2->bridge.of_node = pdev->dev.of_node; 985 + 986 + return dsi2; 987 + } 988 + 989 + static void __dw_mipi_dsi2_remove(struct dw_mipi_dsi2 *dsi2) 990 + { 991 + mipi_dsi_host_unregister(&dsi2->dsi_host); 992 + } 993 + 994 + /* 995 + * Probe/remove API, used to create the bridge instance. 996 + */ 997 + struct dw_mipi_dsi2 * 998 + dw_mipi_dsi2_probe(struct platform_device *pdev, 999 + const struct dw_mipi_dsi2_plat_data *plat_data) 1000 + { 1001 + return __dw_mipi_dsi2_probe(pdev, plat_data); 1002 + } 1003 + EXPORT_SYMBOL_GPL(dw_mipi_dsi2_probe); 1004 + 1005 + void dw_mipi_dsi2_remove(struct dw_mipi_dsi2 *dsi2) 1006 + { 1007 + __dw_mipi_dsi2_remove(dsi2); 1008 + } 1009 + EXPORT_SYMBOL_GPL(dw_mipi_dsi2_remove); 1010 + 1011 + /* 1012 + * Bind/unbind API, used from platforms based on the component framework 1013 + * to attach the bridge to an encoder. 1014 + */ 1015 + int dw_mipi_dsi2_bind(struct dw_mipi_dsi2 *dsi2, struct drm_encoder *encoder) 1016 + { 1017 + return drm_bridge_attach(encoder, &dsi2->bridge, NULL, 0); 1018 + } 1019 + EXPORT_SYMBOL_GPL(dw_mipi_dsi2_bind); 1020 + 1021 + void dw_mipi_dsi2_unbind(struct dw_mipi_dsi2 *dsi2) 1022 + { 1023 + } 1024 + EXPORT_SYMBOL_GPL(dw_mipi_dsi2_unbind); 1025 + 1026 + MODULE_AUTHOR("Guochun Huang <hero.huang@rock-chips.com>"); 1027 + MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@cherry.de>"); 1028 + MODULE_DESCRIPTION("DW MIPI DSI2 host controller driver"); 1029 + MODULE_LICENSE("GPL"); 1030 + MODULE_ALIAS("platform:dw-mipi-dsi2");
+1 -1
drivers/gpu/drm/bridge/tc358767.c
··· 2587 2587 } 2588 2588 2589 2589 static const struct i2c_device_id tc358767_i2c_ids[] = { 2590 - { "tc358767", 0 }, 2590 + { "tc358767" }, 2591 2591 { } 2592 2592 }; 2593 2593 MODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids);
+2 -2
drivers/gpu/drm/bridge/tc358768.c
··· 1244 1244 }; 1245 1245 1246 1246 static const struct i2c_device_id tc358768_i2c_ids[] = { 1247 - { "tc358768", 0 }, 1248 - { "tc358778", 0 }, 1247 + { "tc358768" }, 1248 + { "tc358778" }, 1249 1249 { } 1250 1250 }; 1251 1251 MODULE_DEVICE_TABLE(i2c, tc358768_i2c_ids);
+1 -1
drivers/gpu/drm/bridge/ti-dlpc3433.c
··· 389 389 } 390 390 391 391 static const struct i2c_device_id dlpc3433_id[] = { 392 - { "ti,dlpc3433", 0 }, 392 + { "ti,dlpc3433" }, 393 393 { /* sentinel */ } 394 394 }; 395 395 MODULE_DEVICE_TABLE(i2c, dlpc3433_id);
+143 -4
drivers/gpu/drm/bridge/ti-sn65dsi83.c
··· 132 132 #define REG_IRQ_STAT_CHA_SOT_BIT_ERR BIT(2) 133 133 #define REG_IRQ_STAT_CHA_PLL_UNLOCK BIT(0) 134 134 135 + enum sn65dsi83_channel { 136 + CHANNEL_A, 137 + CHANNEL_B 138 + }; 139 + 140 + enum sn65dsi83_lvds_term { 141 + OHM_100, 142 + OHM_200 143 + }; 144 + 135 145 enum sn65dsi83_model { 136 146 MODEL_SN65DSI83, 137 147 MODEL_SN65DSI84, ··· 157 147 struct regulator *vcc; 158 148 bool lvds_dual_link; 159 149 bool lvds_dual_link_even_odd_swap; 150 + int lvds_vod_swing_conf[2]; 151 + int lvds_term_conf[2]; 160 152 }; 161 153 162 154 static const struct regmap_range sn65dsi83_readable_ranges[] = { ··· 247 235 .volatile_table = &sn65dsi83_volatile_table, 248 236 .cache_type = REGCACHE_MAPLE, 249 237 .max_register = REG_IRQ_STAT, 238 + }; 239 + 240 + static const int lvds_vod_swing_data_table[2][4][2] = { 241 + { /* 100 Ohm */ 242 + { 180000, 313000 }, 243 + { 215000, 372000 }, 244 + { 250000, 430000 }, 245 + { 290000, 488000 }, 246 + }, 247 + { /* 200 Ohm */ 248 + { 150000, 261000 }, 249 + { 200000, 346000 }, 250 + { 250000, 428000 }, 251 + { 300000, 511000 }, 252 + }, 253 + }; 254 + 255 + static const int lvds_vod_swing_clock_table[2][4][2] = { 256 + { /* 100 Ohm */ 257 + { 140000, 244000 }, 258 + { 168000, 290000 }, 259 + { 195000, 335000 }, 260 + { 226000, 381000 }, 261 + }, 262 + { /* 200 Ohm */ 263 + { 117000, 204000 }, 264 + { 156000, 270000 }, 265 + { 195000, 334000 }, 266 + { 234000, 399000 }, 267 + }, 250 268 }; 251 269 252 270 static struct sn65dsi83 *bridge_to_sn65dsi83(struct drm_bridge *bridge) ··· 477 435 val |= REG_LVDS_FMT_LVDS_LINK_CFG; 478 436 479 437 regmap_write(ctx->regmap, REG_LVDS_FMT, val); 480 - regmap_write(ctx->regmap, REG_LVDS_VCOM, 0x05); 438 + regmap_write(ctx->regmap, REG_LVDS_VCOM, 439 + REG_LVDS_VCOM_CHA_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_A]) | 440 + REG_LVDS_VCOM_CHB_LVDS_VOD_SWING(ctx->lvds_vod_swing_conf[CHANNEL_B])); 481 441 regmap_write(ctx->regmap, REG_LVDS_LANE, 482 442 (ctx->lvds_dual_link_even_odd_swap ? 483 443 REG_LVDS_LANE_EVEN_ODD_SWAP : 0) | 484 - REG_LVDS_LANE_CHA_LVDS_TERM | 485 - REG_LVDS_LANE_CHB_LVDS_TERM); 444 + (ctx->lvds_term_conf[CHANNEL_A] ? 445 + REG_LVDS_LANE_CHA_LVDS_TERM : 0) | 446 + (ctx->lvds_term_conf[CHANNEL_B] ? 447 + REG_LVDS_LANE_CHB_LVDS_TERM : 0)); 486 448 regmap_write(ctx->regmap, REG_LVDS_CM, 0x00); 487 449 488 450 le16val = cpu_to_le16(mode->hdisplay); ··· 622 576 .atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts, 623 577 }; 624 578 579 + static int sn65dsi83_select_lvds_vod_swing(struct device *dev, 580 + u32 lvds_vod_swing_data[2], u32 lvds_vod_swing_clk[2], u8 lvds_term) 581 + { 582 + int i; 583 + 584 + for (i = 0; i <= 3; i++) { 585 + if (lvds_vod_swing_data_table[lvds_term][i][0] >= lvds_vod_swing_data[0] && 586 + lvds_vod_swing_data_table[lvds_term][i][1] <= lvds_vod_swing_data[1] && 587 + lvds_vod_swing_clock_table[lvds_term][i][0] >= lvds_vod_swing_clk[0] && 588 + lvds_vod_swing_clock_table[lvds_term][i][1] <= lvds_vod_swing_clk[1]) 589 + return i; 590 + } 591 + 592 + dev_err(dev, "failed to find appropriate LVDS_VOD_SWING configuration\n"); 593 + return -EINVAL; 594 + } 595 + 596 + static int sn65dsi83_parse_lvds_endpoint(struct sn65dsi83 *ctx, int channel) 597 + { 598 + struct device *dev = ctx->dev; 599 + struct device_node *endpoint; 600 + int endpoint_reg; 601 + /* Set so the property can be freely selected if not defined */ 602 + u32 lvds_vod_swing_data[2] = { 0, 1000000 }; 603 + u32 lvds_vod_swing_clk[2] = { 0, 1000000 }; 604 + /* Set default near end terminataion to 200 Ohm */ 605 + u32 lvds_term = 200; 606 + int lvds_vod_swing_conf; 607 + int ret = 0; 608 + int ret_data; 609 + int ret_clock; 610 + 611 + if (channel == CHANNEL_A) 612 + endpoint_reg = 2; 613 + else 614 + endpoint_reg = 3; 615 + 616 + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, endpoint_reg, -1); 617 + 618 + of_property_read_u32(endpoint, "ti,lvds-termination-ohms", &lvds_term); 619 + if (lvds_term == 100) 620 + ctx->lvds_term_conf[channel] = OHM_100; 621 + else if (lvds_term == 200) 622 + ctx->lvds_term_conf[channel] = OHM_200; 623 + else { 624 + ret = -EINVAL; 625 + goto exit; 626 + } 627 + 628 + ret_data = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-data-microvolt", 629 + lvds_vod_swing_data, ARRAY_SIZE(lvds_vod_swing_data)); 630 + if (ret_data != 0 && ret_data != -EINVAL) { 631 + ret = ret_data; 632 + goto exit; 633 + } 634 + 635 + ret_clock = of_property_read_u32_array(endpoint, "ti,lvds-vod-swing-clock-microvolt", 636 + lvds_vod_swing_clk, ARRAY_SIZE(lvds_vod_swing_clk)); 637 + if (ret_clock != 0 && ret_clock != -EINVAL) { 638 + ret = ret_clock; 639 + goto exit; 640 + } 641 + 642 + /* Use default value if both properties are NOT defined. */ 643 + if (ret_data == -EINVAL && ret_clock == -EINVAL) 644 + lvds_vod_swing_conf = 0x1; 645 + 646 + /* Use lookup table if any of the two properties is defined. */ 647 + if (!ret_data || !ret_clock) { 648 + lvds_vod_swing_conf = sn65dsi83_select_lvds_vod_swing(dev, lvds_vod_swing_data, 649 + lvds_vod_swing_clk, ctx->lvds_term_conf[channel]); 650 + if (lvds_vod_swing_conf < 0) { 651 + ret = lvds_vod_swing_conf; 652 + goto exit; 653 + } 654 + } 655 + 656 + ctx->lvds_vod_swing_conf[channel] = lvds_vod_swing_conf; 657 + ret = 0; 658 + exit: 659 + of_node_put(endpoint); 660 + return ret; 661 + } 662 + 625 663 static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model) 626 664 { 627 665 struct drm_bridge *panel_bridge; 628 666 struct device *dev = ctx->dev; 667 + int ret; 668 + 669 + ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_A); 670 + if (ret < 0) 671 + return ret; 672 + 673 + ret = sn65dsi83_parse_lvds_endpoint(ctx, CHANNEL_B); 674 + if (ret < 0) 675 + return ret; 629 676 630 677 ctx->lvds_dual_link = false; 631 678 ctx->lvds_dual_link_even_odd_swap = false; ··· 745 606 746 607 panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0); 747 608 if (IS_ERR(panel_bridge)) 748 - return PTR_ERR(panel_bridge); 609 + return dev_err_probe(dev, PTR_ERR(panel_bridge), "Failed to get panel bridge\n"); 749 610 750 611 ctx->panel_bridge = panel_bridge; 751 612
+2 -2
drivers/gpu/drm/bridge/ti-sn65dsi86.c
··· 1971 1971 } 1972 1972 1973 1973 static const struct i2c_device_id ti_sn65dsi86_id[] = { 1974 - { "ti,sn65dsi86", 0}, 1975 - {}, 1974 + { "ti,sn65dsi86" }, 1975 + {} 1976 1976 }; 1977 1977 MODULE_DEVICE_TABLE(i2c, ti_sn65dsi86_id); 1978 1978
+1 -1
drivers/gpu/drm/bridge/ti-tfp410.c
··· 435 435 } 436 436 437 437 static const struct i2c_device_id tfp410_i2c_ids[] = { 438 - { "tfp410", 0 }, 438 + { "tfp410" }, 439 439 { } 440 440 }; 441 441 MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids);
+50
drivers/gpu/drm/clients/Kconfig
··· 12 12 config DRM_CLIENT_SELECTION 13 13 tristate 14 14 depends on DRM 15 + select DRM_CLIENT_LIB if DRM_CLIENT_LOG 15 16 select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION 16 17 help 17 18 Drivers that support in-kernel DRM clients have to select this ··· 70 69 71 70 If in doubt, say "N" or spread the word to your closed source 72 71 library vendor. 72 + 73 + config DRM_CLIENT_LOG 74 + bool "Print the kernel boot message on the screen" 75 + depends on DRM_CLIENT_SELECTION 76 + select DRM_CLIENT 77 + select DRM_CLIENT_SETUP 78 + select DRM_DRAW 79 + select FONT_SUPPORT 80 + help 81 + This enable a drm logger, that will print the kernel messages to the 82 + screen until the userspace is ready to take over. 83 + 84 + If you only need logs, but no terminal, or if you prefer userspace 85 + terminal, say "Y". 86 + 87 + choice 88 + prompt "Default DRM Client" 89 + depends on DRM_CLIENT_SELECTION 90 + depends on DRM_FBDEV_EMULATION || DRM_CLIENT_LOG 91 + default DRM_CLIENT_DEFAULT_FBDEV 92 + help 93 + Selects the default drm client. 94 + 95 + The selection made here can be overridden by using the kernel 96 + command line 'drm_client_lib.active=fbdev' option. 97 + 98 + config DRM_CLIENT_DEFAULT_FBDEV 99 + bool "fbdev" 100 + depends on DRM_FBDEV_EMULATION 101 + help 102 + Use fbdev emulation as default drm client. This is needed to have 103 + fbcon on top of a drm driver. 104 + 105 + config DRM_CLIENT_DEFAULT_LOG 106 + bool "log" 107 + depends on DRM_CLIENT_LOG 108 + help 109 + Use drm log as default drm client. This will display boot logs on the 110 + screen, but doesn't implement a full terminal. For that you will need 111 + a userspace terminal using drm/kms. 112 + 113 + endchoice 114 + 115 + config DRM_CLIENT_DEFAULT 116 + string 117 + depends on DRM_CLIENT 118 + default "fbdev" if DRM_CLIENT_DEFAULT_FBDEV 119 + default "log" if DRM_CLIENT_DEFAULT_LOG 120 + default "" 73 121 74 122 endmenu
+3
drivers/gpu/drm/clients/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 + subdir-ccflags-y += -I$(src)/.. 4 + 3 5 drm_client_lib-y := drm_client_setup.o 6 + drm_client_lib-$(CONFIG_DRM_CLIENT_LOG) += drm_log.o 4 7 drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o 5 8 obj-$(CONFIG_DRM_CLIENT_LIB) += drm_client_lib.o
+6
drivers/gpu/drm/clients/drm_client_internal.h
··· 16 16 } 17 17 #endif 18 18 19 + #ifdef CONFIG_DRM_CLIENT_LOG 20 + void drm_log_register(struct drm_device *dev); 21 + #else 22 + static inline void drm_log_register(struct drm_device *dev) {} 23 + #endif 24 + 19 25 #endif
+25 -4
drivers/gpu/drm/clients/drm_client_setup.c
··· 7 7 8 8 #include "drm_client_internal.h" 9 9 10 + static char drm_client_default[16] = CONFIG_DRM_CLIENT_DEFAULT; 11 + module_param_string(active, drm_client_default, sizeof(drm_client_default), 0444); 12 + MODULE_PARM_DESC(active, 13 + "Choose which drm client to start, default is" 14 + CONFIG_DRM_CLIENT_DEFAULT "]"); 15 + 10 16 /** 11 17 * drm_client_setup() - Setup in-kernel DRM clients 12 18 * @dev: DRM device ··· 31 25 */ 32 26 void drm_client_setup(struct drm_device *dev, const struct drm_format_info *format) 33 27 { 34 - int ret; 35 28 36 - ret = drm_fbdev_client_setup(dev, format); 37 - if (ret) 38 - drm_warn(dev, "Failed to set up DRM client; error %d\n", ret); 29 + #ifdef CONFIG_DRM_FBDEV_EMULATION 30 + if (!strcmp(drm_client_default, "fbdev")) { 31 + int ret; 32 + 33 + ret = drm_fbdev_client_setup(dev, format); 34 + if (ret) 35 + drm_warn(dev, "Failed to set up DRM client; error %d\n", ret); 36 + return; 37 + } 38 + #endif 39 + 40 + #ifdef CONFIG_DRM_CLIENT_LOG 41 + if (!strcmp(drm_client_default, "log")) { 42 + drm_log_register(dev); 43 + return; 44 + } 45 + #endif 46 + if (strcmp(drm_client_default, "")) 47 + drm_warn(dev, "Unknown DRM client %s\n", drm_client_default); 39 48 } 40 49 EXPORT_SYMBOL(drm_client_setup); 41 50
+420
drivers/gpu/drm/clients/drm_log.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 or MIT 2 + /* 3 + * Copyright (c) 2024 Red Hat. 4 + * Author: Jocelyn Falempe <jfalempe@redhat.com> 5 + */ 6 + 7 + #include <linux/console.h> 8 + #include <linux/font.h> 9 + #include <linux/init.h> 10 + #include <linux/iosys-map.h> 11 + #include <linux/module.h> 12 + #include <linux/types.h> 13 + 14 + #include <drm/drm_client.h> 15 + #include <drm/drm_drv.h> 16 + #include <drm/drm_fourcc.h> 17 + #include <drm/drm_framebuffer.h> 18 + #include <drm/drm_print.h> 19 + 20 + #include "drm_client_internal.h" 21 + #include "drm_draw_internal.h" 22 + #include "drm_internal.h" 23 + 24 + MODULE_AUTHOR("Jocelyn Falempe"); 25 + MODULE_DESCRIPTION("DRM boot logger"); 26 + MODULE_LICENSE("GPL"); 27 + 28 + static unsigned int scale = 1; 29 + module_param(scale, uint, 0444); 30 + MODULE_PARM_DESC(scale, "Integer scaling factor for drm_log, default is 1"); 31 + 32 + /** 33 + * DOC: overview 34 + * 35 + * This is a simple graphic logger, to print the kernel message on screen, until 36 + * a userspace application is able to take over. 37 + * It is only for debugging purpose. 38 + */ 39 + 40 + struct drm_log_scanout { 41 + struct drm_client_buffer *buffer; 42 + const struct font_desc *font; 43 + u32 rows; 44 + u32 columns; 45 + u32 scaled_font_h; 46 + u32 scaled_font_w; 47 + u32 line; 48 + u32 format; 49 + u32 px_width; 50 + u32 front_color; 51 + u32 prefix_color; 52 + }; 53 + 54 + struct drm_log { 55 + struct mutex lock; 56 + struct drm_client_dev client; 57 + struct console con; 58 + bool probed; 59 + u32 n_scanout; 60 + struct drm_log_scanout *scanout; 61 + }; 62 + 63 + static struct drm_log *client_to_drm_log(struct drm_client_dev *client) 64 + { 65 + return container_of(client, struct drm_log, client); 66 + } 67 + 68 + static struct drm_log *console_to_drm_log(struct console *con) 69 + { 70 + return container_of(con, struct drm_log, con); 71 + } 72 + 73 + static void drm_log_blit(struct iosys_map *dst, unsigned int dst_pitch, 74 + const u8 *src, unsigned int src_pitch, 75 + u32 height, u32 width, u32 px_width, u32 color) 76 + { 77 + switch (px_width) { 78 + case 2: 79 + drm_draw_blit16(dst, dst_pitch, src, src_pitch, height, width, scale, color); 80 + break; 81 + case 3: 82 + drm_draw_blit24(dst, dst_pitch, src, src_pitch, height, width, scale, color); 83 + break; 84 + case 4: 85 + drm_draw_blit32(dst, dst_pitch, src, src_pitch, height, width, scale, color); 86 + break; 87 + default: 88 + WARN_ONCE(1, "Can't blit with pixel width %d\n", px_width); 89 + } 90 + } 91 + 92 + static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line) 93 + { 94 + struct drm_framebuffer *fb = scanout->buffer->fb; 95 + unsigned long height = scanout->scaled_font_h; 96 + struct iosys_map map; 97 + struct drm_rect r = DRM_RECT_INIT(0, line * height, fb->width, height); 98 + 99 + if (drm_client_buffer_vmap_local(scanout->buffer, &map)) 100 + return; 101 + iosys_map_memset(&map, r.y1 * fb->pitches[0], 0, height * fb->pitches[0]); 102 + drm_client_buffer_vunmap_local(scanout->buffer); 103 + drm_client_framebuffer_flush(scanout->buffer, &r); 104 + } 105 + 106 + static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s, 107 + unsigned int len, unsigned int prefix_len) 108 + { 109 + struct drm_framebuffer *fb = scanout->buffer->fb; 110 + struct iosys_map map; 111 + const struct font_desc *font = scanout->font; 112 + size_t font_pitch = DIV_ROUND_UP(font->width, 8); 113 + const u8 *src; 114 + u32 px_width = fb->format->cpp[0]; 115 + struct drm_rect r = DRM_RECT_INIT(0, scanout->line * scanout->scaled_font_h, 116 + fb->width, (scanout->line + 1) * scanout->scaled_font_h); 117 + u32 i; 118 + 119 + if (drm_client_buffer_vmap_local(scanout->buffer, &map)) 120 + return; 121 + 122 + iosys_map_incr(&map, r.y1 * fb->pitches[0]); 123 + for (i = 0; i < len && i < scanout->columns; i++) { 124 + u32 color = (i < prefix_len) ? scanout->prefix_color : scanout->front_color; 125 + src = drm_draw_get_char_bitmap(font, s[i], font_pitch); 126 + drm_log_blit(&map, fb->pitches[0], src, font_pitch, 127 + scanout->scaled_font_h, scanout->scaled_font_w, 128 + px_width, color); 129 + iosys_map_incr(&map, scanout->scaled_font_w * px_width); 130 + } 131 + 132 + scanout->line++; 133 + if (scanout->line >= scanout->rows) 134 + scanout->line = 0; 135 + drm_client_buffer_vunmap_local(scanout->buffer); 136 + drm_client_framebuffer_flush(scanout->buffer, &r); 137 + } 138 + 139 + static void drm_log_draw_new_line(struct drm_log_scanout *scanout, 140 + const char *s, unsigned int len, unsigned int prefix_len) 141 + { 142 + if (scanout->line == 0) { 143 + drm_log_clear_line(scanout, 0); 144 + drm_log_clear_line(scanout, 1); 145 + drm_log_clear_line(scanout, 2); 146 + } else if (scanout->line + 2 < scanout->rows) 147 + drm_log_clear_line(scanout, scanout->line + 2); 148 + 149 + drm_log_draw_line(scanout, s, len, prefix_len); 150 + } 151 + 152 + /* 153 + * Depends on print_time() in printk.c 154 + * Timestamp is written with "[%5lu.%06lu]" 155 + */ 156 + #define TS_PREFIX_LEN 13 157 + 158 + static void drm_log_draw_kmsg_record(struct drm_log_scanout *scanout, 159 + const char *s, unsigned int len) 160 + { 161 + u32 prefix_len = 0; 162 + 163 + if (len > TS_PREFIX_LEN && s[0] == '[' && s[6] == '.' && s[TS_PREFIX_LEN] == ']') 164 + prefix_len = TS_PREFIX_LEN + 1; 165 + 166 + /* do not print the ending \n character */ 167 + if (s[len - 1] == '\n') 168 + len--; 169 + 170 + while (len > scanout->columns) { 171 + drm_log_draw_new_line(scanout, s, scanout->columns, prefix_len); 172 + s += scanout->columns; 173 + len -= scanout->columns; 174 + prefix_len = 0; 175 + } 176 + if (len) 177 + drm_log_draw_new_line(scanout, s, len, prefix_len); 178 + } 179 + 180 + static u32 drm_log_find_usable_format(struct drm_plane *plane) 181 + { 182 + int i; 183 + 184 + for (i = 0; i < plane->format_count; i++) 185 + if (drm_draw_color_from_xrgb8888(0xffffff, plane->format_types[i]) != 0) 186 + return plane->format_types[i]; 187 + return DRM_FORMAT_INVALID; 188 + } 189 + 190 + static int drm_log_setup_modeset(struct drm_client_dev *client, 191 + struct drm_mode_set *mode_set, 192 + struct drm_log_scanout *scanout) 193 + { 194 + struct drm_crtc *crtc = mode_set->crtc; 195 + u32 width = mode_set->mode->hdisplay; 196 + u32 height = mode_set->mode->vdisplay; 197 + u32 format; 198 + 199 + scanout->font = get_default_font(width, height, NULL, NULL); 200 + if (!scanout->font) 201 + return -ENOENT; 202 + 203 + format = drm_log_find_usable_format(crtc->primary); 204 + if (format == DRM_FORMAT_INVALID) 205 + return -EINVAL; 206 + 207 + scanout->buffer = drm_client_framebuffer_create(client, width, height, format); 208 + if (IS_ERR(scanout->buffer)) { 209 + drm_warn(client->dev, "drm_log can't create framebuffer %d %d %p4cc\n", 210 + width, height, &format); 211 + return -ENOMEM; 212 + } 213 + mode_set->fb = scanout->buffer->fb; 214 + scanout->scaled_font_h = scanout->font->height * scale; 215 + scanout->scaled_font_w = scanout->font->width * scale; 216 + scanout->rows = height / scanout->scaled_font_h; 217 + scanout->columns = width / scanout->scaled_font_w; 218 + scanout->front_color = drm_draw_color_from_xrgb8888(0xffffff, format); 219 + scanout->prefix_color = drm_draw_color_from_xrgb8888(0x4e9a06, format); 220 + return 0; 221 + } 222 + 223 + static int drm_log_count_modeset(struct drm_client_dev *client) 224 + { 225 + struct drm_mode_set *mode_set; 226 + int count = 0; 227 + 228 + mutex_lock(&client->modeset_mutex); 229 + drm_client_for_each_modeset(mode_set, client) 230 + count++; 231 + mutex_unlock(&client->modeset_mutex); 232 + return count; 233 + } 234 + 235 + static void drm_log_init_client(struct drm_log *dlog) 236 + { 237 + struct drm_client_dev *client = &dlog->client; 238 + struct drm_mode_set *mode_set; 239 + int i, max_modeset; 240 + int n_modeset = 0; 241 + 242 + dlog->probed = true; 243 + 244 + if (drm_client_modeset_probe(client, 0, 0)) 245 + return; 246 + 247 + max_modeset = drm_log_count_modeset(client); 248 + if (!max_modeset) 249 + return; 250 + 251 + dlog->scanout = kcalloc(max_modeset, sizeof(*dlog->scanout), GFP_KERNEL); 252 + if (!dlog->scanout) 253 + return; 254 + 255 + mutex_lock(&client->modeset_mutex); 256 + drm_client_for_each_modeset(mode_set, client) { 257 + if (!mode_set->mode) 258 + continue; 259 + if (drm_log_setup_modeset(client, mode_set, &dlog->scanout[n_modeset])) 260 + continue; 261 + n_modeset++; 262 + } 263 + mutex_unlock(&client->modeset_mutex); 264 + if (n_modeset == 0) 265 + goto err_nomodeset; 266 + 267 + if (drm_client_modeset_commit(client)) 268 + goto err_failed_commit; 269 + 270 + dlog->n_scanout = n_modeset; 271 + return; 272 + 273 + err_failed_commit: 274 + for (i = 0; i < n_modeset; i++) 275 + drm_client_framebuffer_delete(dlog->scanout[i].buffer); 276 + 277 + err_nomodeset: 278 + kfree(dlog->scanout); 279 + dlog->scanout = NULL; 280 + } 281 + 282 + static void drm_log_free_scanout(struct drm_client_dev *client) 283 + { 284 + struct drm_log *dlog = client_to_drm_log(client); 285 + int i; 286 + 287 + if (dlog->n_scanout) { 288 + for (i = 0; i < dlog->n_scanout; i++) 289 + drm_client_framebuffer_delete(dlog->scanout[i].buffer); 290 + dlog->n_scanout = 0; 291 + kfree(dlog->scanout); 292 + dlog->scanout = NULL; 293 + } 294 + } 295 + 296 + static void drm_log_client_unregister(struct drm_client_dev *client) 297 + { 298 + struct drm_log *dlog = client_to_drm_log(client); 299 + struct drm_device *dev = client->dev; 300 + 301 + unregister_console(&dlog->con); 302 + 303 + mutex_lock(&dlog->lock); 304 + drm_log_free_scanout(client); 305 + drm_client_release(client); 306 + mutex_unlock(&dlog->lock); 307 + kfree(dlog); 308 + drm_dbg(dev, "Unregistered with drm log\n"); 309 + } 310 + 311 + static int drm_log_client_hotplug(struct drm_client_dev *client) 312 + { 313 + struct drm_log *dlog = client_to_drm_log(client); 314 + 315 + mutex_lock(&dlog->lock); 316 + drm_log_free_scanout(client); 317 + dlog->probed = false; 318 + mutex_unlock(&dlog->lock); 319 + return 0; 320 + } 321 + 322 + static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_lock) 323 + { 324 + struct drm_log *dlog = client_to_drm_log(client); 325 + 326 + console_stop(&dlog->con); 327 + 328 + return 0; 329 + } 330 + 331 + static int drm_log_client_resume(struct drm_client_dev *client, bool _console_lock) 332 + { 333 + struct drm_log *dlog = client_to_drm_log(client); 334 + 335 + console_start(&dlog->con); 336 + 337 + return 0; 338 + } 339 + 340 + static const struct drm_client_funcs drm_log_client_funcs = { 341 + .owner = THIS_MODULE, 342 + .unregister = drm_log_client_unregister, 343 + .hotplug = drm_log_client_hotplug, 344 + .suspend = drm_log_client_suspend, 345 + .resume = drm_log_client_resume, 346 + }; 347 + 348 + static void drm_log_write_thread(struct console *con, struct nbcon_write_context *wctxt) 349 + { 350 + struct drm_log *dlog = console_to_drm_log(con); 351 + int i; 352 + 353 + if (!dlog->probed) 354 + drm_log_init_client(dlog); 355 + 356 + /* Check that we are still the master before drawing */ 357 + if (drm_master_internal_acquire(dlog->client.dev)) { 358 + drm_master_internal_release(dlog->client.dev); 359 + 360 + for (i = 0; i < dlog->n_scanout; i++) 361 + drm_log_draw_kmsg_record(&dlog->scanout[i], wctxt->outbuf, wctxt->len); 362 + } 363 + } 364 + 365 + static void drm_log_lock(struct console *con, unsigned long *flags) 366 + { 367 + struct drm_log *dlog = console_to_drm_log(con); 368 + 369 + mutex_lock(&dlog->lock); 370 + migrate_disable(); 371 + } 372 + 373 + static void drm_log_unlock(struct console *con, unsigned long flags) 374 + { 375 + struct drm_log *dlog = console_to_drm_log(con); 376 + 377 + migrate_enable(); 378 + mutex_unlock(&dlog->lock); 379 + } 380 + 381 + static void drm_log_register_console(struct console *con) 382 + { 383 + strscpy(con->name, "drm_log"); 384 + con->write_thread = drm_log_write_thread; 385 + con->device_lock = drm_log_lock; 386 + con->device_unlock = drm_log_unlock; 387 + con->flags = CON_PRINTBUFFER | CON_NBCON; 388 + con->index = -1; 389 + 390 + register_console(con); 391 + } 392 + 393 + /** 394 + * drm_log_register() - Register a drm device to drm_log 395 + * @dev: the drm device to register. 396 + */ 397 + void drm_log_register(struct drm_device *dev) 398 + { 399 + struct drm_log *new; 400 + 401 + new = kzalloc(sizeof(*new), GFP_KERNEL); 402 + if (!new) 403 + goto err_warn; 404 + 405 + mutex_init(&new->lock); 406 + if (drm_client_init(dev, &new->client, "drm_log", &drm_log_client_funcs)) 407 + goto err_free; 408 + 409 + drm_client_register(&new->client); 410 + 411 + drm_log_register_console(&new->con); 412 + 413 + drm_dbg(dev, "Registered with drm log as %s\n", new->con.name); 414 + return; 415 + 416 + err_free: 417 + kfree(new); 418 + err_warn: 419 + drm_warn(dev, "Failed to register with drm log\n"); 420 + }
+15 -1
drivers/gpu/drm/display/drm_bridge_connector.c
··· 18 18 #include <drm/drm_managed.h> 19 19 #include <drm/drm_modeset_helper_vtables.h> 20 20 #include <drm/drm_probe_helper.h> 21 + #include <drm/display/drm_hdmi_helper.h> 21 22 #include <drm/display/drm_hdmi_state_helper.h> 22 23 23 24 /** ··· 300 299 return 0; 301 300 } 302 301 302 + static enum drm_mode_status 303 + drm_bridge_connector_mode_valid(struct drm_connector *connector, 304 + struct drm_display_mode *mode) 305 + { 306 + struct drm_bridge_connector *bridge_connector = 307 + to_drm_bridge_connector(connector); 308 + 309 + if (bridge_connector->bridge_hdmi) 310 + return drm_hdmi_connector_mode_valid(connector, mode); 311 + 312 + return MODE_OK; 313 + } 314 + 303 315 static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = { 304 316 .get_modes = drm_bridge_connector_get_modes, 305 - /* No need for .mode_valid(), the bridges are checked by the core. */ 317 + .mode_valid = drm_bridge_connector_mode_valid, 306 318 .enable_hpd = drm_bridge_connector_enable_hpd, 307 319 .disable_hpd = drm_bridge_connector_disable_hpd, 308 320 };
+1 -1
drivers/gpu/drm/display/drm_dp_mst_topology.c
··· 2281 2281 port->cached_edid = drm_edid_read_ddc(port->connector, 2282 2282 &port->aux.ddc); 2283 2283 2284 - drm_connector_register(port->connector); 2284 + drm_connector_dynamic_register(port->connector); 2285 2285 return; 2286 2286 2287 2287 error:
+29
drivers/gpu/drm/display/drm_hdmi_state_helper.c
··· 347 347 is_limited_range ? HDMI_QUANTIZATION_RANGE_LIMITED : HDMI_QUANTIZATION_RANGE_FULL; 348 348 int ret; 349 349 350 + infoframe->set = false; 351 + 350 352 ret = drm_hdmi_avi_infoframe_from_display_mode(frame, connector, mode); 351 353 if (ret) 352 354 return ret; ··· 378 376 &infoframe->data.spd; 379 377 int ret; 380 378 379 + infoframe->set = false; 380 + 381 381 ret = hdmi_spd_infoframe_init(frame, 382 382 connector->hdmi.vendor, 383 383 connector->hdmi.product); ··· 401 397 struct hdmi_drm_infoframe *frame = 402 398 &infoframe->data.drm; 403 399 int ret; 400 + 401 + infoframe->set = false; 404 402 405 403 if (connector->max_bpc < 10) 406 404 return 0; ··· 430 424 struct hdmi_vendor_infoframe *frame = 431 425 &infoframe->data.vendor.hdmi; 432 426 int ret; 427 + 428 + infoframe->set = false; 433 429 434 430 if (!info->has_hdmi_infoframe) 435 431 return 0; ··· 528 520 return 0; 529 521 } 530 522 EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check); 523 + 524 + /** 525 + * drm_hdmi_connector_mode_valid() - Check if mode is valid for HDMI connector 526 + * @connector: DRM connector to validate the mode 527 + * @mode: Display mode to validate 528 + * 529 + * Generic .mode_valid implementation for HDMI connectors. 530 + */ 531 + enum drm_mode_status 532 + drm_hdmi_connector_mode_valid(struct drm_connector *connector, 533 + struct drm_display_mode *mode) 534 + { 535 + unsigned long long clock; 536 + 537 + clock = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); 538 + if (!clock) 539 + return MODE_ERROR; 540 + 541 + return hdmi_clock_valid(connector, mode, clock); 542 + } 543 + EXPORT_SYMBOL(drm_hdmi_connector_mode_valid); 531 544 532 545 static int clear_device_infoframe(struct drm_connector *connector, 533 546 enum hdmi_infoframe_type type)
+142 -27
drivers/gpu/drm/drm_connector.c
··· 218 218 } 219 219 } 220 220 221 - static int __drm_connector_init(struct drm_device *dev, 222 - struct drm_connector *connector, 223 - const struct drm_connector_funcs *funcs, 224 - int connector_type, 225 - struct i2c_adapter *ddc) 221 + static int drm_connector_init_only(struct drm_device *dev, 222 + struct drm_connector *connector, 223 + const struct drm_connector_funcs *funcs, 224 + int connector_type, 225 + struct i2c_adapter *ddc) 226 226 { 227 227 struct drm_mode_config *config = &dev->mode_config; 228 228 int ret; ··· 273 273 /* provide ddc symlink in sysfs */ 274 274 connector->ddc = ddc; 275 275 276 + INIT_LIST_HEAD(&connector->head); 276 277 INIT_LIST_HEAD(&connector->global_connector_list_entry); 277 278 INIT_LIST_HEAD(&connector->probed_modes); 278 279 INIT_LIST_HEAD(&connector->modes); 279 280 mutex_init(&connector->mutex); 281 + mutex_init(&connector->eld_mutex); 280 282 mutex_init(&connector->edid_override_mutex); 281 283 mutex_init(&connector->hdmi.infoframes.lock); 282 284 connector->edid_blob_ptr = NULL; ··· 289 287 DRM_MODE_PANEL_ORIENTATION_UNKNOWN; 290 288 291 289 drm_connector_get_cmdline_mode(connector); 292 - 293 - /* We should add connectors at the end to avoid upsetting the connector 294 - * index too much. 295 - */ 296 - spin_lock_irq(&config->connector_list_lock); 297 - list_add_tail(&connector->head, &config->connector_list); 298 - config->num_connector++; 299 - spin_unlock_irq(&config->connector_list_lock); 300 290 301 291 if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && 302 292 connector_type != DRM_MODE_CONNECTOR_WRITEBACK) ··· 326 332 return ret; 327 333 } 328 334 335 + static void drm_connector_add(struct drm_connector *connector) 336 + { 337 + struct drm_device *dev = connector->dev; 338 + struct drm_mode_config *config = &dev->mode_config; 339 + 340 + if (drm_WARN_ON(dev, !list_empty(&connector->head))) 341 + return; 342 + 343 + spin_lock_irq(&config->connector_list_lock); 344 + list_add_tail(&connector->head, &config->connector_list); 345 + config->num_connector++; 346 + spin_unlock_irq(&config->connector_list_lock); 347 + } 348 + 349 + static void drm_connector_remove(struct drm_connector *connector) 350 + { 351 + struct drm_device *dev = connector->dev; 352 + 353 + /* 354 + * For dynamic connectors drm_connector_cleanup() can call this function 355 + * before the connector is registered and added to the list. 356 + */ 357 + if (list_empty(&connector->head)) 358 + return; 359 + 360 + spin_lock_irq(&dev->mode_config.connector_list_lock); 361 + list_del_init(&connector->head); 362 + dev->mode_config.num_connector--; 363 + spin_unlock_irq(&dev->mode_config.connector_list_lock); 364 + } 365 + 366 + static int drm_connector_init_and_add(struct drm_device *dev, 367 + struct drm_connector *connector, 368 + const struct drm_connector_funcs *funcs, 369 + int connector_type, 370 + struct i2c_adapter *ddc) 371 + { 372 + int ret; 373 + 374 + ret = drm_connector_init_only(dev, connector, funcs, connector_type, ddc); 375 + if (ret) 376 + return ret; 377 + 378 + drm_connector_add(connector); 379 + 380 + return 0; 381 + } 382 + 329 383 /** 330 384 * drm_connector_init - Init a preallocated connector 331 385 * @dev: DRM device ··· 403 361 if (drm_WARN_ON(dev, !(funcs && funcs->destroy))) 404 362 return -EINVAL; 405 363 406 - return __drm_connector_init(dev, connector, funcs, connector_type, NULL); 364 + return drm_connector_init_and_add(dev, connector, funcs, connector_type, NULL); 407 365 } 408 366 EXPORT_SYMBOL(drm_connector_init); 367 + 368 + /** 369 + * drm_connector_dynamic_init - Init a preallocated dynamic connector 370 + * @dev: DRM device 371 + * @connector: the connector to init 372 + * @funcs: callbacks for this connector 373 + * @connector_type: user visible type of the connector 374 + * @ddc: pointer to the associated ddc adapter 375 + * 376 + * Initialises a preallocated dynamic connector. Connectors should be 377 + * subclassed as part of driver connector objects. The connector 378 + * structure should not be allocated with devm_kzalloc(). 379 + * 380 + * Drivers should call this for dynamic connectors which can be hotplugged 381 + * after drm_dev_register() has been called already, e.g. DP MST connectors. 382 + * For all other - static - connectors, drivers should call one of the 383 + * drm_connector_init*()/drmm_connector_init*() functions. 384 + * 385 + * After calling this function the drivers must call 386 + * drm_connector_dynamic_register(). 387 + * 388 + * To remove the connector the driver must call drm_connector_unregister() 389 + * followed by drm_connector_put(). Putting the last reference will call the 390 + * driver's &drm_connector_funcs.destroy hook, which in turn must call 391 + * drm_connector_cleanup() and free the connector structure. 392 + * 393 + * Returns: 394 + * Zero on success, error code on failure. 395 + */ 396 + int drm_connector_dynamic_init(struct drm_device *dev, 397 + struct drm_connector *connector, 398 + const struct drm_connector_funcs *funcs, 399 + int connector_type, 400 + struct i2c_adapter *ddc) 401 + { 402 + if (drm_WARN_ON(dev, !(funcs && funcs->destroy))) 403 + return -EINVAL; 404 + 405 + return drm_connector_init_only(dev, connector, funcs, connector_type, ddc); 406 + } 407 + EXPORT_SYMBOL(drm_connector_dynamic_init); 409 408 410 409 /** 411 410 * drm_connector_init_with_ddc - Init a preallocated connector ··· 481 398 if (drm_WARN_ON(dev, !(funcs && funcs->destroy))) 482 399 return -EINVAL; 483 400 484 - return __drm_connector_init(dev, connector, funcs, connector_type, ddc); 401 + return drm_connector_init_and_add(dev, connector, funcs, connector_type, ddc); 485 402 } 486 403 EXPORT_SYMBOL(drm_connector_init_with_ddc); 487 404 ··· 525 442 if (drm_WARN_ON(dev, funcs && funcs->destroy)) 526 443 return -EINVAL; 527 444 528 - ret = __drm_connector_init(dev, connector, funcs, connector_type, ddc); 445 + ret = drm_connector_init_and_add(dev, connector, funcs, connector_type, ddc); 529 446 if (ret) 530 447 return ret; 531 448 ··· 742 659 connector->name = NULL; 743 660 fwnode_handle_put(connector->fwnode); 744 661 connector->fwnode = NULL; 745 - spin_lock_irq(&dev->mode_config.connector_list_lock); 746 - list_del(&connector->head); 747 - dev->mode_config.num_connector--; 748 - spin_unlock_irq(&dev->mode_config.connector_list_lock); 662 + 663 + drm_connector_remove(connector); 749 664 750 665 WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); 751 666 if (connector->state && connector->funcs->atomic_destroy_state) ··· 764 683 * drm_connector_register - register a connector 765 684 * @connector: the connector to register 766 685 * 767 - * Register userspace interfaces for a connector. Only call this for connectors 768 - * which can be hotplugged after drm_dev_register() has been called already, 769 - * e.g. DP MST connectors. All other connectors will be registered automatically 770 - * when calling drm_dev_register(). 686 + * Register userspace interfaces for a connector. Drivers shouldn't call this 687 + * function. Static connectors will be registered automatically by DRM core 688 + * from drm_dev_register(), dynamic connectors (MST) should be registered by 689 + * drivers calling drm_connector_dynamic_register(). 771 690 * 772 691 * When the connector is no longer available, callers must call 773 692 * drm_connector_unregister(). 693 + * 694 + * Note: Existing uses of this function in drivers should be a nop already and 695 + * are scheduled to be removed. 774 696 * 775 697 * Returns: 776 698 * Zero on success, error code on failure. ··· 834 750 EXPORT_SYMBOL(drm_connector_register); 835 751 836 752 /** 753 + * drm_connector_dynamic_register - register a dynamic connector 754 + * @connector: the connector to register 755 + * 756 + * Register userspace interfaces for a connector. Only call this for connectors 757 + * initialized by calling drm_connector_dynamic_init(). All other connectors 758 + * will be registered automatically when calling drm_dev_register(). 759 + * 760 + * When the connector is no longer available the driver must call 761 + * drm_connector_unregister(). 762 + * 763 + * Returns: 764 + * Zero on success, error code on failure. 765 + */ 766 + int drm_connector_dynamic_register(struct drm_connector *connector) 767 + { 768 + /* Was the connector inited already? */ 769 + if (WARN_ON(!(connector->funcs && connector->funcs->destroy))) 770 + return -EINVAL; 771 + 772 + drm_connector_add(connector); 773 + 774 + return drm_connector_register(connector); 775 + } 776 + EXPORT_SYMBOL(drm_connector_dynamic_register); 777 + 778 + /** 837 779 * drm_connector_unregister - unregister a connector 838 780 * @connector: the connector to unregister 839 781 * 840 - * Unregister userspace interfaces for a connector. Only call this for 841 - * connectors which have been registered explicitly by calling 842 - * drm_connector_register(). 782 + * Unregister userspace interfaces for a connector. Drivers should call this 783 + * for dynamic connectors (MST) only, which were registered explicitly by 784 + * calling drm_connector_dynamic_register(). All other - static - connectors 785 + * will be unregistered automatically by DRM core and drivers shouldn't call 786 + * this function for those. 787 + * 788 + * Note: Existing uses of this function in drivers for static connectors 789 + * should be a nop already and are scheduled to be removed. 843 790 */ 844 791 void drm_connector_unregister(struct drm_connector *connector) 845 792 {
+233
drivers/gpu/drm/drm_draw.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 or MIT 2 + /* 3 + * Copyright (c) 2023 Red Hat. 4 + * Author: Jocelyn Falempe <jfalempe@redhat.com> 5 + */ 6 + 7 + #include <linux/bits.h> 8 + #include <linux/iosys-map.h> 9 + #include <linux/types.h> 10 + 11 + #include <drm/drm_fourcc.h> 12 + 13 + #include "drm_draw_internal.h" 14 + 15 + /* 16 + * Conversions from xrgb8888 17 + */ 18 + 19 + static u16 convert_xrgb8888_to_rgb565(u32 pix) 20 + { 21 + return ((pix & 0x00F80000) >> 8) | 22 + ((pix & 0x0000FC00) >> 5) | 23 + ((pix & 0x000000F8) >> 3); 24 + } 25 + 26 + static u16 convert_xrgb8888_to_rgba5551(u32 pix) 27 + { 28 + return ((pix & 0x00f80000) >> 8) | 29 + ((pix & 0x0000f800) >> 5) | 30 + ((pix & 0x000000f8) >> 2) | 31 + BIT(0); /* set alpha bit */ 32 + } 33 + 34 + static u16 convert_xrgb8888_to_xrgb1555(u32 pix) 35 + { 36 + return ((pix & 0x00f80000) >> 9) | 37 + ((pix & 0x0000f800) >> 6) | 38 + ((pix & 0x000000f8) >> 3); 39 + } 40 + 41 + static u16 convert_xrgb8888_to_argb1555(u32 pix) 42 + { 43 + return BIT(15) | /* set alpha bit */ 44 + ((pix & 0x00f80000) >> 9) | 45 + ((pix & 0x0000f800) >> 6) | 46 + ((pix & 0x000000f8) >> 3); 47 + } 48 + 49 + static u32 convert_xrgb8888_to_argb8888(u32 pix) 50 + { 51 + return pix | GENMASK(31, 24); /* fill alpha bits */ 52 + } 53 + 54 + static u32 convert_xrgb8888_to_xbgr8888(u32 pix) 55 + { 56 + return ((pix & 0x00ff0000) >> 16) << 0 | 57 + ((pix & 0x0000ff00) >> 8) << 8 | 58 + ((pix & 0x000000ff) >> 0) << 16 | 59 + ((pix & 0xff000000) >> 24) << 24; 60 + } 61 + 62 + static u32 convert_xrgb8888_to_abgr8888(u32 pix) 63 + { 64 + return ((pix & 0x00ff0000) >> 16) << 0 | 65 + ((pix & 0x0000ff00) >> 8) << 8 | 66 + ((pix & 0x000000ff) >> 0) << 16 | 67 + GENMASK(31, 24); /* fill alpha bits */ 68 + } 69 + 70 + static u32 convert_xrgb8888_to_xrgb2101010(u32 pix) 71 + { 72 + pix = ((pix & 0x000000FF) << 2) | 73 + ((pix & 0x0000FF00) << 4) | 74 + ((pix & 0x00FF0000) << 6); 75 + return pix | ((pix >> 8) & 0x00300C03); 76 + } 77 + 78 + static u32 convert_xrgb8888_to_argb2101010(u32 pix) 79 + { 80 + pix = ((pix & 0x000000FF) << 2) | 81 + ((pix & 0x0000FF00) << 4) | 82 + ((pix & 0x00FF0000) << 6); 83 + return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); 84 + } 85 + 86 + static u32 convert_xrgb8888_to_abgr2101010(u32 pix) 87 + { 88 + pix = ((pix & 0x00FF0000) >> 14) | 89 + ((pix & 0x0000FF00) << 4) | 90 + ((pix & 0x000000FF) << 22); 91 + return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); 92 + } 93 + 94 + /** 95 + * drm_draw_color_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format 96 + * @color: input color, in xrgb8888 format 97 + * @format: output format 98 + * 99 + * Returns: 100 + * Color in the format specified, casted to u32. 101 + * Or 0 if the format is not supported. 102 + */ 103 + u32 drm_draw_color_from_xrgb8888(u32 color, u32 format) 104 + { 105 + switch (format) { 106 + case DRM_FORMAT_RGB565: 107 + return convert_xrgb8888_to_rgb565(color); 108 + case DRM_FORMAT_RGBA5551: 109 + return convert_xrgb8888_to_rgba5551(color); 110 + case DRM_FORMAT_XRGB1555: 111 + return convert_xrgb8888_to_xrgb1555(color); 112 + case DRM_FORMAT_ARGB1555: 113 + return convert_xrgb8888_to_argb1555(color); 114 + case DRM_FORMAT_RGB888: 115 + case DRM_FORMAT_XRGB8888: 116 + return color; 117 + case DRM_FORMAT_ARGB8888: 118 + return convert_xrgb8888_to_argb8888(color); 119 + case DRM_FORMAT_XBGR8888: 120 + return convert_xrgb8888_to_xbgr8888(color); 121 + case DRM_FORMAT_ABGR8888: 122 + return convert_xrgb8888_to_abgr8888(color); 123 + case DRM_FORMAT_XRGB2101010: 124 + return convert_xrgb8888_to_xrgb2101010(color); 125 + case DRM_FORMAT_ARGB2101010: 126 + return convert_xrgb8888_to_argb2101010(color); 127 + case DRM_FORMAT_ABGR2101010: 128 + return convert_xrgb8888_to_abgr2101010(color); 129 + default: 130 + WARN_ONCE(1, "Can't convert to %p4cc\n", &format); 131 + return 0; 132 + } 133 + } 134 + EXPORT_SYMBOL(drm_draw_color_from_xrgb8888); 135 + 136 + /* 137 + * Blit functions 138 + */ 139 + void drm_draw_blit16(struct iosys_map *dmap, unsigned int dpitch, 140 + const u8 *sbuf8, unsigned int spitch, 141 + unsigned int height, unsigned int width, 142 + unsigned int scale, u16 fg16) 143 + { 144 + unsigned int y, x; 145 + 146 + for (y = 0; y < height; y++) 147 + for (x = 0; x < width; x++) 148 + if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) 149 + iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, fg16); 150 + } 151 + EXPORT_SYMBOL(drm_draw_blit16); 152 + 153 + void drm_draw_blit24(struct iosys_map *dmap, unsigned int dpitch, 154 + const u8 *sbuf8, unsigned int spitch, 155 + unsigned int height, unsigned int width, 156 + unsigned int scale, u32 fg32) 157 + { 158 + unsigned int y, x; 159 + 160 + for (y = 0; y < height; y++) { 161 + for (x = 0; x < width; x++) { 162 + u32 off = y * dpitch + x * 3; 163 + 164 + if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) { 165 + /* write blue-green-red to output in little endianness */ 166 + iosys_map_wr(dmap, off, u8, (fg32 & 0x000000FF) >> 0); 167 + iosys_map_wr(dmap, off + 1, u8, (fg32 & 0x0000FF00) >> 8); 168 + iosys_map_wr(dmap, off + 2, u8, (fg32 & 0x00FF0000) >> 16); 169 + } 170 + } 171 + } 172 + } 173 + EXPORT_SYMBOL(drm_draw_blit24); 174 + 175 + void drm_draw_blit32(struct iosys_map *dmap, unsigned int dpitch, 176 + const u8 *sbuf8, unsigned int spitch, 177 + unsigned int height, unsigned int width, 178 + unsigned int scale, u32 fg32) 179 + { 180 + unsigned int y, x; 181 + 182 + for (y = 0; y < height; y++) 183 + for (x = 0; x < width; x++) 184 + if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) 185 + iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, fg32); 186 + } 187 + EXPORT_SYMBOL(drm_draw_blit32); 188 + 189 + /* 190 + * Fill functions 191 + */ 192 + void drm_draw_fill16(struct iosys_map *dmap, unsigned int dpitch, 193 + unsigned int height, unsigned int width, 194 + u16 color) 195 + { 196 + unsigned int y, x; 197 + 198 + for (y = 0; y < height; y++) 199 + for (x = 0; x < width; x++) 200 + iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, color); 201 + } 202 + EXPORT_SYMBOL(drm_draw_fill16); 203 + 204 + void drm_draw_fill24(struct iosys_map *dmap, unsigned int dpitch, 205 + unsigned int height, unsigned int width, 206 + u16 color) 207 + { 208 + unsigned int y, x; 209 + 210 + for (y = 0; y < height; y++) { 211 + for (x = 0; x < width; x++) { 212 + unsigned int off = y * dpitch + x * 3; 213 + 214 + /* write blue-green-red to output in little endianness */ 215 + iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0); 216 + iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8); 217 + iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16); 218 + } 219 + } 220 + } 221 + EXPORT_SYMBOL(drm_draw_fill24); 222 + 223 + void drm_draw_fill32(struct iosys_map *dmap, unsigned int dpitch, 224 + unsigned int height, unsigned int width, 225 + u32 color) 226 + { 227 + unsigned int y, x; 228 + 229 + for (y = 0; y < height; y++) 230 + for (x = 0; x < width; x++) 231 + iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, color); 232 + } 233 + EXPORT_SYMBOL(drm_draw_fill32);
+56
drivers/gpu/drm/drm_draw_internal.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 or MIT */ 2 + /* 3 + * Copyright (c) 2023 Red Hat. 4 + * Author: Jocelyn Falempe <jfalempe@redhat.com> 5 + */ 6 + 7 + #ifndef __DRM_DRAW_INTERNAL_H__ 8 + #define __DRM_DRAW_INTERNAL_H__ 9 + 10 + #include <linux/font.h> 11 + #include <linux/types.h> 12 + 13 + struct iosys_map; 14 + 15 + /* check if the pixel at coord x,y is 1 (foreground) or 0 (background) */ 16 + static inline bool drm_draw_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, int x, int y) 17 + { 18 + return (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) != 0; 19 + } 20 + 21 + static inline const u8 *drm_draw_get_char_bitmap(const struct font_desc *font, 22 + char c, size_t font_pitch) 23 + { 24 + return font->data + (c * font->height) * font_pitch; 25 + } 26 + 27 + u32 drm_draw_color_from_xrgb8888(u32 color, u32 format); 28 + 29 + void drm_draw_blit16(struct iosys_map *dmap, unsigned int dpitch, 30 + const u8 *sbuf8, unsigned int spitch, 31 + unsigned int height, unsigned int width, 32 + unsigned int scale, u16 fg16); 33 + 34 + void drm_draw_blit24(struct iosys_map *dmap, unsigned int dpitch, 35 + const u8 *sbuf8, unsigned int spitch, 36 + unsigned int height, unsigned int width, 37 + unsigned int scale, u32 fg32); 38 + 39 + void drm_draw_blit32(struct iosys_map *dmap, unsigned int dpitch, 40 + const u8 *sbuf8, unsigned int spitch, 41 + unsigned int height, unsigned int width, 42 + unsigned int scale, u32 fg32); 43 + 44 + void drm_draw_fill16(struct iosys_map *dmap, unsigned int dpitch, 45 + unsigned int height, unsigned int width, 46 + u16 color); 47 + 48 + void drm_draw_fill24(struct iosys_map *dmap, unsigned int dpitch, 49 + unsigned int height, unsigned int width, 50 + u16 color); 51 + 52 + void drm_draw_fill32(struct iosys_map *dmap, unsigned int dpitch, 53 + unsigned int height, unsigned int width, 54 + u32 color); 55 + 56 + #endif /* __DRM_DRAW_INTERNAL_H__ */
+6
drivers/gpu/drm/drm_edid.c
··· 5605 5605 5606 5606 static void clear_eld(struct drm_connector *connector) 5607 5607 { 5608 + mutex_lock(&connector->eld_mutex); 5608 5609 memset(connector->eld, 0, sizeof(connector->eld)); 5610 + mutex_unlock(&connector->eld_mutex); 5609 5611 5610 5612 connector->latency_present[0] = false; 5611 5613 connector->latency_present[1] = false; ··· 5658 5656 5659 5657 if (!drm_edid) 5660 5658 return; 5659 + 5660 + mutex_lock(&connector->eld_mutex); 5661 5661 5662 5662 mnl = get_monitor_name(drm_edid, &eld[DRM_ELD_MONITOR_NAME_STRING]); 5663 5663 drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD monitor %s\n", ··· 5721 5717 drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] ELD size %d, SAD count %d\n", 5722 5718 connector->base.id, connector->name, 5723 5719 drm_eld_size(eld), total_sad_count); 5720 + 5721 + mutex_unlock(&connector->eld_mutex); 5724 5722 } 5725 5723 5726 5724 static int _drm_edid_to_sad(const struct drm_edid *drm_edid,
+9
drivers/gpu/drm/drm_mode_config.c
··· 150 150 drm_connector_list_iter_begin(dev, &conn_iter); 151 151 count = 0; 152 152 connector_id = u64_to_user_ptr(card_res->connector_id_ptr); 153 + /* 154 + * FIXME: the connectors on the list may not be fully initialized yet, 155 + * if the ioctl is called before the connectors are registered. (See 156 + * drm_dev_register()->drm_modeset_register_all() for static and 157 + * drm_connector_dynamic_register() for dynamic connectors.) 158 + * The driver should only get registered after static connectors are 159 + * fully initialized and dynamic connectors should be added to the 160 + * connector list only after fully initializing them. 161 + */ 153 162 drm_for_each_connector_iter(connector, &conn_iter) { 154 163 /* only expose writeback connectors if userspace understands them */ 155 164 if (!file_priv->writeback_connectors &&
+1 -2
drivers/gpu/drm/drm_modes.c
··· 1282 1282 * @mode: mode 1283 1283 * 1284 1284 * Returns: 1285 - * @modes's vrefresh rate in Hz, rounded to the nearest integer. Calculates the 1286 - * value first if it is not yet set. 1285 + * @modes's vrefresh rate in Hz, rounded to the nearest integer. 1287 1286 */ 1288 1287 int drm_mode_vrefresh(const struct drm_display_mode *mode) 1289 1288 {
+1
drivers/gpu/drm/drm_panel.c
··· 24 24 #include <linux/backlight.h> 25 25 #include <linux/err.h> 26 26 #include <linux/module.h> 27 + #include <linux/of.h> 27 28 28 29 #include <drm/drm_crtc.h> 29 30 #include <drm/drm_panel.h>
+29 -240
drivers/gpu/drm/drm_panic.c
··· 31 31 #include <drm/drm_rect.h> 32 32 33 33 #include "drm_crtc_internal.h" 34 + #include "drm_draw_internal.h" 34 35 35 36 MODULE_AUTHOR("Jocelyn Falempe"); 36 37 MODULE_DESCRIPTION("DRM panic handler"); ··· 140 139 #endif 141 140 142 141 /* 143 - * Color conversion 142 + * Blit & Fill functions 144 143 */ 145 - 146 - static u16 convert_xrgb8888_to_rgb565(u32 pix) 147 - { 148 - return ((pix & 0x00F80000) >> 8) | 149 - ((pix & 0x0000FC00) >> 5) | 150 - ((pix & 0x000000F8) >> 3); 151 - } 152 - 153 - static u16 convert_xrgb8888_to_rgba5551(u32 pix) 154 - { 155 - return ((pix & 0x00f80000) >> 8) | 156 - ((pix & 0x0000f800) >> 5) | 157 - ((pix & 0x000000f8) >> 2) | 158 - BIT(0); /* set alpha bit */ 159 - } 160 - 161 - static u16 convert_xrgb8888_to_xrgb1555(u32 pix) 162 - { 163 - return ((pix & 0x00f80000) >> 9) | 164 - ((pix & 0x0000f800) >> 6) | 165 - ((pix & 0x000000f8) >> 3); 166 - } 167 - 168 - static u16 convert_xrgb8888_to_argb1555(u32 pix) 169 - { 170 - return BIT(15) | /* set alpha bit */ 171 - ((pix & 0x00f80000) >> 9) | 172 - ((pix & 0x0000f800) >> 6) | 173 - ((pix & 0x000000f8) >> 3); 174 - } 175 - 176 - static u32 convert_xrgb8888_to_argb8888(u32 pix) 177 - { 178 - return pix | GENMASK(31, 24); /* fill alpha bits */ 179 - } 180 - 181 - static u32 convert_xrgb8888_to_xbgr8888(u32 pix) 182 - { 183 - return ((pix & 0x00ff0000) >> 16) << 0 | 184 - ((pix & 0x0000ff00) >> 8) << 8 | 185 - ((pix & 0x000000ff) >> 0) << 16 | 186 - ((pix & 0xff000000) >> 24) << 24; 187 - } 188 - 189 - static u32 convert_xrgb8888_to_abgr8888(u32 pix) 190 - { 191 - return ((pix & 0x00ff0000) >> 16) << 0 | 192 - ((pix & 0x0000ff00) >> 8) << 8 | 193 - ((pix & 0x000000ff) >> 0) << 16 | 194 - GENMASK(31, 24); /* fill alpha bits */ 195 - } 196 - 197 - static u32 convert_xrgb8888_to_xrgb2101010(u32 pix) 198 - { 199 - pix = ((pix & 0x000000FF) << 2) | 200 - ((pix & 0x0000FF00) << 4) | 201 - ((pix & 0x00FF0000) << 6); 202 - return pix | ((pix >> 8) & 0x00300C03); 203 - } 204 - 205 - static u32 convert_xrgb8888_to_argb2101010(u32 pix) 206 - { 207 - pix = ((pix & 0x000000FF) << 2) | 208 - ((pix & 0x0000FF00) << 4) | 209 - ((pix & 0x00FF0000) << 6); 210 - return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); 211 - } 212 - 213 - static u32 convert_xrgb8888_to_abgr2101010(u32 pix) 214 - { 215 - pix = ((pix & 0x00FF0000) >> 14) | 216 - ((pix & 0x0000FF00) << 4) | 217 - ((pix & 0x000000FF) << 22); 218 - return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); 219 - } 220 - 221 - /* 222 - * convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format 223 - * @color: input color, in xrgb8888 format 224 - * @format: output format 225 - * 226 - * Returns: 227 - * Color in the format specified, casted to u32. 228 - * Or 0 if the format is not supported. 229 - */ 230 - static u32 convert_from_xrgb8888(u32 color, u32 format) 231 - { 232 - switch (format) { 233 - case DRM_FORMAT_RGB565: 234 - return convert_xrgb8888_to_rgb565(color); 235 - case DRM_FORMAT_RGBA5551: 236 - return convert_xrgb8888_to_rgba5551(color); 237 - case DRM_FORMAT_XRGB1555: 238 - return convert_xrgb8888_to_xrgb1555(color); 239 - case DRM_FORMAT_ARGB1555: 240 - return convert_xrgb8888_to_argb1555(color); 241 - case DRM_FORMAT_RGB888: 242 - case DRM_FORMAT_XRGB8888: 243 - return color; 244 - case DRM_FORMAT_ARGB8888: 245 - return convert_xrgb8888_to_argb8888(color); 246 - case DRM_FORMAT_XBGR8888: 247 - return convert_xrgb8888_to_xbgr8888(color); 248 - case DRM_FORMAT_ABGR8888: 249 - return convert_xrgb8888_to_abgr8888(color); 250 - case DRM_FORMAT_XRGB2101010: 251 - return convert_xrgb8888_to_xrgb2101010(color); 252 - case DRM_FORMAT_ARGB2101010: 253 - return convert_xrgb8888_to_argb2101010(color); 254 - case DRM_FORMAT_ABGR2101010: 255 - return convert_xrgb8888_to_abgr2101010(color); 256 - default: 257 - WARN_ONCE(1, "Can't convert to %p4cc\n", &format); 258 - return 0; 259 - } 260 - } 261 - 262 - /* 263 - * Blit & Fill 264 - */ 265 - /* check if the pixel at coord x,y is 1 (foreground) or 0 (background) */ 266 - static bool drm_panic_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, int x, int y) 267 - { 268 - return (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) != 0; 269 - } 270 - 271 - static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch, 272 - const u8 *sbuf8, unsigned int spitch, 273 - unsigned int height, unsigned int width, 274 - unsigned int scale, u16 fg16) 275 - { 276 - unsigned int y, x; 277 - 278 - for (y = 0; y < height; y++) 279 - for (x = 0; x < width; x++) 280 - if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) 281 - iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, fg16); 282 - } 283 - 284 - static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch, 285 - const u8 *sbuf8, unsigned int spitch, 286 - unsigned int height, unsigned int width, 287 - unsigned int scale, u32 fg32) 288 - { 289 - unsigned int y, x; 290 - 291 - for (y = 0; y < height; y++) { 292 - for (x = 0; x < width; x++) { 293 - u32 off = y * dpitch + x * 3; 294 - 295 - if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) { 296 - /* write blue-green-red to output in little endianness */ 297 - iosys_map_wr(dmap, off, u8, (fg32 & 0x000000FF) >> 0); 298 - iosys_map_wr(dmap, off + 1, u8, (fg32 & 0x0000FF00) >> 8); 299 - iosys_map_wr(dmap, off + 2, u8, (fg32 & 0x00FF0000) >> 16); 300 - } 301 - } 302 - } 303 - } 304 - 305 - static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch, 306 - const u8 *sbuf8, unsigned int spitch, 307 - unsigned int height, unsigned int width, 308 - unsigned int scale, u32 fg32) 309 - { 310 - unsigned int y, x; 311 - 312 - for (y = 0; y < height; y++) 313 - for (x = 0; x < width; x++) 314 - if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) 315 - iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, fg32); 316 - } 317 - 318 144 static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect *clip, 319 145 const u8 *sbuf8, unsigned int spitch, unsigned int scale, 320 146 u32 fg_color) ··· 150 322 151 323 for (y = 0; y < drm_rect_height(clip); y++) 152 324 for (x = 0; x < drm_rect_width(clip); x++) 153 - if (drm_panic_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) 325 + if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) 154 326 sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color); 155 327 } 156 328 ··· 182 354 183 355 switch (sb->format->cpp[0]) { 184 356 case 2: 185 - drm_panic_blit16(&map, sb->pitch[0], sbuf8, spitch, 186 - drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); 357 + drm_draw_blit16(&map, sb->pitch[0], sbuf8, spitch, 358 + drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); 187 359 break; 188 360 case 3: 189 - drm_panic_blit24(&map, sb->pitch[0], sbuf8, spitch, 190 - drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); 361 + drm_draw_blit24(&map, sb->pitch[0], sbuf8, spitch, 362 + drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); 191 363 break; 192 364 case 4: 193 - drm_panic_blit32(&map, sb->pitch[0], sbuf8, spitch, 194 - drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); 365 + drm_draw_blit32(&map, sb->pitch[0], sbuf8, spitch, 366 + drm_rect_height(clip), drm_rect_width(clip), scale, fg_color); 195 367 break; 196 368 default: 197 369 WARN_ONCE(1, "Can't blit with pixel width %d\n", sb->format->cpp[0]); 198 370 } 199 - } 200 - 201 - static void drm_panic_fill16(struct iosys_map *dmap, unsigned int dpitch, 202 - unsigned int height, unsigned int width, 203 - u16 color) 204 - { 205 - unsigned int y, x; 206 - 207 - for (y = 0; y < height; y++) 208 - for (x = 0; x < width; x++) 209 - iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, color); 210 - } 211 - 212 - static void drm_panic_fill24(struct iosys_map *dmap, unsigned int dpitch, 213 - unsigned int height, unsigned int width, 214 - u32 color) 215 - { 216 - unsigned int y, x; 217 - 218 - for (y = 0; y < height; y++) { 219 - for (x = 0; x < width; x++) { 220 - unsigned int off = y * dpitch + x * 3; 221 - 222 - /* write blue-green-red to output in little endianness */ 223 - iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0); 224 - iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8); 225 - iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16); 226 - } 227 - } 228 - } 229 - 230 - static void drm_panic_fill32(struct iosys_map *dmap, unsigned int dpitch, 231 - unsigned int height, unsigned int width, 232 - u32 color) 233 - { 234 - unsigned int y, x; 235 - 236 - for (y = 0; y < height; y++) 237 - for (x = 0; x < width; x++) 238 - iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, color); 239 371 } 240 372 241 373 static void drm_panic_fill_pixel(struct drm_scanout_buffer *sb, ··· 230 442 231 443 switch (sb->format->cpp[0]) { 232 444 case 2: 233 - drm_panic_fill16(&map, sb->pitch[0], drm_rect_height(clip), 234 - drm_rect_width(clip), color); 445 + drm_draw_fill16(&map, sb->pitch[0], drm_rect_height(clip), 446 + drm_rect_width(clip), color); 235 447 break; 236 448 case 3: 237 - drm_panic_fill24(&map, sb->pitch[0], drm_rect_height(clip), 238 - drm_rect_width(clip), color); 449 + drm_draw_fill24(&map, sb->pitch[0], drm_rect_height(clip), 450 + drm_rect_width(clip), color); 239 451 break; 240 452 case 4: 241 - drm_panic_fill32(&map, sb->pitch[0], drm_rect_height(clip), 242 - drm_rect_width(clip), color); 453 + drm_draw_fill32(&map, sb->pitch[0], drm_rect_height(clip), 454 + drm_rect_width(clip), color); 243 455 break; 244 456 default: 245 457 WARN_ONCE(1, "Can't fill with pixel width %d\n", sb->format->cpp[0]); 246 458 } 247 - } 248 - 249 - static const u8 *get_char_bitmap(const struct font_desc *font, char c, size_t font_pitch) 250 - { 251 - return font->data + (c * font->height) * font_pitch; 252 459 } 253 460 254 461 static unsigned int get_max_line_len(const struct drm_panic_line *lines, int len) ··· 284 501 rec.x1 += (drm_rect_width(clip) - (line_len * font->width)) / 2; 285 502 286 503 for (j = 0; j < line_len; j++) { 287 - src = get_char_bitmap(font, msg[i].txt[j], font_pitch); 504 + src = drm_draw_get_char_bitmap(font, msg[i].txt[j], font_pitch); 288 505 rec.x2 = rec.x1 + font->width; 289 506 drm_panic_blit(sb, &rec, src, font_pitch, 1, color); 290 507 rec.x1 += font->width; ··· 316 533 317 534 static void draw_panic_static_user(struct drm_scanout_buffer *sb) 318 535 { 319 - u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format); 320 - u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format); 536 + u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, 537 + sb->format->format); 538 + u32 bg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, 539 + sb->format->format); 321 540 const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); 322 541 struct drm_rect r_screen, r_logo, r_msg; 323 542 unsigned int msg_width, msg_height; ··· 385 600 */ 386 601 static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb) 387 602 { 388 - u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format); 389 - u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format); 603 + u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, 604 + sb->format->format); 605 + u32 bg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, 606 + sb->format->format); 390 607 const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); 391 608 struct drm_rect r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height); 392 609 struct kmsg_dump_iter iter; ··· 578 791 */ 579 792 static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb) 580 793 { 581 - u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format); 582 - u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format); 794 + u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, 795 + sb->format->format); 796 + u32 bg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, 797 + sb->format->format); 583 798 const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); 584 799 struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas; 585 800 unsigned int max_qr_size, scale; ··· 667 878 { 668 879 if (format->num_planes != 1) 669 880 return false; 670 - return convert_from_xrgb8888(0xffffff, format->format) != 0; 881 + return drm_draw_color_from_xrgb8888(0xffffff, format->format) != 0; 671 882 } 672 883 673 884 static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
+2
drivers/gpu/drm/exynos/exynos_hdmi.c
··· 1648 1648 struct hdmi_context *hdata = dev_get_drvdata(dev); 1649 1649 struct drm_connector *connector = &hdata->connector; 1650 1650 1651 + mutex_lock(&connector->eld_mutex); 1651 1652 memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); 1653 + mutex_unlock(&connector->eld_mutex); 1652 1654 1653 1655 return 0; 1654 1656 }
+1
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
··· 6 6 */ 7 7 8 8 #include <linux/backlight.h> 9 + #include <linux/of.h> 9 10 #include <linux/of_graph.h> 10 11 11 12 #include <drm/drm_atomic_helper.h>
+1 -1
drivers/gpu/drm/i2c/ch7006_drv.c
··· 486 486 } 487 487 488 488 static const struct i2c_device_id ch7006_ids[] = { 489 - { "ch7006", 0 }, 489 + { "ch7006" }, 490 490 { } 491 491 }; 492 492 MODULE_DEVICE_TABLE(i2c, ch7006_ids);
+1 -1
drivers/gpu/drm/i2c/sil164_drv.c
··· 413 413 } 414 414 415 415 static const struct i2c_device_id sil164_ids[] = { 416 - { "sil164", 0 }, 416 + { "sil164" }, 417 417 { } 418 418 }; 419 419 MODULE_DEVICE_TABLE(i2c, sil164_ids);
+2 -2
drivers/gpu/drm/i2c/tda9950.c
··· 486 486 } 487 487 488 488 static struct i2c_device_id tda9950_ids[] = { 489 - { "tda9950", 0 }, 490 - { }, 489 + { "tda9950" }, 490 + { } 491 491 }; 492 492 MODULE_DEVICE_TABLE(i2c, tda9950_ids); 493 493
+1 -1
drivers/gpu/drm/i2c/tda998x_drv.c
··· 2094 2094 #endif 2095 2095 2096 2096 static const struct i2c_device_id tda998x_ids[] = { 2097 - { "tda998x", 0 }, 2097 + { "tda998x" }, 2098 2098 { } 2099 2099 }; 2100 2100 MODULE_DEVICE_TABLE(i2c, tda998x_ids);
+3
drivers/gpu/drm/i915/display/intel_audio.c
··· 699 699 const struct drm_display_mode *adjusted_mode = 700 700 &crtc_state->hw.adjusted_mode; 701 701 702 + mutex_lock(&connector->eld_mutex); 702 703 if (!connector->eld[0]) { 703 704 drm_dbg_kms(&i915->drm, 704 705 "Bogus ELD on [CONNECTOR:%d:%s]\n", 705 706 connector->base.id, connector->name); 707 + mutex_unlock(&connector->eld_mutex); 706 708 return false; 707 709 } 708 710 ··· 712 710 memcpy(crtc_state->eld, connector->eld, sizeof(crtc_state->eld)); 713 711 714 712 crtc_state->eld[6] = drm_av_sync_delay(connector, adjusted_mode) / 2; 713 + mutex_unlock(&connector->eld_mutex); 715 714 716 715 return true; 717 716 }
+9 -8
drivers/gpu/drm/i915/display/intel_dp_mst.c
··· 1715 1715 if (!intel_connector) 1716 1716 return NULL; 1717 1717 1718 + connector = &intel_connector->base; 1719 + 1718 1720 intel_connector->get_hw_state = intel_dp_mst_get_hw_state; 1719 1721 intel_connector->sync_state = intel_dp_connector_sync_state; 1720 1722 intel_connector->mst_port = intel_dp; ··· 1725 1723 1726 1724 intel_dp_init_modeset_retry_work(intel_connector); 1727 1725 1728 - intel_connector->dp.dsc_decompression_aux = drm_dp_mst_dsc_aux_for_port(port); 1729 - intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector); 1730 - intel_connector->dp.dsc_hblank_expansion_quirk = 1731 - detect_dsc_hblank_expansion_quirk(intel_connector); 1732 - 1733 - connector = &intel_connector->base; 1734 - ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, 1735 - DRM_MODE_CONNECTOR_DisplayPort); 1726 + ret = drm_connector_dynamic_init(&dev_priv->drm, connector, &intel_dp_mst_connector_funcs, 1727 + DRM_MODE_CONNECTOR_DisplayPort, NULL); 1736 1728 if (ret) { 1737 1729 drm_dp_mst_put_port_malloc(port); 1738 1730 intel_connector_free(intel_connector); 1739 1731 return NULL; 1740 1732 } 1733 + 1734 + intel_connector->dp.dsc_decompression_aux = drm_dp_mst_dsc_aux_for_port(port); 1735 + intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector); 1736 + intel_connector->dp.dsc_hblank_expansion_quirk = 1737 + detect_dsc_hblank_expansion_quirk(intel_connector); 1741 1738 1742 1739 drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs); 1743 1740
+2
drivers/gpu/drm/msm/dp/dp_audio.c
··· 414 414 return -ENODEV; 415 415 } 416 416 417 + mutex_lock(&msm_dp_display->connector->eld_mutex); 417 418 memcpy(buf, msm_dp_display->connector->eld, 418 419 min(sizeof(msm_dp_display->connector->eld), len)); 420 + mutex_unlock(&msm_dp_display->connector->eld_mutex); 419 421 420 422 return 0; 421 423 }
+2 -2
drivers/gpu/drm/nouveau/dispnv50/disp.c
··· 1265 1265 mstc->mstm = mstm; 1266 1266 mstc->port = port; 1267 1267 1268 - ret = drm_connector_init(dev, &mstc->connector, &nv50_mstc, 1269 - DRM_MODE_CONNECTOR_DisplayPort); 1268 + ret = drm_connector_dynamic_init(dev, &mstc->connector, &nv50_mstc, 1269 + DRM_MODE_CONNECTOR_DisplayPort, NULL); 1270 1270 if (ret) { 1271 1271 kfree(*pmstc); 1272 1272 *pmstc = NULL;
+1
drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
··· 9 9 #include <linux/backlight.h> 10 10 #include <linux/delay.h> 11 11 #include <linux/gpio/consumer.h> 12 + #include <linux/mod_devicetable.h> 12 13 #include <linux/module.h> 13 14 #include <linux/regulator/consumer.h> 14 15
+1
drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c
··· 11 11 #include <linux/backlight.h> 12 12 #include <linux/delay.h> 13 13 #include <linux/gpio/consumer.h> 14 + #include <linux/mod_devicetable.h> 14 15 #include <linux/module.h> 15 16 #include <linux/regulator/consumer.h> 16 17
+1
drivers/gpu/drm/panel/panel-samsung-s6e63m0.c
··· 15 15 #include <linux/delay.h> 16 16 #include <linux/gpio/consumer.h> 17 17 #include <linux/module.h> 18 + #include <linux/property.h> 18 19 #include <linux/regulator/consumer.h> 19 20 #include <linux/media-bus-format.h> 20 21
+102
drivers/gpu/drm/panel/panel-simple.c
··· 3222 3222 .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, 3223 3223 }; 3224 3224 3225 + static const struct display_timing multi_inno_mi0700a2t_30_timing = { 3226 + .pixelclock = { 26400000, 33000000, 46800000 }, 3227 + .hactive = { 800, 800, 800 }, 3228 + .hfront_porch = { 16, 204, 354 }, 3229 + .hback_porch = { 46, 46, 46 }, 3230 + .hsync_len = { 1, 6, 40 }, 3231 + .vactive = { 480, 480, 480 }, 3232 + .vfront_porch = { 7, 22, 147 }, 3233 + .vback_porch = { 23, 23, 23 }, 3234 + .vsync_len = { 1, 3, 20 }, 3235 + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | 3236 + DISPLAY_FLAGS_DE_HIGH, 3237 + }; 3238 + 3239 + static const struct panel_desc multi_inno_mi0700a2t_30 = { 3240 + .timings = &multi_inno_mi0700a2t_30_timing, 3241 + .num_timings = 1, 3242 + .bpc = 6, 3243 + .size = { 3244 + .width = 153, 3245 + .height = 92, 3246 + }, 3247 + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 3248 + .bus_flags = DRM_BUS_FLAG_DE_HIGH, 3249 + .connector_type = DRM_MODE_CONNECTOR_LVDS, 3250 + }; 3251 + 3225 3252 static const struct display_timing multi_inno_mi0700s4t_6_timing = { 3226 3253 .pixelclock = { 29000000, 33000000, 38000000 }, 3227 3254 .hactive = { 800, 800, 800 }, ··· 3336 3309 .disable = 50, 3337 3310 }, 3338 3311 .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 3312 + .bus_flags = DRM_BUS_FLAG_DE_HIGH, 3313 + .connector_type = DRM_MODE_CONNECTOR_LVDS, 3314 + }; 3315 + 3316 + static const struct display_timing multi_inno_mi1010z1t_1cp11_timing = { 3317 + .pixelclock = { 40800000, 51200000, 67200000 }, 3318 + .hactive = { 1024, 1024, 1024 }, 3319 + .hfront_porch = { 30, 110, 130 }, 3320 + .hback_porch = { 30, 110, 130 }, 3321 + .hsync_len = { 30, 100, 116 }, 3322 + .vactive = { 600, 600, 600 }, 3323 + .vfront_porch = { 4, 13, 80 }, 3324 + .vback_porch = { 4, 13, 80 }, 3325 + .vsync_len = { 2, 9, 40 }, 3326 + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | 3327 + DISPLAY_FLAGS_DE_HIGH, 3328 + }; 3329 + 3330 + static const struct panel_desc multi_inno_mi1010z1t_1cp11 = { 3331 + .timings = &multi_inno_mi1010z1t_1cp11_timing, 3332 + .num_timings = 1, 3333 + .bpc = 6, 3334 + .size = { 3335 + .width = 260, 3336 + .height = 162, 3337 + }, 3338 + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 3339 3339 .bus_flags = DRM_BUS_FLAG_DE_HIGH, 3340 3340 .connector_type = DRM_MODE_CONNECTOR_LVDS, 3341 3341 }; ··· 4334 4280 .bus_flags = DRM_BUS_FLAG_DE_HIGH, 4335 4281 }; 4336 4282 4283 + /* 4284 + * The datasheet computes total blanking as back porch + front porch, not 4285 + * including sync pulse width. This is for both H and V. To make the total 4286 + * blanking and period correct, subtract the pulse width from the front 4287 + * porch. 4288 + * 4289 + * This works well for the Min and Typ values, but for Max values the sync 4290 + * pulse width is higher than back porch + front porch, so work around that 4291 + * by reducing the Max sync length value to 1 and then treating the Max 4292 + * porches as in the Min and Typ cases. 4293 + * 4294 + * Exact datasheet values are added as a comment where they differ from the 4295 + * ones implemented for the above reason. 4296 + */ 4297 + static const struct display_timing tianma_tm070jdhg34_00_timing = { 4298 + .pixelclock = { 68400000, 71900000, 78100000 }, 4299 + .hactive = { 1280, 1280, 1280 }, 4300 + .hfront_porch = { 130, 138, 158 }, /* 131, 139, 159 */ 4301 + .hback_porch = { 5, 5, 5 }, 4302 + .hsync_len = { 1, 1, 1 }, /* 1, 1, 256 */ 4303 + .vactive = { 800, 800, 800 }, 4304 + .vfront_porch = { 2, 39, 98 }, /* 3, 40, 99 */ 4305 + .vback_porch = { 2, 2, 2 }, 4306 + .vsync_len = { 1, 1, 1 }, /* 1, 1, 128 */ 4307 + .flags = DISPLAY_FLAGS_DE_HIGH, 4308 + }; 4309 + 4310 + static const struct panel_desc tianma_tm070jdhg34_00 = { 4311 + .timings = &tianma_tm070jdhg34_00_timing, 4312 + .num_timings = 1, 4313 + .bpc = 8, 4314 + .size = { 4315 + .width = 150, /* 149.76 */ 4316 + .height = 94, /* 93.60 */ 4317 + }, 4318 + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 4319 + .connector_type = DRM_MODE_CONNECTOR_LVDS, 4320 + }; 4321 + 4337 4322 static const struct display_timing tianma_tm070rvhg71_timing = { 4338 4323 .pixelclock = { 27700000, 29200000, 39600000 }, 4339 4324 .hactive = { 800, 800, 800 }, ··· 4999 4906 .compatible = "mitsubishi,aa084xe01", 5000 4907 .data = &mitsubishi_aa084xe01, 5001 4908 }, { 4909 + .compatible = "multi-inno,mi0700a2t-30", 4910 + .data = &multi_inno_mi0700a2t_30, 4911 + }, { 5002 4912 .compatible = "multi-inno,mi0700s4t-6", 5003 4913 .data = &multi_inno_mi0700s4t_6, 5004 4914 }, { ··· 5010 4914 }, { 5011 4915 .compatible = "multi-inno,mi1010ait-1cp", 5012 4916 .data = &multi_inno_mi1010ait_1cp, 4917 + }, { 4918 + .compatible = "multi-inno,mi1010z1t-1cp11", 4919 + .data = &multi_inno_mi1010z1t_1cp11, 5013 4920 }, { 5014 4921 .compatible = "nec,nl12880bc20-05", 5015 4922 .data = &nec_nl12880bc20_05, ··· 5121 5022 }, { 5122 5023 .compatible = "tianma,tm070jdhg30", 5123 5024 .data = &tianma_tm070jdhg30, 5025 + }, { 5026 + .compatible = "tianma,tm070jdhg34-00", 5027 + .data = &tianma_tm070jdhg34_00, 5124 5028 }, { 5125 5029 .compatible = "tianma,tm070jvhg33", 5126 5030 .data = &tianma_tm070jvhg33,
+2 -7
drivers/gpu/drm/panel/panel-visionox-rm69299.c
··· 193 193 194 194 mipi_dsi_set_drvdata(dsi, ctx); 195 195 196 - ctx->panel.dev = dev; 197 196 ctx->dsi = dsi; 198 197 199 198 ctx->supplies[0].supply = "vdda"; ··· 200 201 ctx->supplies[1].supply = "vdd3p3"; 201 202 ctx->supplies[1].init_load_uA = 13200; 202 203 203 - ret = devm_regulator_bulk_get(ctx->panel.dev, ARRAY_SIZE(ctx->supplies), 204 - ctx->supplies); 204 + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), ctx->supplies); 205 205 if (ret < 0) 206 206 return ret; 207 207 208 - ctx->reset_gpio = devm_gpiod_get(ctx->panel.dev, 209 - "reset", GPIOD_OUT_LOW); 208 + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 210 209 if (IS_ERR(ctx->reset_gpio)) { 211 210 dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio)); 212 211 return PTR_ERR(ctx->reset_gpio); ··· 212 215 213 216 drm_panel_init(&ctx->panel, dev, &visionox_rm69299_drm_funcs, 214 217 DRM_MODE_CONNECTOR_DSI); 215 - ctx->panel.dev = dev; 216 - ctx->panel.funcs = &visionox_rm69299_drm_funcs; 217 218 drm_panel_add(&ctx->panel); 218 219 219 220 dsi->lanes = 4;
+6 -6
drivers/gpu/drm/panthor/panthor_devfreq.c
··· 243 243 return 0; 244 244 } 245 245 246 - int panthor_devfreq_resume(struct panthor_device *ptdev) 246 + void panthor_devfreq_resume(struct panthor_device *ptdev) 247 247 { 248 248 struct panthor_devfreq *pdevfreq = ptdev->devfreq; 249 249 250 250 if (!pdevfreq->devfreq) 251 - return 0; 251 + return; 252 252 253 253 panthor_devfreq_reset(pdevfreq); 254 254 255 - return devfreq_resume_device(pdevfreq->devfreq); 255 + drm_WARN_ON(&ptdev->base, devfreq_resume_device(pdevfreq->devfreq)); 256 256 } 257 257 258 - int panthor_devfreq_suspend(struct panthor_device *ptdev) 258 + void panthor_devfreq_suspend(struct panthor_device *ptdev) 259 259 { 260 260 struct panthor_devfreq *pdevfreq = ptdev->devfreq; 261 261 262 262 if (!pdevfreq->devfreq) 263 - return 0; 263 + return; 264 264 265 - return devfreq_suspend_device(pdevfreq->devfreq); 265 + drm_WARN_ON(&ptdev->base, devfreq_suspend_device(pdevfreq->devfreq)); 266 266 } 267 267 268 268 void panthor_devfreq_record_busy(struct panthor_device *ptdev)
+2 -2
drivers/gpu/drm/panthor/panthor_devfreq.h
··· 12 12 13 13 int panthor_devfreq_init(struct panthor_device *ptdev); 14 14 15 - int panthor_devfreq_resume(struct panthor_device *ptdev); 16 - int panthor_devfreq_suspend(struct panthor_device *ptdev); 15 + void panthor_devfreq_resume(struct panthor_device *ptdev); 16 + void panthor_devfreq_suspend(struct panthor_device *ptdev); 17 17 18 18 void panthor_devfreq_record_busy(struct panthor_device *ptdev); 19 19 void panthor_devfreq_record_idle(struct panthor_device *ptdev);
+28 -40
drivers/gpu/drm/panthor/panthor_device.c
··· 435 435 return 0; 436 436 } 437 437 438 + static int panthor_device_resume_hw_components(struct panthor_device *ptdev) 439 + { 440 + int ret; 441 + 442 + panthor_gpu_resume(ptdev); 443 + panthor_mmu_resume(ptdev); 444 + 445 + ret = panthor_fw_resume(ptdev); 446 + if (!ret) 447 + return 0; 448 + 449 + panthor_mmu_suspend(ptdev); 450 + panthor_gpu_suspend(ptdev); 451 + return ret; 452 + } 453 + 438 454 int panthor_device_resume(struct device *dev) 439 455 { 440 456 struct panthor_device *ptdev = dev_get_drvdata(dev); ··· 473 457 if (ret) 474 458 goto err_disable_stacks_clk; 475 459 476 - ret = panthor_devfreq_resume(ptdev); 477 - if (ret) 478 - goto err_disable_coregroup_clk; 460 + panthor_devfreq_resume(ptdev); 479 461 480 462 if (panthor_device_is_initialized(ptdev) && 481 463 drm_dev_enter(&ptdev->base, &cookie)) { 482 - panthor_gpu_resume(ptdev); 483 - panthor_mmu_resume(ptdev); 484 - ret = drm_WARN_ON(&ptdev->base, panthor_fw_resume(ptdev)); 485 - if (!ret) { 486 - panthor_sched_resume(ptdev); 487 - } else { 488 - panthor_mmu_suspend(ptdev); 489 - panthor_gpu_suspend(ptdev); 464 + ret = panthor_device_resume_hw_components(ptdev); 465 + if (ret && ptdev->reset.fast) { 466 + drm_err(&ptdev->base, "Fast reset failed, trying a slow reset"); 467 + ptdev->reset.fast = false; 468 + ret = panthor_device_resume_hw_components(ptdev); 490 469 } 470 + 471 + if (!ret) 472 + panthor_sched_resume(ptdev); 491 473 492 474 drm_dev_exit(cookie); 493 475 ··· 510 496 511 497 err_suspend_devfreq: 512 498 panthor_devfreq_suspend(ptdev); 513 - 514 - err_disable_coregroup_clk: 515 499 clk_disable_unprepare(ptdev->clks.coregroup); 516 500 517 501 err_disable_stacks_clk: ··· 520 508 521 509 err_set_suspended: 522 510 atomic_set(&ptdev->pm.state, PANTHOR_DEVICE_PM_STATE_SUSPENDED); 511 + atomic_set(&ptdev->pm.recovery_needed, 1); 523 512 return ret; 524 513 } 525 514 526 515 int panthor_device_suspend(struct device *dev) 527 516 { 528 517 struct panthor_device *ptdev = dev_get_drvdata(dev); 529 - int ret, cookie; 518 + int cookie; 530 519 531 520 if (atomic_read(&ptdev->pm.state) != PANTHOR_DEVICE_PM_STATE_ACTIVE) 532 521 return -EINVAL; ··· 559 546 drm_dev_exit(cookie); 560 547 } 561 548 562 - ret = panthor_devfreq_suspend(ptdev); 563 - if (ret) { 564 - if (panthor_device_is_initialized(ptdev) && 565 - drm_dev_enter(&ptdev->base, &cookie)) { 566 - panthor_gpu_resume(ptdev); 567 - panthor_mmu_resume(ptdev); 568 - drm_WARN_ON(&ptdev->base, panthor_fw_resume(ptdev)); 569 - panthor_sched_resume(ptdev); 570 - drm_dev_exit(cookie); 571 - } 572 - 573 - goto err_set_active; 574 - } 549 + panthor_devfreq_suspend(ptdev); 575 550 576 551 clk_disable_unprepare(ptdev->clks.coregroup); 577 552 clk_disable_unprepare(ptdev->clks.stacks); 578 553 clk_disable_unprepare(ptdev->clks.core); 579 554 atomic_set(&ptdev->pm.state, PANTHOR_DEVICE_PM_STATE_SUSPENDED); 580 555 return 0; 581 - 582 - err_set_active: 583 - /* If something failed and we have to revert back to an 584 - * active state, we also need to clear the MMIO userspace 585 - * mappings, so any dumb pages that were mapped while we 586 - * were trying to suspend gets invalidated. 587 - */ 588 - mutex_lock(&ptdev->pm.mmio_lock); 589 - atomic_set(&ptdev->pm.state, PANTHOR_DEVICE_PM_STATE_ACTIVE); 590 - unmap_mapping_range(ptdev->base.anon_inode->i_mapping, 591 - DRM_PANTHOR_USER_MMIO_OFFSET, 0, 1); 592 - mutex_unlock(&ptdev->pm.mmio_lock); 593 - return ret; 594 556 }
+37
drivers/gpu/drm/panthor/panthor_device.h
··· 9 9 #include <linux/atomic.h> 10 10 #include <linux/io-pgtable.h> 11 11 #include <linux/regulator/consumer.h> 12 + #include <linux/pm_runtime.h> 12 13 #include <linux/sched.h> 13 14 #include <linux/spinlock.h> 14 15 ··· 157 156 158 157 /** @pending: Set to true if a reset is pending. */ 159 158 atomic_t pending; 159 + 160 + /** 161 + * @fast: True if the post_reset logic can proceed with a fast reset. 162 + * 163 + * A fast reset is just a reset where the driver doesn't reload the FW sections. 164 + * 165 + * Any time the firmware is properly suspended, a fast reset can take place. 166 + * On the other hand, if the halt operation failed, the driver will reload 167 + * all FW sections to make sure we start from a fresh state. 168 + */ 169 + bool fast; 160 170 } reset; 161 171 162 172 /** @pm: Power management related data. */ ··· 192 180 * is suspended. 193 181 */ 194 182 struct page *dummy_latest_flush; 183 + 184 + /** @recovery_needed: True when a resume attempt failed. */ 185 + atomic_t recovery_needed; 195 186 } pm; 196 187 197 188 /** @profile_mask: User-set profiling flags for job accounting. */ ··· 257 242 258 243 int panthor_device_resume(struct device *dev); 259 244 int panthor_device_suspend(struct device *dev); 245 + 246 + static inline int panthor_device_resume_and_get(struct panthor_device *ptdev) 247 + { 248 + int ret = pm_runtime_resume_and_get(ptdev->base.dev); 249 + 250 + /* If the resume failed, we need to clear the runtime_error, which 251 + * can done by forcing the RPM state to suspended. If multiple 252 + * threads called panthor_device_resume_and_get(), we only want 253 + * one of them to update the state, hence the cmpxchg. Note that a 254 + * thread might enter panthor_device_resume_and_get() and call 255 + * pm_runtime_resume_and_get() after another thread had attempted 256 + * to resume and failed. This means we will end up with an error 257 + * without even attempting a resume ourselves. The only risk here 258 + * is to report an error when the second resume attempt might have 259 + * succeeded. Given resume errors are not expected, this is probably 260 + * something we can live with. 261 + */ 262 + if (ret && atomic_cmpxchg(&ptdev->pm.recovery_needed, 1, 0) == 1) 263 + pm_runtime_set_suspended(ptdev->base.dev); 264 + 265 + return ret; 266 + } 260 267 261 268 enum drm_panthor_exception_type { 262 269 DRM_PANTHOR_EXCEPTION_OK = 0x00,
+3 -2
drivers/gpu/drm/panthor/panthor_drv.c
··· 763 763 { 764 764 int ret; 765 765 766 - ret = pm_runtime_resume_and_get(ptdev->base.dev); 766 + ret = panthor_device_resume_and_get(ptdev); 767 767 if (ret) 768 768 return ret; 769 769 ··· 1493 1493 * - 1.1 - adds DEV_QUERY_TIMESTAMP_INFO query 1494 1494 * - 1.2 - adds DEV_QUERY_GROUP_PRIORITIES_INFO query 1495 1495 * - adds PANTHOR_GROUP_PRIORITY_REALTIME priority 1496 + * - 1.3 - adds DRM_PANTHOR_GROUP_STATE_INNOCENT flag 1496 1497 */ 1497 1498 static const struct drm_driver panthor_drm_driver = { 1498 1499 .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ | ··· 1507 1506 .name = "panthor", 1508 1507 .desc = "Panthor DRM driver", 1509 1508 .major = 1, 1510 - .minor = 2, 1509 + .minor = 3, 1511 1510 1512 1511 .gem_create_object = panthor_gem_create_object, 1513 1512 .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,
+21 -47
drivers/gpu/drm/panthor/panthor_fw.c
··· 12 12 #include <linux/iosys-map.h> 13 13 #include <linux/mutex.h> 14 14 #include <linux/platform_device.h> 15 + #include <linux/pm_runtime.h> 15 16 16 17 #include <drm/drm_drv.h> 17 18 #include <drm/drm_managed.h> ··· 262 261 263 262 /** @booted: True is the FW is booted */ 264 263 bool booted; 265 - 266 - /** 267 - * @fast_reset: True if the post_reset logic can proceed with a fast reset. 268 - * 269 - * A fast reset is just a reset where the driver doesn't reload the FW sections. 270 - * 271 - * Any time the firmware is properly suspended, a fast reset can take place. 272 - * On the other hand, if the halt operation failed, the driver will reload 273 - * all sections to make sure we start from a fresh state. 274 - */ 275 - bool fast_reset; 276 264 277 265 /** @irq: Job irq data. */ 278 266 struct panthor_irq irq; ··· 1079 1089 /* Make sure we won't be woken up by a ping. */ 1080 1090 cancel_delayed_work_sync(&ptdev->fw->watchdog.ping_work); 1081 1091 1082 - ptdev->fw->fast_reset = false; 1092 + ptdev->reset.fast = false; 1083 1093 1084 1094 if (!on_hang) { 1085 1095 struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); ··· 1089 1099 gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1); 1090 1100 if (!readl_poll_timeout(ptdev->iomem + MCU_STATUS, status, 1091 1101 status == MCU_STATUS_HALT, 10, 100000)) { 1092 - ptdev->fw->fast_reset = true; 1102 + ptdev->reset.fast = true; 1093 1103 } else { 1094 1104 drm_warn(&ptdev->base, "Failed to cleanly suspend MCU"); 1095 1105 } ··· 1114 1124 if (ret) 1115 1125 return ret; 1116 1126 1117 - /* If this is a fast reset, try to start the MCU without reloading 1118 - * the FW sections. If it fails, go for a full reset. 1119 - */ 1120 - if (ptdev->fw->fast_reset) { 1127 + if (!ptdev->reset.fast) { 1128 + /* On a slow reset, reload all sections, including RO ones. 1129 + * We're not supposed to end up here anyway, let's just assume 1130 + * the overhead of reloading everything is acceptable. 1131 + */ 1132 + panthor_reload_fw_sections(ptdev, true); 1133 + } else { 1121 1134 /* The FW detects 0 -> 1 transitions. Make sure we reset 1122 1135 * the HALT bit before the FW is rebooted. 1123 1136 * This is not needed on a slow reset because FW sections are 1124 1137 * re-initialized. 1125 1138 */ 1126 1139 struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); 1140 + 1127 1141 panthor_fw_update_reqs(glb_iface, req, 0, GLB_HALT); 1128 - 1129 - ret = panthor_fw_start(ptdev); 1130 - if (!ret) 1131 - goto out; 1132 - 1133 - /* Forcibly reset the MCU and force a slow reset, so we get a 1134 - * fresh boot on the next panthor_fw_start() call. 1135 - */ 1136 - panthor_fw_stop(ptdev); 1137 - ptdev->fw->fast_reset = false; 1138 - drm_err(&ptdev->base, "FW fast reset failed, trying a slow reset"); 1139 - 1140 - ret = panthor_vm_flush_all(ptdev->fw->vm); 1141 - if (ret) { 1142 - drm_err(&ptdev->base, "FW slow reset failed (couldn't flush FW's AS l2cache)"); 1143 - return ret; 1144 - } 1145 1142 } 1146 - 1147 - /* Reload all sections, including RO ones. We're not supposed 1148 - * to end up here anyway, let's just assume the overhead of 1149 - * reloading everything is acceptable. 1150 - */ 1151 - panthor_reload_fw_sections(ptdev, true); 1152 1143 1153 1144 ret = panthor_fw_start(ptdev); 1154 1145 if (ret) { 1155 - drm_err(&ptdev->base, "FW slow reset failed (couldn't start the FW )"); 1146 + drm_err(&ptdev->base, "FW %s reset failed", 1147 + ptdev->reset.fast ? "fast" : "slow"); 1156 1148 return ret; 1157 1149 } 1158 1150 1159 - out: 1160 1151 /* We must re-initialize the global interface even on fast-reset. */ 1161 1152 panthor_fw_init_global_iface(ptdev); 1162 1153 return 0; ··· 1161 1190 1162 1191 cancel_delayed_work_sync(&ptdev->fw->watchdog.ping_work); 1163 1192 1164 - /* Make sure the IRQ handler can be called after that point. */ 1165 - if (ptdev->fw->irq.irq) 1166 - panthor_job_irq_suspend(&ptdev->fw->irq); 1193 + if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) { 1194 + /* Make sure the IRQ handler cannot be called after that point. */ 1195 + if (ptdev->fw->irq.irq) 1196 + panthor_job_irq_suspend(&ptdev->fw->irq); 1167 1197 1168 - panthor_fw_stop(ptdev); 1198 + panthor_fw_stop(ptdev); 1199 + } 1169 1200 1170 1201 list_for_each_entry(section, &ptdev->fw->sections, node) 1171 1202 panthor_kernel_bo_destroy(section->mem); ··· 1180 1207 panthor_vm_put(ptdev->fw->vm); 1181 1208 ptdev->fw->vm = NULL; 1182 1209 1183 - panthor_gpu_power_off(ptdev, L2, ptdev->gpu_info.l2_present, 20000); 1210 + if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) 1211 + panthor_gpu_power_off(ptdev, L2, ptdev->gpu_info.l2_present, 20000); 1184 1212 } 1185 1213 1186 1214 /**
+8 -6
drivers/gpu/drm/panthor/panthor_gpu.c
··· 180 180 unsigned long flags; 181 181 182 182 /* Make sure the IRQ handler is not running after that point. */ 183 - panthor_gpu_irq_suspend(&ptdev->gpu->irq); 183 + if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) 184 + panthor_gpu_irq_suspend(&ptdev->gpu->irq); 184 185 185 186 /* Wake-up all waiters. */ 186 187 spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); ··· 470 469 */ 471 470 void panthor_gpu_suspend(struct panthor_device *ptdev) 472 471 { 473 - /* 474 - * It may be preferable to simply power down the L2, but for now just 475 - * soft-reset which will leave the L2 powered down. 476 - */ 477 - panthor_gpu_soft_reset(ptdev); 472 + /* On a fast reset, simply power down the L2. */ 473 + if (!ptdev->reset.fast) 474 + panthor_gpu_soft_reset(ptdev); 475 + else 476 + panthor_gpu_power_off(ptdev, L2, 1, 20000); 477 + 478 478 panthor_gpu_irq_suspend(&ptdev->gpu->irq); 479 479 } 480 480
+2 -1
drivers/gpu/drm/panthor/panthor_mmu.c
··· 2672 2672 */ 2673 2673 void panthor_mmu_unplug(struct panthor_device *ptdev) 2674 2674 { 2675 - panthor_mmu_irq_suspend(&ptdev->mmu->irq); 2675 + if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) 2676 + panthor_mmu_irq_suspend(&ptdev->mmu->irq); 2676 2677 2677 2678 mutex_lock(&ptdev->mmu->as.slots_lock); 2678 2679 for (u32 i = 0; i < ARRAY_SIZE(ptdev->mmu->as.slots); i++) {
+20 -2
drivers/gpu/drm/panthor/panthor_sched.c
··· 611 611 bool timedout; 612 612 613 613 /** 614 + * @innocent: True when the group becomes unusable because the group suspension 615 + * failed during a reset. 616 + * 617 + * Sometimes the FW was put in a bad state by other groups, causing the group 618 + * suspension happening in the reset path to fail. In that case, we consider the 619 + * group innocent. 620 + */ 621 + bool innocent; 622 + 623 + /** 614 624 * @syncobjs: Pool of per-queue synchronization objects. 615 625 * 616 626 * One sync object per queue. The position of the sync object is ··· 2364 2354 if (!drm_dev_enter(&ptdev->base, &cookie)) 2365 2355 return; 2366 2356 2367 - ret = pm_runtime_resume_and_get(ptdev->base.dev); 2357 + ret = panthor_device_resume_and_get(ptdev); 2368 2358 if (drm_WARN_ON(&ptdev->base, ret)) 2369 2359 goto out_dev_exit; 2370 2360 ··· 2699 2689 while (slot_mask) { 2700 2690 u32 csg_id = ffs(slot_mask) - 1; 2701 2691 struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id]; 2692 + 2693 + /* If the group was still usable before that point, we consider 2694 + * it innocent. 2695 + */ 2696 + if (group_can_run(csg_slot->group)) 2697 + csg_slot->group->innocent = true; 2702 2698 2703 2699 /* We consider group suspension failures as fatal and flag the 2704 2700 * group as unusable by setting timedout=true. ··· 3131 3115 return dma_fence_get(job->done_fence); 3132 3116 } 3133 3117 3134 - ret = pm_runtime_resume_and_get(ptdev->base.dev); 3118 + ret = panthor_device_resume_and_get(ptdev); 3135 3119 if (drm_WARN_ON(&ptdev->base, ret)) 3136 3120 return ERR_PTR(ret); 3137 3121 ··· 3586 3570 get_state->state |= DRM_PANTHOR_GROUP_STATE_FATAL_FAULT; 3587 3571 get_state->fatal_queues = group->fatal_queues; 3588 3572 } 3573 + if (group->innocent) 3574 + get_state->state |= DRM_PANTHOR_GROUP_STATE_INNOCENT; 3589 3575 mutex_unlock(&sched->lock); 3590 3576 3591 3577 group_put(group);
+2
drivers/gpu/drm/radeon/radeon_audio.c
··· 775 775 if (!dig->pin || dig->pin->id != port) 776 776 continue; 777 777 *enabled = true; 778 + mutex_lock(&connector->eld_mutex); 778 779 ret = drm_eld_size(connector->eld); 779 780 memcpy(buf, connector->eld, min(max_bytes, ret)); 781 + mutex_unlock(&connector->eld_mutex); 780 782 break; 781 783 } 782 784
+18
drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
··· 546 546 .dsi_clk_mask = BIT(1) | BIT(0), 547 547 }; 548 548 549 + static const struct rcar_du_device_info rcar_du_r8a779h0_info = { 550 + .gen = 4, 551 + .features = RCAR_DU_FEATURE_CRTC_IRQ 552 + | RCAR_DU_FEATURE_VSP1_SOURCE 553 + | RCAR_DU_FEATURE_NO_BLENDING, 554 + .channels_mask = BIT(0), 555 + .routes = { 556 + /* R8A779H0 has one MIPI DSI output. */ 557 + [RCAR_DU_OUTPUT_DSI0] = { 558 + .possible_crtcs = BIT(0), 559 + .port = 0, 560 + }, 561 + }, 562 + .num_rpf = 5, 563 + .dsi_clk_mask = BIT(0), 564 + }; 565 + 549 566 static const struct of_device_id rcar_du_of_table[] = { 550 567 { .compatible = "renesas,du-r8a7742", .data = &rcar_du_r8a7790_info }, 551 568 { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, ··· 589 572 { .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info }, 590 573 { .compatible = "renesas,du-r8a779a0", .data = &rcar_du_r8a779a0_info }, 591 574 { .compatible = "renesas,du-r8a779g0", .data = &rcar_du_r8a779g0_info }, 575 + { .compatible = "renesas,du-r8a779h0", .data = &rcar_du_r8a779h0_info }, 592 576 { } 593 577 }; 594 578
+18 -6
drivers/gpu/drm/renesas/rcar-du/rcar_du_group.c
··· 107 107 */ 108 108 rcrtc = rcdu->crtcs; 109 109 num_crtcs = rcdu->num_crtcs; 110 - } else if (rcdu->info->gen >= 3 && rgrp->num_crtcs > 1) { 110 + } else if ((rcdu->info->gen == 3 && rgrp->num_crtcs > 1) || 111 + rcdu->info->gen == 4) { 111 112 /* 112 113 * On Gen3 dot clocks are setup through per-group registers, 113 114 * only available when the group has two channels. 115 + * On Gen4 the registers are there for single channel too. 114 116 */ 115 117 rcrtc = &rcdu->crtcs[rgrp->index * 2]; 116 118 num_crtcs = rgrp->num_crtcs; ··· 187 185 dorcr |= DORCR_PG1T | DORCR_DK1S | DORCR_PG1D_DS1; 188 186 rcar_du_group_write(rgrp, DORCR, dorcr); 189 187 190 - /* Apply planes to CRTCs association. */ 191 - mutex_lock(&rgrp->lock); 192 - rcar_du_group_write(rgrp, DPTSR, (rgrp->dptsr_planes << 16) | 193 - rgrp->dptsr_planes); 194 - mutex_unlock(&rgrp->lock); 188 + /* 189 + * DPTSR is used to select the source for the planes of a group. The 190 + * first source is chosen by writing 0 to the respective bits, and this 191 + * is always the default value of the register. In other words, writing 192 + * DPTSR is only needed if the SoC supports choosing the second source. 193 + * 194 + * The SoCs documentations seems to confirm this, as the DPTSR register 195 + * is not documented if only the first source exists on that SoC. 196 + */ 197 + if (rgrp->channels_mask & BIT(1)) { 198 + mutex_lock(&rgrp->lock); 199 + rcar_du_group_write(rgrp, DPTSR, (rgrp->dptsr_planes << 16) | 200 + rgrp->dptsr_planes); 201 + mutex_unlock(&rgrp->lock); 202 + } 195 203 } 196 204 197 205 /*
+3 -1
drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
··· 587 587 for (timeout = 10; timeout > 0; --timeout) { 588 588 if ((rcar_mipi_dsi_read(dsi, PPICLSR) & PPICLSR_STPST) && 589 589 (rcar_mipi_dsi_read(dsi, PPIDLSR) & PPIDLSR_STPST) && 590 - (rcar_mipi_dsi_read(dsi, CLOCKSET1) & CLOCKSET1_LOCK)) 590 + (rcar_mipi_dsi_read(dsi, CLOCKSET1) & CLOCKSET1_LOCK_PHY)) 591 591 break; 592 592 593 593 usleep_range(1000, 2000); ··· 1081 1081 static const struct of_device_id rcar_mipi_dsi_of_table[] = { 1082 1082 { .compatible = "renesas,r8a779a0-dsi-csi2-tx", .data = &v3u_data }, 1083 1083 { .compatible = "renesas,r8a779g0-dsi-csi2-tx", .data = &v4h_data }, 1084 + /* DSI in r8a779h0 is identical to r8a779g0 */ 1085 + { .compatible = "renesas,r8a779h0-dsi-csi2-tx", .data = &v4h_data }, 1084 1086 { } 1085 1087 }; 1086 1088
-1
drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
··· 142 142 143 143 #define CLOCKSET1 0x101c 144 144 #define CLOCKSET1_LOCK_PHY (1 << 17) 145 - #define CLOCKSET1_LOCK (1 << 16) 146 145 #define CLOCKSET1_CLKSEL (1 << 8) 147 146 #define CLOCKSET1_CLKINSEL_EXTAL (0 << 2) 148 147 #define CLOCKSET1_CLKINSEL_DIG (1 << 2)
+1 -7
drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
··· 28 28 #include "rzg2l_du_vsp.h" 29 29 30 30 #define DU_MCR0 0x00 31 - #define DU_MCR0_DPI_OE BIT(0) 32 31 #define DU_MCR0_DI_EN BIT(8) 33 32 34 33 #define DU_DITR0 0x10 ··· 216 217 217 218 static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start) 218 219 { 219 - struct rzg2l_du_crtc_state *rstate = to_rzg2l_crtc_state(rcrtc->crtc.state); 220 220 struct rzg2l_du_device *rcdu = rcrtc->dev; 221 - u32 val = DU_MCR0_DI_EN; 222 221 223 - if (rstate->outputs & BIT(RZG2L_DU_OUTPUT_DPAD0)) 224 - val |= DU_MCR0_DPI_OE; 225 - 226 - writel(start ? val : 0, rcdu->mmio + DU_MCR0); 222 + writel(start ? DU_MCR0_DI_EN : 0, rcdu->mmio + DU_MCR0); 227 223 } 228 224 229 225 static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc)
+18
drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
··· 10 10 #include <linux/export.h> 11 11 #include <linux/of.h> 12 12 13 + #include <drm/drm_atomic_helper.h> 13 14 #include <drm/drm_bridge.h> 14 15 #include <drm/drm_bridge_connector.h> 15 16 #include <drm/drm_panel.h> ··· 23 22 */ 24 23 25 24 static const struct drm_encoder_funcs rzg2l_du_encoder_funcs = { 25 + }; 26 + 27 + static enum drm_mode_status 28 + rzg2l_du_encoder_mode_valid(struct drm_encoder *encoder, 29 + const struct drm_display_mode *mode) 30 + { 31 + struct rzg2l_du_encoder *renc = to_rzg2l_encoder(encoder); 32 + 33 + if (renc->output == RZG2L_DU_OUTPUT_DPAD0 && mode->clock > 83500) 34 + return MODE_CLOCK_HIGH; 35 + 36 + return MODE_OK; 37 + } 38 + 39 + static const struct drm_encoder_helper_funcs rzg2l_du_encoder_helper_funcs = { 40 + .mode_valid = rzg2l_du_encoder_mode_valid, 26 41 }; 27 42 28 43 int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu, ··· 65 48 return PTR_ERR(renc); 66 49 67 50 renc->output = output; 51 + drm_encoder_helper_add(&renc->base, &rzg2l_du_encoder_helper_funcs); 68 52 69 53 /* Attach the bridge to the encoder. */ 70 54 ret = drm_bridge_attach(&renc->base, bridge, NULL,
+10
drivers/gpu/drm/rockchip/Kconfig
··· 11 11 select DRM_DW_HDMI if ROCKCHIP_DW_HDMI 12 12 select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP 13 13 select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI 14 + select DRM_DW_MIPI_DSI2 if ROCKCHIP_DW_MIPI_DSI2 14 15 select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI 15 16 select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI 16 17 select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC ··· 81 80 This selects support for Rockchip SoC specific extensions 82 81 for the Synopsys DesignWare dsi driver. If you want to 83 82 enable MIPI DSI on RK3288 or RK3399 based SoC, you should 83 + select this option. 84 + 85 + config ROCKCHIP_DW_MIPI_DSI2 86 + bool "Rockchip specific extensions for Synopsys DW MIPI DSI2" 87 + select GENERIC_PHY_MIPI_DPHY 88 + help 89 + This selects support for Rockchip SoC specific extensions 90 + for the Synopsys DesignWare DSI2 driver. If you want to 91 + enable MIPI DSI on RK3576 or RK3588 based SoC, you should 84 92 select this option. 85 93 86 94 config ROCKCHIP_INNO_HDMI
+1
drivers/gpu/drm/rockchip/Makefile
··· 13 13 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o 14 14 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o 15 15 rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o 16 + rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o 16 17 rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o 17 18 rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o 18 19 rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
+1 -1
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
··· 2 2 /* 3 3 * Rockchip SoC DP (Display Port) interface driver. 4 4 * 5 - * Copyright (C) Fuzhou Rockchip Electronics Co., Ltd. 5 + * Copyright (C) Rockchip Electronics Co., Ltd. 6 6 * Author: Andy Yan <andy.yan@rock-chips.com> 7 7 * Yakir Yang <ykk@rock-chips.com> 8 8 * Jeff Chen <jeff.chen@rock-chips.com>
+1 -1
drivers/gpu/drm/rockchip/cdn-dp-core.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: Chris Zhong <zyw@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/cdn-dp-core.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 3 * Copyright (C) 2016 Chris Zhong <zyw@rock-chips.com> 4 - * Copyright (C) 2016 ROCKCHIP, Inc. 4 + * Copyright (C) Rockchip Electronics Co., Ltd. 5 5 */ 6 6 7 7 #ifndef _CDN_DP_CORE_H
+1 -1
drivers/gpu/drm/rockchip/cdn-dp-reg.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: Chris Zhong <zyw@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/cdn-dp-reg.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: Chris Zhong <zyw@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0+ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: 5 5 * Chris Zhong <zyw@rock-chips.com> 6 6 * Nickey Yang <nickey.yang@rock-chips.com>
+487
drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright (C) 2024 Rockchip Electronics Co., Ltd. 4 + * Author: 5 + * Guochun Huang <hero.huang@rock-chips.com> 6 + * Heiko Stuebner <heiko.stuebner@cherry.de> 7 + */ 8 + 9 + #include <linux/bitfield.h> 10 + #include <linux/clk.h> 11 + #include <linux/component.h> 12 + #include <linux/media-bus-format.h> 13 + #include <linux/mod_devicetable.h> 14 + #include <linux/module.h> 15 + #include <linux/of.h> 16 + #include <linux/pm_runtime.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/regmap.h> 19 + #include <linux/reset.h> 20 + #include <linux/mfd/syscon.h> 21 + #include <linux/phy/phy.h> 22 + 23 + #include <drm/bridge/dw_mipi_dsi2.h> 24 + #include <drm/drm_mipi_dsi.h> 25 + #include <drm/drm_of.h> 26 + #include <drm/drm_simple_kms_helper.h> 27 + 28 + #include <uapi/linux/videodev2.h> 29 + 30 + #include "rockchip_drm_drv.h" 31 + 32 + #define PSEC_PER_SEC 1000000000000LL 33 + 34 + struct dsigrf_reg { 35 + u16 offset; 36 + u16 lsb; 37 + u16 msb; 38 + }; 39 + 40 + enum grf_reg_fields { 41 + TXREQCLKHS_EN, 42 + GATING_EN, 43 + IPI_SHUTDN, 44 + IPI_COLORM, 45 + IPI_COLOR_DEPTH, 46 + IPI_FORMAT, 47 + MAX_FIELDS, 48 + }; 49 + 50 + #define IPI_DEPTH_5_6_5_BITS 0x02 51 + #define IPI_DEPTH_6_BITS 0x03 52 + #define IPI_DEPTH_8_BITS 0x05 53 + #define IPI_DEPTH_10_BITS 0x06 54 + 55 + struct rockchip_dw_dsi2_chip_data { 56 + u32 reg; 57 + const struct dsigrf_reg *grf_regs; 58 + unsigned long long max_bit_rate_per_lane; 59 + }; 60 + 61 + struct dw_mipi_dsi2_rockchip { 62 + struct device *dev; 63 + struct rockchip_encoder encoder; 64 + struct regmap *regmap; 65 + 66 + unsigned int lane_mbps; /* per lane */ 67 + u32 format; 68 + 69 + struct regmap *grf_regmap; 70 + struct phy *phy; 71 + union phy_configure_opts phy_opts; 72 + 73 + struct dw_mipi_dsi2 *dmd; 74 + struct dw_mipi_dsi2_plat_data pdata; 75 + const struct rockchip_dw_dsi2_chip_data *cdata; 76 + }; 77 + 78 + static inline struct dw_mipi_dsi2_rockchip *to_dsi2(struct drm_encoder *encoder) 79 + { 80 + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); 81 + 82 + return container_of(rkencoder, struct dw_mipi_dsi2_rockchip, encoder); 83 + } 84 + 85 + static void grf_field_write(struct dw_mipi_dsi2_rockchip *dsi2, enum grf_reg_fields index, 86 + unsigned int val) 87 + { 88 + const struct dsigrf_reg *field = &dsi2->cdata->grf_regs[index]; 89 + 90 + if (!field) 91 + return; 92 + 93 + regmap_write(dsi2->grf_regmap, field->offset, 94 + (val << field->lsb) | (GENMASK(field->msb, field->lsb) << 16)); 95 + } 96 + 97 + static int dw_mipi_dsi2_phy_init(void *priv_data) 98 + { 99 + return 0; 100 + } 101 + 102 + static void dw_mipi_dsi2_phy_power_on(void *priv_data) 103 + { 104 + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; 105 + int ret; 106 + 107 + ret = phy_set_mode(dsi2->phy, PHY_MODE_MIPI_DPHY); 108 + if (ret) { 109 + dev_err(dsi2->dev, "Failed to set phy mode: %d\n", ret); 110 + return; 111 + } 112 + 113 + phy_configure(dsi2->phy, &dsi2->phy_opts); 114 + phy_power_on(dsi2->phy); 115 + } 116 + 117 + static void dw_mipi_dsi2_phy_power_off(void *priv_data) 118 + { 119 + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; 120 + 121 + phy_power_off(dsi2->phy); 122 + } 123 + 124 + static int 125 + dw_mipi_dsi2_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, 126 + unsigned long mode_flags, u32 lanes, u32 format, 127 + unsigned int *lane_mbps) 128 + { 129 + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; 130 + u64 max_lane_rate, target_phyclk; 131 + unsigned int lane_rate_kbps; 132 + int bpp; 133 + 134 + max_lane_rate = dsi2->cdata->max_bit_rate_per_lane; 135 + 136 + dsi2->format = format; 137 + bpp = mipi_dsi_pixel_format_to_bpp(format); 138 + if (bpp < 0) { 139 + dev_err(dsi2->dev, "failed to get bpp for pixel format %d\n", format); 140 + return bpp; 141 + } 142 + 143 + lane_rate_kbps = mode->clock * bpp / lanes; 144 + 145 + /* 146 + * Set BW a little larger only in video burst mode in 147 + * consideration of the protocol overhead and HS mode 148 + * switching to BLLP mode, take 1 / 0.9, since Mbps must 149 + * big than bandwidth of RGB 150 + */ 151 + if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 152 + lane_rate_kbps = (lane_rate_kbps * 10) / 9; 153 + 154 + if (lane_rate_kbps > max_lane_rate) { 155 + dev_err(dsi2->dev, "DPHY clock frequency is out of range\n"); 156 + return -ERANGE; 157 + } 158 + 159 + dsi2->lane_mbps = lane_rate_kbps / 1000; 160 + *lane_mbps = dsi2->lane_mbps; 161 + 162 + if (dsi2->phy) { 163 + target_phyclk = DIV_ROUND_CLOSEST_ULL(lane_rate_kbps * lanes * 1000, bpp); 164 + phy_mipi_dphy_get_default_config(target_phyclk, bpp, lanes, 165 + &dsi2->phy_opts.mipi_dphy); 166 + } 167 + 168 + return 0; 169 + } 170 + 171 + static void dw_mipi_dsi2_phy_get_iface(void *priv_data, struct dw_mipi_dsi2_phy_iface *iface) 172 + { 173 + /* PPI width is fixed to 16 bits in DCPHY */ 174 + iface->ppi_width = 16; 175 + iface->phy_type = DW_MIPI_DSI2_DPHY; 176 + } 177 + 178 + static int 179 + dw_mipi_dsi2_phy_get_timing(void *priv_data, unsigned int lane_mbps, 180 + struct dw_mipi_dsi2_phy_timing *timing) 181 + { 182 + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; 183 + struct phy_configure_opts_mipi_dphy *cfg = &dsi2->phy_opts.mipi_dphy; 184 + unsigned long long tmp, ui; 185 + unsigned long long hstx_clk; 186 + 187 + hstx_clk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16); 188 + 189 + ui = ALIGN(PSEC_PER_SEC, hstx_clk); 190 + do_div(ui, hstx_clk); 191 + 192 + /* PHY_LP2HS_TIME = (TLPX + THS-PREPARE + THS-ZERO) / Tphy_hstx_clk */ 193 + tmp = cfg->lpx + cfg->hs_prepare + cfg->hs_zero; 194 + tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui); 195 + timing->data_lp2hs = tmp; 196 + 197 + /* PHY_HS2LP_TIME = (THS-TRAIL + THS-EXIT) / Tphy_hstx_clk */ 198 + tmp = cfg->hs_trail + cfg->hs_exit; 199 + tmp = DIV_ROUND_CLOSEST_ULL(tmp << 16, ui); 200 + timing->data_hs2lp = tmp; 201 + 202 + return 0; 203 + } 204 + 205 + static const struct dw_mipi_dsi2_phy_ops dw_mipi_dsi2_rockchip_phy_ops = { 206 + .init = dw_mipi_dsi2_phy_init, 207 + .power_on = dw_mipi_dsi2_phy_power_on, 208 + .power_off = dw_mipi_dsi2_phy_power_off, 209 + .get_interface = dw_mipi_dsi2_phy_get_iface, 210 + .get_lane_mbps = dw_mipi_dsi2_get_lane_mbps, 211 + .get_timing = dw_mipi_dsi2_phy_get_timing, 212 + }; 213 + 214 + static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder, 215 + struct drm_atomic_state *state) 216 + { 217 + struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder); 218 + u32 color_depth; 219 + 220 + switch (dsi2->format) { 221 + case MIPI_DSI_FMT_RGB666: 222 + case MIPI_DSI_FMT_RGB666_PACKED: 223 + color_depth = IPI_DEPTH_6_BITS; 224 + break; 225 + case MIPI_DSI_FMT_RGB565: 226 + color_depth = IPI_DEPTH_5_6_5_BITS; 227 + break; 228 + case MIPI_DSI_FMT_RGB888: 229 + color_depth = IPI_DEPTH_8_BITS; 230 + break; 231 + default: 232 + /* Should've been caught by atomic_check */ 233 + WARN_ON(1); 234 + return; 235 + } 236 + 237 + grf_field_write(dsi2, IPI_COLOR_DEPTH, color_depth); 238 + } 239 + 240 + static int 241 + dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder, 242 + struct drm_crtc_state *crtc_state, 243 + struct drm_connector_state *conn_state) 244 + { 245 + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); 246 + struct dw_mipi_dsi2_rockchip *dsi2 = to_dsi2(encoder); 247 + struct drm_connector *connector = conn_state->connector; 248 + struct drm_display_info *info = &connector->display_info; 249 + 250 + switch (dsi2->format) { 251 + case MIPI_DSI_FMT_RGB666: 252 + case MIPI_DSI_FMT_RGB666_PACKED: 253 + s->output_mode = ROCKCHIP_OUT_MODE_P666; 254 + break; 255 + case MIPI_DSI_FMT_RGB565: 256 + s->output_mode = ROCKCHIP_OUT_MODE_P565; 257 + break; 258 + case MIPI_DSI_FMT_RGB888: 259 + s->output_mode = ROCKCHIP_OUT_MODE_P888; 260 + break; 261 + default: 262 + WARN_ON(1); 263 + return -EINVAL; 264 + } 265 + 266 + if (info->num_bus_formats) 267 + s->bus_format = info->bus_formats[0]; 268 + else 269 + s->bus_format = MEDIA_BUS_FMT_RGB888_1X24; 270 + 271 + s->output_type = DRM_MODE_CONNECTOR_DSI; 272 + s->bus_flags = info->bus_flags; 273 + s->color_space = V4L2_COLORSPACE_DEFAULT; 274 + 275 + return 0; 276 + } 277 + 278 + static const struct drm_encoder_helper_funcs 279 + dw_mipi_dsi2_encoder_helper_funcs = { 280 + .atomic_enable = dw_mipi_dsi2_encoder_atomic_enable, 281 + .atomic_check = dw_mipi_dsi2_encoder_atomic_check, 282 + }; 283 + 284 + static int rockchip_dsi2_drm_create_encoder(struct dw_mipi_dsi2_rockchip *dsi2, 285 + struct drm_device *drm_dev) 286 + { 287 + struct drm_encoder *encoder = &dsi2->encoder.encoder; 288 + int ret; 289 + 290 + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, 291 + dsi2->dev->of_node); 292 + 293 + ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI); 294 + if (ret) { 295 + dev_err(dsi2->dev, "Failed to initialize encoder with drm\n"); 296 + return ret; 297 + } 298 + 299 + drm_encoder_helper_add(encoder, &dw_mipi_dsi2_encoder_helper_funcs); 300 + 301 + return 0; 302 + } 303 + 304 + static int dw_mipi_dsi2_rockchip_bind(struct device *dev, struct device *master, 305 + void *data) 306 + { 307 + struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev); 308 + struct drm_device *drm_dev = data; 309 + int ret; 310 + 311 + ret = rockchip_dsi2_drm_create_encoder(dsi2, drm_dev); 312 + if (ret) 313 + return dev_err_probe(dev, ret, "Failed to create drm encoder\n"); 314 + 315 + rockchip_drm_encoder_set_crtc_endpoint_id(&dsi2->encoder, 316 + dev->of_node, 0, 0); 317 + 318 + ret = dw_mipi_dsi2_bind(dsi2->dmd, &dsi2->encoder.encoder); 319 + if (ret) 320 + return dev_err_probe(dev, ret, "Failed to bind\n"); 321 + 322 + return 0; 323 + } 324 + 325 + static void dw_mipi_dsi2_rockchip_unbind(struct device *dev, struct device *master, 326 + void *data) 327 + { 328 + struct dw_mipi_dsi2_rockchip *dsi2 = dev_get_drvdata(dev); 329 + 330 + dw_mipi_dsi2_unbind(dsi2->dmd); 331 + } 332 + 333 + static const struct component_ops dw_mipi_dsi2_rockchip_ops = { 334 + .bind = dw_mipi_dsi2_rockchip_bind, 335 + .unbind = dw_mipi_dsi2_rockchip_unbind, 336 + }; 337 + 338 + static int dw_mipi_dsi2_rockchip_host_attach(void *priv_data, 339 + struct mipi_dsi_device *device) 340 + { 341 + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; 342 + int ret; 343 + 344 + ret = component_add(dsi2->dev, &dw_mipi_dsi2_rockchip_ops); 345 + if (ret) 346 + return dev_err_probe(dsi2->dev, ret, "Failed to register component\n"); 347 + 348 + return 0; 349 + } 350 + 351 + static int dw_mipi_dsi2_rockchip_host_detach(void *priv_data, 352 + struct mipi_dsi_device *device) 353 + { 354 + struct dw_mipi_dsi2_rockchip *dsi2 = priv_data; 355 + 356 + component_del(dsi2->dev, &dw_mipi_dsi2_rockchip_ops); 357 + 358 + return 0; 359 + } 360 + 361 + static const struct dw_mipi_dsi2_host_ops dw_mipi_dsi2_rockchip_host_ops = { 362 + .attach = dw_mipi_dsi2_rockchip_host_attach, 363 + .detach = dw_mipi_dsi2_rockchip_host_detach, 364 + }; 365 + 366 + static const struct regmap_config dw_mipi_dsi2_rockchip_regmap_config = { 367 + .name = "dsi2-host", 368 + .reg_bits = 32, 369 + .val_bits = 32, 370 + .reg_stride = 4, 371 + .fast_io = true, 372 + }; 373 + 374 + static int dw_mipi_dsi2_rockchip_probe(struct platform_device *pdev) 375 + { 376 + struct device *dev = &pdev->dev; 377 + struct device_node *np = dev->of_node; 378 + const struct rockchip_dw_dsi2_chip_data *cdata = 379 + of_device_get_match_data(dev); 380 + struct dw_mipi_dsi2_rockchip *dsi2; 381 + struct resource *res; 382 + void __iomem *base; 383 + int i; 384 + 385 + dsi2 = devm_kzalloc(dev, sizeof(*dsi2), GFP_KERNEL); 386 + if (!dsi2) 387 + return -ENOMEM; 388 + 389 + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 390 + if (IS_ERR(base)) 391 + return dev_err_probe(dev, PTR_ERR(base), "Unable to get dsi registers\n"); 392 + 393 + dsi2->regmap = devm_regmap_init_mmio(dev, base, &dw_mipi_dsi2_rockchip_regmap_config); 394 + if (IS_ERR(dsi2->regmap)) 395 + return dev_err_probe(dev, PTR_ERR(dsi2->regmap), "failed to init register map\n"); 396 + 397 + i = 0; 398 + while (cdata[i].reg) { 399 + if (cdata[i].reg == res->start) { 400 + dsi2->cdata = &cdata[i]; 401 + break; 402 + } 403 + 404 + i++; 405 + } 406 + 407 + if (!dsi2->cdata) 408 + return dev_err_probe(dev, -EINVAL, "No dsi-config for %s node\n", np->name); 409 + 410 + dsi2->grf_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); 411 + if (IS_ERR(dsi2->grf_regmap)) 412 + return dev_err_probe(dsi2->dev, PTR_ERR(dsi2->grf_regmap), "Unable to get grf\n"); 413 + 414 + dsi2->phy = devm_phy_optional_get(dev, "dcphy"); 415 + if (IS_ERR(dsi2->phy)) 416 + return dev_err_probe(dev, PTR_ERR(dsi2->phy), "failed to get mipi phy\n"); 417 + 418 + dsi2->dev = dev; 419 + dsi2->pdata.regmap = dsi2->regmap; 420 + dsi2->pdata.max_data_lanes = 4; 421 + dsi2->pdata.phy_ops = &dw_mipi_dsi2_rockchip_phy_ops; 422 + dsi2->pdata.host_ops = &dw_mipi_dsi2_rockchip_host_ops; 423 + dsi2->pdata.priv_data = dsi2; 424 + platform_set_drvdata(pdev, dsi2); 425 + 426 + dsi2->dmd = dw_mipi_dsi2_probe(pdev, &dsi2->pdata); 427 + if (IS_ERR(dsi2->dmd)) 428 + return dev_err_probe(dev, PTR_ERR(dsi2->dmd), "Failed to probe dw_mipi_dsi2\n"); 429 + 430 + return 0; 431 + } 432 + 433 + static void dw_mipi_dsi2_rockchip_remove(struct platform_device *pdev) 434 + { 435 + struct dw_mipi_dsi2_rockchip *dsi2 = platform_get_drvdata(pdev); 436 + 437 + dw_mipi_dsi2_remove(dsi2->dmd); 438 + } 439 + 440 + static const struct dsigrf_reg rk3588_dsi0_grf_reg_fields[MAX_FIELDS] = { 441 + [TXREQCLKHS_EN] = { 0x0000, 11, 11 }, 442 + [GATING_EN] = { 0x0000, 10, 10 }, 443 + [IPI_SHUTDN] = { 0x0000, 9, 9 }, 444 + [IPI_COLORM] = { 0x0000, 8, 8 }, 445 + [IPI_COLOR_DEPTH] = { 0x0000, 4, 7 }, 446 + [IPI_FORMAT] = { 0x0000, 0, 3 }, 447 + }; 448 + 449 + static const struct dsigrf_reg rk3588_dsi1_grf_reg_fields[MAX_FIELDS] = { 450 + [TXREQCLKHS_EN] = { 0x0004, 11, 11 }, 451 + [GATING_EN] = { 0x0004, 10, 10 }, 452 + [IPI_SHUTDN] = { 0x0004, 9, 9 }, 453 + [IPI_COLORM] = { 0x0004, 8, 8 }, 454 + [IPI_COLOR_DEPTH] = { 0x0004, 4, 7 }, 455 + [IPI_FORMAT] = { 0x0004, 0, 3 }, 456 + }; 457 + 458 + static const struct rockchip_dw_dsi2_chip_data rk3588_chip_data[] = { 459 + { 460 + .reg = 0xfde20000, 461 + .grf_regs = rk3588_dsi0_grf_reg_fields, 462 + .max_bit_rate_per_lane = 4500000ULL, 463 + }, 464 + { 465 + .reg = 0xfde30000, 466 + .grf_regs = rk3588_dsi1_grf_reg_fields, 467 + .max_bit_rate_per_lane = 4500000ULL, 468 + } 469 + }; 470 + 471 + static const struct of_device_id dw_mipi_dsi2_rockchip_dt_ids[] = { 472 + { 473 + .compatible = "rockchip,rk3588-mipi-dsi2", 474 + .data = &rk3588_chip_data, 475 + }, 476 + {} 477 + }; 478 + MODULE_DEVICE_TABLE(of, dw_mipi_dsi2_rockchip_dt_ids); 479 + 480 + struct platform_driver dw_mipi_dsi2_rockchip_driver = { 481 + .probe = dw_mipi_dsi2_rockchip_probe, 482 + .remove = dw_mipi_dsi2_rockchip_remove, 483 + .driver = { 484 + .of_match_table = dw_mipi_dsi2_rockchip_dt_ids, 485 + .name = "dw-mipi-dsi2", 486 + }, 487 + };
+1 -1
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 - * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd 3 + * Copyright (c) 2014, Rockchip Electronics Co., Ltd. 4 4 */ 5 5 6 6 #include <linux/clk.h>
+110 -37
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
··· 28 28 #define RK3588_GRF_SOC_CON2 0x0308 29 29 #define RK3588_HDMI0_HPD_INT_MSK BIT(13) 30 30 #define RK3588_HDMI0_HPD_INT_CLR BIT(12) 31 + #define RK3588_HDMI1_HPD_INT_MSK BIT(15) 32 + #define RK3588_HDMI1_HPD_INT_CLR BIT(14) 31 33 #define RK3588_GRF_SOC_CON7 0x031c 32 34 #define RK3588_SET_HPD_PATH_MASK GENMASK(13, 12) 33 35 #define RK3588_GRF_SOC_STATUS1 0x0384 34 36 #define RK3588_HDMI0_LEVEL_INT BIT(16) 37 + #define RK3588_HDMI1_LEVEL_INT BIT(24) 35 38 #define RK3588_GRF_VO1_CON3 0x000c 39 + #define RK3588_GRF_VO1_CON6 0x0018 36 40 #define RK3588_SCLIN_MASK BIT(9) 37 41 #define RK3588_SDAIN_MASK BIT(10) 38 42 #define RK3588_MODE_MASK BIT(11) 39 43 #define RK3588_I2S_SEL_MASK BIT(13) 40 44 #define RK3588_GRF_VO1_CON9 0x0024 41 45 #define RK3588_HDMI0_GRANT_SEL BIT(10) 46 + #define RK3588_HDMI1_GRANT_SEL BIT(12) 42 47 43 48 #define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16) 44 49 #define HOTPLUG_DEBOUNCE_MS 150 50 + #define MAX_HDMI_PORT_NUM 2 45 51 46 52 struct rockchip_hdmi_qp { 47 53 struct device *dev; ··· 59 53 struct phy *phy; 60 54 struct gpio_desc *enable_gpio; 61 55 struct delayed_work hpd_work; 56 + int port_id; 62 57 }; 63 58 64 59 static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder) ··· 134 127 u32 val; 135 128 136 129 regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val); 130 + val &= hdmi->port_id ? RK3588_HDMI1_LEVEL_INT : RK3588_HDMI0_LEVEL_INT; 137 131 138 - return val & RK3588_HDMI0_LEVEL_INT ? 139 - connector_status_connected : connector_status_disconnected; 132 + return val ? connector_status_connected : connector_status_disconnected; 140 133 } 141 134 142 135 static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) 143 136 { 144 137 struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; 138 + u32 val; 145 139 146 - regmap_write(hdmi->regmap, 147 - RK3588_GRF_SOC_CON2, 148 - HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, 149 - RK3588_HDMI0_HPD_INT_CLR | 150 - RK3588_HDMI0_HPD_INT_MSK)); 140 + if (hdmi->port_id) 141 + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, 142 + RK3588_HDMI1_HPD_INT_CLR | RK3588_HDMI1_HPD_INT_MSK); 143 + else 144 + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, 145 + RK3588_HDMI0_HPD_INT_CLR | RK3588_HDMI0_HPD_INT_MSK); 146 + 147 + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); 151 148 } 152 149 153 150 static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { ··· 184 173 regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); 185 174 186 175 if (intr_stat) { 187 - val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, 188 - RK3588_HDMI0_HPD_INT_MSK); 176 + if (hdmi->port_id) 177 + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, 178 + RK3588_HDMI1_HPD_INT_MSK); 179 + else 180 + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, 181 + RK3588_HDMI0_HPD_INT_MSK); 189 182 regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); 190 183 return IRQ_WAKE_THREAD; 191 184 } ··· 206 191 if (!intr_stat) 207 192 return IRQ_NONE; 208 193 209 - val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, 210 - RK3588_HDMI0_HPD_INT_CLR); 194 + if (hdmi->port_id) 195 + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR, 196 + RK3588_HDMI1_HPD_INT_CLR); 197 + else 198 + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, 199 + RK3588_HDMI0_HPD_INT_CLR); 211 200 regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); 212 201 213 202 mod_delayed_work(system_wq, &hdmi->hpd_work, 214 203 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); 215 204 216 - val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); 205 + if (hdmi->port_id) 206 + val |= HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK); 207 + else 208 + val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); 217 209 regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); 218 210 219 211 return IRQ_HANDLED; 220 212 } 221 213 214 + struct rockchip_hdmi_qp_cfg { 215 + unsigned int num_ports; 216 + unsigned int port_ids[MAX_HDMI_PORT_NUM]; 217 + const struct dw_hdmi_qp_phy_ops *phy_ops; 218 + }; 219 + 220 + static const struct rockchip_hdmi_qp_cfg rk3588_hdmi_cfg = { 221 + .num_ports = 2, 222 + .port_ids = { 223 + 0xfde80000, 224 + 0xfdea0000, 225 + }, 226 + .phy_ops = &rk3588_hdmi_phy_ops, 227 + }; 228 + 222 229 static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = { 223 230 { .compatible = "rockchip,rk3588-dw-hdmi-qp", 224 - .data = &rk3588_hdmi_phy_ops }, 231 + .data = &rk3588_hdmi_cfg }, 225 232 {}, 226 233 }; 227 234 MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids); ··· 251 214 static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, 252 215 void *data) 253 216 { 254 - static const char * const clk_names[] = { 255 - "pclk", "earc", "aud", "hdp", "hclk_vo1", 256 - "ref" /* keep "ref" last */ 257 - }; 258 217 struct platform_device *pdev = to_platform_device(dev); 218 + const struct rockchip_hdmi_qp_cfg *cfg; 259 219 struct dw_hdmi_qp_plat_data plat_data; 260 220 struct drm_device *drm = data; 261 221 struct drm_connector *connector; 262 222 struct drm_encoder *encoder; 263 223 struct rockchip_hdmi_qp *hdmi; 264 - struct clk *clk; 224 + struct resource *res; 225 + struct clk_bulk_data *clks; 265 226 int ret, irq, i; 266 227 u32 val; 267 228 ··· 270 235 if (!hdmi) 271 236 return -ENOMEM; 272 237 273 - plat_data.phy_ops = of_device_get_match_data(dev); 274 - if (!plat_data.phy_ops) 238 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 239 + if (!res) 275 240 return -ENODEV; 276 241 277 - plat_data.phy_data = hdmi; 242 + cfg = of_device_get_match_data(dev); 243 + if (!cfg) 244 + return -ENODEV; 245 + 278 246 hdmi->dev = &pdev->dev; 247 + hdmi->port_id = -ENODEV; 248 + 249 + /* Identify port ID by matching base IO address */ 250 + for (i = 0; i < cfg->num_ports; i++) { 251 + if (res->start == cfg->port_ids[i]) { 252 + hdmi->port_id = i; 253 + break; 254 + } 255 + } 256 + if (hdmi->port_id < 0) { 257 + drm_err(hdmi, "Failed to match HDMI port ID\n"); 258 + return hdmi->port_id; 259 + } 260 + 261 + plat_data.phy_ops = cfg->phy_ops; 262 + plat_data.phy_data = hdmi; 279 263 280 264 encoder = &hdmi->encoder.encoder; 281 265 encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); ··· 324 270 return PTR_ERR(hdmi->vo_regmap); 325 271 } 326 272 327 - for (i = 0; i < ARRAY_SIZE(clk_names); i++) { 328 - clk = devm_clk_get_enabled(hdmi->dev, clk_names[i]); 273 + ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks); 274 + if (ret < 0) { 275 + drm_err(hdmi, "Failed to get clocks: %d\n", ret); 276 + return ret; 277 + } 329 278 330 - if (IS_ERR(clk)) { 331 - ret = PTR_ERR(clk); 332 - if (ret != -EPROBE_DEFER) 333 - drm_err(hdmi, "Failed to get %s clock: %d\n", 334 - clk_names[i], ret); 335 - return ret; 279 + for (i = 0; i < ret; i++) { 280 + if (!strcmp(clks[i].id, "ref")) { 281 + hdmi->ref_clk = clks[1].clk; 282 + break; 336 283 } 337 284 } 338 - hdmi->ref_clk = clk; 285 + if (!hdmi->ref_clk) { 286 + drm_err(hdmi, "Missing ref clock\n"); 287 + return -EINVAL; 288 + } 339 289 340 290 hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", 341 291 GPIOD_OUT_HIGH); ··· 361 303 HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | 362 304 HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | 363 305 HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); 364 - regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val); 306 + regmap_write(hdmi->vo_regmap, 307 + hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3, 308 + val); 365 309 366 310 val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, 367 311 RK3588_SET_HPD_PATH_MASK); 368 312 regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); 369 313 370 - val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, 371 - RK3588_HDMI0_GRANT_SEL); 314 + if (hdmi->port_id) 315 + val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, 316 + RK3588_HDMI1_GRANT_SEL); 317 + else 318 + val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, 319 + RK3588_HDMI0_GRANT_SEL); 372 320 regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val); 373 321 374 - val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); 322 + if (hdmi->port_id) 323 + val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK); 324 + else 325 + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); 375 326 regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); 376 327 377 328 INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work); ··· 458 391 HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | 459 392 HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | 460 393 HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); 461 - regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val); 394 + regmap_write(hdmi->vo_regmap, 395 + hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3, 396 + val); 462 397 463 398 val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, 464 399 RK3588_SET_HPD_PATH_MASK); 465 400 regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); 466 401 467 - val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, 468 - RK3588_HDMI0_GRANT_SEL); 402 + if (hdmi->port_id) 403 + val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL, 404 + RK3588_HDMI1_GRANT_SEL); 405 + else 406 + val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, 407 + RK3588_HDMI0_GRANT_SEL); 469 408 regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val); 470 409 471 410 dw_hdmi_qp_resume(dev, hdmi->hdmi);
+1 -1
drivers/gpu/drm/rockchip/inno_hdmi.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Zheng Yang <zhengyang@rock-chips.com> 5 5 * Yakir Yang <ykk@rock-chips.com> 6 6 */
+1 -1
drivers/gpu/drm/rockchip/inno_hdmi.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Zheng Yang <zhengyang@rock-chips.com> 5 5 * Yakir Yang <ykk@rock-chips.com> 6 6 */
+1 -1
drivers/gpu/drm/rockchip/rk3066_hdmi.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Zheng Yang <zhengyang@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/rk3066_hdmi.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Zheng Yang <zhengyang@rock-chips.com> 5 5 */ 6 6
+4 -3
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 * 6 6 * based on exynos_drm_drv.c ··· 486 486 { 487 487 struct drm_device *drm = platform_get_drvdata(pdev); 488 488 489 - if (drm) 490 - drm_atomic_helper_shutdown(drm); 489 + drm_atomic_helper_shutdown(drm); 491 490 } 492 491 493 492 static const struct of_device_id rockchip_drm_dt_ids[] = { ··· 533 534 CONFIG_ROCKCHIP_DW_HDMI_QP); 534 535 ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, 535 536 CONFIG_ROCKCHIP_DW_MIPI_DSI); 537 + ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi2_rockchip_driver, 538 + CONFIG_ROCKCHIP_DW_MIPI_DSI2); 536 539 ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); 537 540 ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver, 538 541 CONFIG_ROCKCHIP_RK3066_HDMI);
+2 -1
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 * 6 6 * based on exynos_drm_drv.h ··· 90 90 extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; 91 91 extern struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver; 92 92 extern struct platform_driver dw_mipi_dsi_rockchip_driver; 93 + extern struct platform_driver dw_mipi_dsi2_rockchip_driver; 93 94 extern struct platform_driver inno_hdmi_driver; 94 95 extern struct platform_driver rockchip_dp_driver; 95 96 extern struct platform_driver rockchip_lvds_driver;
+1 -1
drivers/gpu/drm/rockchip/rockchip_drm_fb.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/rockchip_drm_fb.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/rockchip_drm_gem.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/rockchip_drm_gem.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/rockchip_drm_vop.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+352 -17
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
··· 24 24 #include <drm/drm_atomic_uapi.h> 25 25 #include <drm/drm_blend.h> 26 26 #include <drm/drm_crtc.h> 27 + #include <linux/debugfs.h> 27 28 #include <drm/drm_debugfs.h> 28 29 #include <drm/drm_flip_work.h> 29 30 #include <drm/drm_framebuffer.h> 31 + #include <drm/drm_gem_framebuffer_helper.h> 30 32 #include <drm/drm_probe_helper.h> 31 33 #include <drm/drm_vblank.h> 32 34 33 35 #include <uapi/linux/videodev2.h> 34 36 #include <dt-bindings/soc/rockchip,vop2.h> 35 37 36 - #include "rockchip_drm_drv.h" 37 38 #include "rockchip_drm_gem.h" 38 39 #include "rockchip_drm_vop2.h" 39 40 #include "rockchip_rgb.h" ··· 187 186 */ 188 187 u32 registered_num_wins; 189 188 189 + struct resource *res; 190 190 void __iomem *regs; 191 191 struct regmap *map; 192 192 ··· 238 236 (x) == ROCKCHIP_VOP2_EP_LVDS1) 239 237 240 238 #define vop2_output_if_is_dpi(x) ((x) == ROCKCHIP_VOP2_EP_RGB0) 239 + 240 + /* 241 + * bus-format types. 242 + */ 243 + struct drm_bus_format_enum_list { 244 + int type; 245 + const char *name; 246 + }; 247 + 248 + static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = { 249 + { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, 250 + { MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" }, 251 + { MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" }, 252 + { MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" }, 253 + { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" }, 254 + { MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" }, 255 + { MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" }, 256 + { MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" }, 257 + { MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" }, 258 + { MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" }, 259 + { MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" }, 260 + { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" }, 261 + { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" }, 262 + { MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" }, 263 + { MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" }, 264 + { MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" }, 265 + { MEDIA_BUS_FMT_RGB101010_1X30, "RGB101010_1X30" }, 266 + { MEDIA_BUS_FMT_YUYV10_1X20, "YUYV10_1X20" }, 267 + }; 268 + 269 + static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list) 241 270 242 271 static const struct regmap_config vop2_regmap_config; 243 272 ··· 591 558 592 559 if (modifier == DRM_FORMAT_MOD_INVALID) 593 560 return false; 561 + 562 + if (vop2->data->soc_id == 3568 || vop2->data->soc_id == 3566) { 563 + if (vop2_cluster_window(win)) { 564 + if (modifier == DRM_FORMAT_MOD_LINEAR) { 565 + drm_dbg_kms(vop2->drm, 566 + "Cluster window only supports format with afbc\n"); 567 + return false; 568 + } 569 + } 570 + } 571 + 572 + if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_XBGR2101010) { 573 + if (vop2->data->soc_id == 3588) { 574 + if (!rockchip_afbc(plane, modifier)) { 575 + drm_dbg_kms(vop2->drm, "Only support 32 bpp format with afbc\n"); 576 + return false; 577 + } 578 + } 579 + } 594 580 595 581 if (modifier == DRM_FORMAT_MOD_LINEAR) 596 582 return true; ··· 1444 1392 &fb->format->format, 1445 1393 afbc_en ? "AFBC" : "", &yrgb_mst); 1446 1394 1395 + if (vop2->data->soc_id > 3568) { 1396 + vop2_win_write(win, VOP2_WIN_AXI_BUS_ID, win->data->axi_bus_id); 1397 + vop2_win_write(win, VOP2_WIN_AXI_YRGB_R_ID, win->data->axi_yrgb_r_id); 1398 + vop2_win_write(win, VOP2_WIN_AXI_UV_R_ID, win->data->axi_uv_r_id); 1399 + } 1400 + 1447 1401 if (vop2_cluster_window(win)) 1448 1402 vop2_win_write(win, VOP2_WIN_AFBC_HALF_BLOCK_EN, half_block_en); 1449 1403 ··· 1659 1601 struct drm_crtc *crtc, 1660 1602 struct drm_crtc_state *crtc_state) 1661 1603 { 1662 - if (!vop2->lut_regs || !crtc_state->color_mgmt_changed) 1604 + if (!vop2->lut_regs) 1663 1605 return; 1664 1606 1665 1607 if (!crtc_state->gamma_lut) { ··· 2394 2336 2395 2337 static void vop2_setup_cluster_alpha(struct vop2 *vop2, struct vop2_win *main_win) 2396 2338 { 2397 - u32 offset = (main_win->data->phys_id * 0x10); 2398 2339 struct vop2_alpha_config alpha_config; 2399 2340 struct vop2_alpha alpha; 2400 2341 struct drm_plane_state *bottom_win_pstate; ··· 2401 2344 u16 src_glb_alpha_val, dst_glb_alpha_val; 2402 2345 bool premulti_en = false; 2403 2346 bool swap = false; 2347 + u32 offset = 0; 2404 2348 2405 2349 /* At one win mode, win0 is dst/bottom win, and win1 is a all zero src/top win */ 2406 2350 bottom_win_pstate = main_win->base.state; ··· 2420 2362 vop2_parse_alpha(&alpha_config, &alpha); 2421 2363 2422 2364 alpha.src_color_ctrl.bits.src_dst_swap = swap; 2365 + 2366 + switch (main_win->data->phys_id) { 2367 + case ROCKCHIP_VOP2_CLUSTER0: 2368 + offset = 0x0; 2369 + break; 2370 + case ROCKCHIP_VOP2_CLUSTER1: 2371 + offset = 0x10; 2372 + break; 2373 + case ROCKCHIP_VOP2_CLUSTER2: 2374 + offset = 0x20; 2375 + break; 2376 + case ROCKCHIP_VOP2_CLUSTER3: 2377 + offset = 0x30; 2378 + break; 2379 + } 2380 + 2423 2381 vop2_writel(vop2, RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL + offset, 2424 2382 alpha.src_color_ctrl.val); 2425 2383 vop2_writel(vop2, RK3568_CLUSTER0_MIX_DST_COLOR_CTRL + offset, ··· 2482 2408 drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { 2483 2409 struct vop2_win *win = to_vop2_win(plane); 2484 2410 int zpos = plane->state->normalized_zpos; 2411 + 2412 + /* 2413 + * Need to configure alpha from second layer. 2414 + */ 2415 + if (zpos == 0) 2416 + continue; 2485 2417 2486 2418 if (plane->state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) 2487 2419 premulti_en = 1; ··· 2565 2485 struct drm_plane *plane; 2566 2486 u32 layer_sel = 0; 2567 2487 u32 port_sel; 2568 - unsigned int nlayer, ofs; 2488 + u8 layer_id; 2489 + u8 old_layer_id; 2490 + u8 layer_sel_id; 2491 + unsigned int ofs; 2569 2492 u32 ovl_ctrl; 2570 2493 int i; 2571 2494 struct vop2_video_port *vp0 = &vop2->vps[0]; ··· 2612 2529 for (i = 0; i < vp->id; i++) 2613 2530 ofs += vop2->vps[i].nlayers; 2614 2531 2615 - nlayer = 0; 2616 2532 drm_atomic_crtc_for_each_plane(plane, &vp->crtc) { 2617 2533 struct vop2_win *win = to_vop2_win(plane); 2534 + struct vop2_win *old_win; 2535 + 2536 + layer_id = (u8)(plane->state->normalized_zpos + ofs); 2537 + 2538 + /* 2539 + * Find the layer this win bind in old state. 2540 + */ 2541 + for (old_layer_id = 0; old_layer_id < vop2->data->win_size; old_layer_id++) { 2542 + layer_sel_id = (layer_sel >> (4 * old_layer_id)) & 0xf; 2543 + if (layer_sel_id == win->data->layer_sel_id) 2544 + break; 2545 + } 2546 + 2547 + /* 2548 + * Find the win bind to this layer in old state 2549 + */ 2550 + for (i = 0; i < vop2->data->win_size; i++) { 2551 + old_win = &vop2->win[i]; 2552 + layer_sel_id = (layer_sel >> (4 * layer_id)) & 0xf; 2553 + if (layer_sel_id == old_win->data->layer_sel_id) 2554 + break; 2555 + } 2618 2556 2619 2557 switch (win->data->phys_id) { 2620 2558 case ROCKCHIP_VOP2_CLUSTER0: ··· 2680 2576 break; 2681 2577 } 2682 2578 2683 - layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs, 2684 - 0x7); 2685 - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(plane->state->normalized_zpos + ofs, 2686 - win->data->layer_sel_id); 2687 - nlayer++; 2688 - } 2689 - 2690 - /* configure unused layers to 0x5 (reserved) */ 2691 - for (; nlayer < vp->nlayers; nlayer++) { 2692 - layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 0x7); 2693 - layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(nlayer + ofs, 5); 2579 + layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(layer_id, 0x7); 2580 + layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(layer_id, win->data->layer_sel_id); 2581 + /* 2582 + * When we bind a window from layerM to layerN, we also need to move the old 2583 + * window on layerN to layerM to avoid one window selected by two or more layers. 2584 + */ 2585 + layer_sel &= ~RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, 0x7); 2586 + layer_sel |= RK3568_OVL_LAYER_SEL__LAYER(old_layer_id, old_win->data->layer_sel_id); 2694 2587 } 2695 2588 2696 2589 vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel); ··· 2722 2621 sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__ESMART1, dly); 2723 2622 break; 2724 2623 case ROCKCHIP_VOP2_SMART0: 2624 + case ROCKCHIP_VOP2_ESMART2: 2725 2625 sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART0, dly); 2726 2626 break; 2727 2627 case ROCKCHIP_VOP2_SMART1: 2628 + case ROCKCHIP_VOP2_ESMART3: 2728 2629 sdly |= FIELD_PREP(RK3568_SMART_DLY_NUM__SMART1, dly); 2729 2630 break; 2730 2631 } ··· 2772 2669 struct vop2 *vop2 = vp->vop2; 2773 2670 2774 2671 /* In case of modeset, gamma lut update already happened in atomic enable */ 2775 - if (!drm_atomic_crtc_needs_modeset(crtc_state)) 2672 + if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed) 2776 2673 vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state); 2777 2674 2778 2675 vop2_post_config(crtc); ··· 2798 2695 .atomic_enable = vop2_crtc_atomic_enable, 2799 2696 .atomic_disable = vop2_crtc_atomic_disable, 2800 2697 }; 2698 + 2699 + static void vop2_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s) 2700 + { 2701 + struct drm_connector_list_iter conn_iter; 2702 + struct drm_connector *connector; 2703 + 2704 + drm_connector_list_iter_begin(crtc->dev, &conn_iter); 2705 + drm_for_each_connector_iter(connector, &conn_iter) { 2706 + if (crtc->state->connector_mask & drm_connector_mask(connector)) 2707 + seq_printf(s, " Connector: %s\n", connector->name); 2708 + } 2709 + drm_connector_list_iter_end(&conn_iter); 2710 + } 2711 + 2712 + static int vop2_plane_state_dump(struct seq_file *s, struct drm_plane *plane) 2713 + { 2714 + struct vop2_win *win = to_vop2_win(plane); 2715 + struct drm_plane_state *pstate = plane->state; 2716 + struct drm_rect *src, *dst; 2717 + struct drm_framebuffer *fb; 2718 + struct drm_gem_object *obj; 2719 + struct rockchip_gem_object *rk_obj; 2720 + bool xmirror; 2721 + bool ymirror; 2722 + bool rotate_270; 2723 + bool rotate_90; 2724 + dma_addr_t fb_addr; 2725 + int i; 2726 + 2727 + seq_printf(s, " %s: %s\n", win->data->name, !pstate ? 2728 + "DISABLED" : pstate->crtc ? "ACTIVE" : "DISABLED"); 2729 + 2730 + if (!pstate || !pstate->fb) 2731 + return 0; 2732 + 2733 + fb = pstate->fb; 2734 + src = &pstate->src; 2735 + dst = &pstate->dst; 2736 + xmirror = pstate->rotation & DRM_MODE_REFLECT_X ? true : false; 2737 + ymirror = pstate->rotation & DRM_MODE_REFLECT_Y ? true : false; 2738 + rotate_270 = pstate->rotation & DRM_MODE_ROTATE_270; 2739 + rotate_90 = pstate->rotation & DRM_MODE_ROTATE_90; 2740 + 2741 + seq_printf(s, "\twin_id: %d\n", win->win_id); 2742 + 2743 + seq_printf(s, "\tformat: %p4cc%s glb_alpha[0x%x]\n", 2744 + &fb->format->format, 2745 + drm_is_afbc(fb->modifier) ? "[AFBC]" : "", 2746 + pstate->alpha >> 8); 2747 + seq_printf(s, "\trotate: xmirror: %d ymirror: %d rotate_90: %d rotate_270: %d\n", 2748 + xmirror, ymirror, rotate_90, rotate_270); 2749 + seq_printf(s, "\tzpos: %d\n", pstate->normalized_zpos); 2750 + seq_printf(s, "\tsrc: pos[%d, %d] rect[%d x %d]\n", src->x1 >> 16, 2751 + src->y1 >> 16, drm_rect_width(src) >> 16, 2752 + drm_rect_height(src) >> 16); 2753 + seq_printf(s, "\tdst: pos[%d, %d] rect[%d x %d]\n", dst->x1, dst->y1, 2754 + drm_rect_width(dst), drm_rect_height(dst)); 2755 + 2756 + for (i = 0; i < fb->format->num_planes; i++) { 2757 + obj = fb->obj[i]; 2758 + rk_obj = to_rockchip_obj(obj); 2759 + fb_addr = rk_obj->dma_addr + fb->offsets[i]; 2760 + 2761 + seq_printf(s, "\tbuf[%d]: addr: %pad pitch: %d offset: %d\n", 2762 + i, &fb_addr, fb->pitches[i], fb->offsets[i]); 2763 + } 2764 + 2765 + return 0; 2766 + } 2767 + 2768 + static int vop2_crtc_state_dump(struct drm_crtc *crtc, struct seq_file *s) 2769 + { 2770 + struct vop2_video_port *vp = to_vop2_video_port(crtc); 2771 + struct drm_crtc_state *cstate = crtc->state; 2772 + struct rockchip_crtc_state *vcstate; 2773 + struct drm_display_mode *mode; 2774 + struct drm_plane *plane; 2775 + bool interlaced; 2776 + 2777 + seq_printf(s, "Video Port%d: %s\n", vp->id, !cstate ? 2778 + "DISABLED" : cstate->active ? "ACTIVE" : "DISABLED"); 2779 + 2780 + if (!cstate || !cstate->active) 2781 + return 0; 2782 + 2783 + mode = &crtc->state->adjusted_mode; 2784 + vcstate = to_rockchip_crtc_state(cstate); 2785 + interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); 2786 + 2787 + vop2_dump_connector_on_crtc(crtc, s); 2788 + seq_printf(s, "\tbus_format[%x]: %s\n", vcstate->bus_format, 2789 + drm_get_bus_format_name(vcstate->bus_format)); 2790 + seq_printf(s, "\toutput_mode[%x]", vcstate->output_mode); 2791 + seq_printf(s, " color_space[%d]\n", vcstate->color_space); 2792 + seq_printf(s, " Display mode: %dx%d%s%d\n", 2793 + mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p", 2794 + drm_mode_vrefresh(mode)); 2795 + seq_printf(s, "\tclk[%d] real_clk[%d] type[%x] flag[%x]\n", 2796 + mode->clock, mode->crtc_clock, mode->type, mode->flags); 2797 + seq_printf(s, "\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start, 2798 + mode->hsync_end, mode->htotal); 2799 + seq_printf(s, "\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start, 2800 + mode->vsync_end, mode->vtotal); 2801 + 2802 + drm_atomic_crtc_for_each_plane(plane, crtc) { 2803 + vop2_plane_state_dump(s, plane); 2804 + } 2805 + 2806 + return 0; 2807 + } 2808 + 2809 + static int vop2_summary_show(struct seq_file *s, void *data) 2810 + { 2811 + struct drm_info_node *node = s->private; 2812 + struct drm_minor *minor = node->minor; 2813 + struct drm_device *drm_dev = minor->dev; 2814 + struct drm_crtc *crtc; 2815 + 2816 + drm_modeset_lock_all(drm_dev); 2817 + drm_for_each_crtc(crtc, drm_dev) { 2818 + vop2_crtc_state_dump(crtc, s); 2819 + } 2820 + drm_modeset_unlock_all(drm_dev); 2821 + 2822 + return 0; 2823 + } 2824 + 2825 + static void vop2_regs_print(struct vop2 *vop2, struct seq_file *s, 2826 + const struct vop2_regs_dump *dump, bool active_only) 2827 + { 2828 + resource_size_t start; 2829 + u32 val; 2830 + int i; 2831 + 2832 + if (dump->en_mask && active_only) { 2833 + val = vop2_readl(vop2, dump->base + dump->en_reg); 2834 + if ((val & dump->en_mask) != dump->en_val) 2835 + return; 2836 + } 2837 + 2838 + seq_printf(s, "\n%s:\n", dump->name); 2839 + 2840 + start = vop2->res->start + dump->base; 2841 + for (i = 0; i < dump->size >> 2; i += 4) { 2842 + seq_printf(s, "%08x: %08x %08x %08x %08x\n", (u32)start + i * 4, 2843 + vop2_readl(vop2, dump->base + (4 * i)), 2844 + vop2_readl(vop2, dump->base + (4 * (i + 1))), 2845 + vop2_readl(vop2, dump->base + (4 * (i + 2))), 2846 + vop2_readl(vop2, dump->base + (4 * (i + 3)))); 2847 + } 2848 + } 2849 + 2850 + static void __vop2_regs_dump(struct seq_file *s, bool active_only) 2851 + { 2852 + struct drm_info_node *node = s->private; 2853 + struct vop2 *vop2 = node->info_ent->data; 2854 + struct drm_minor *minor = node->minor; 2855 + struct drm_device *drm_dev = minor->dev; 2856 + const struct vop2_regs_dump *dump; 2857 + unsigned int i; 2858 + 2859 + drm_modeset_lock_all(drm_dev); 2860 + 2861 + regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register); 2862 + 2863 + if (vop2->enable_count) { 2864 + for (i = 0; i < vop2->data->regs_dump_size; i++) { 2865 + dump = &vop2->data->regs_dump[i]; 2866 + vop2_regs_print(vop2, s, dump, active_only); 2867 + } 2868 + } else { 2869 + seq_puts(s, "VOP disabled\n"); 2870 + } 2871 + drm_modeset_unlock_all(drm_dev); 2872 + } 2873 + 2874 + static int vop2_regs_show(struct seq_file *s, void *arg) 2875 + { 2876 + __vop2_regs_dump(s, false); 2877 + 2878 + return 0; 2879 + } 2880 + 2881 + static int vop2_active_regs_show(struct seq_file *s, void *data) 2882 + { 2883 + __vop2_regs_dump(s, true); 2884 + 2885 + return 0; 2886 + } 2887 + 2888 + static struct drm_info_list vop2_debugfs_list[] = { 2889 + { "summary", vop2_summary_show, 0, NULL }, 2890 + { "active_regs", vop2_active_regs_show, 0, NULL }, 2891 + { "regs", vop2_regs_show, 0, NULL }, 2892 + }; 2893 + 2894 + static void vop2_debugfs_init(struct vop2 *vop2, struct drm_minor *minor) 2895 + { 2896 + struct dentry *root; 2897 + unsigned int i; 2898 + 2899 + root = debugfs_create_dir("vop2", minor->debugfs_root); 2900 + if (!IS_ERR(root)) { 2901 + for (i = 0; i < ARRAY_SIZE(vop2_debugfs_list); i++) 2902 + vop2_debugfs_list[i].data = vop2; 2903 + 2904 + drm_debugfs_create_files(vop2_debugfs_list, 2905 + ARRAY_SIZE(vop2_debugfs_list), 2906 + root, minor); 2907 + } 2908 + } 2909 + 2910 + static int vop2_crtc_late_register(struct drm_crtc *crtc) 2911 + { 2912 + struct vop2_video_port *vp = to_vop2_video_port(crtc); 2913 + struct vop2 *vop2 = vp->vop2; 2914 + 2915 + if (drm_crtc_index(crtc) == 0) 2916 + vop2_debugfs_init(vop2, crtc->dev->primary); 2917 + 2918 + return 0; 2919 + } 2801 2920 2802 2921 static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc) 2803 2922 { ··· 3070 2745 .atomic_destroy_state = vop2_crtc_destroy_state, 3071 2746 .enable_vblank = vop2_crtc_enable_vblank, 3072 2747 .disable_vblank = vop2_crtc_disable_vblank, 2748 + .late_register = vop2_crtc_late_register, 3073 2749 }; 3074 2750 3075 2751 static irqreturn_t vop2_isr(int irq, void *data) ··· 3379 3053 [VOP2_WIN_Y2R_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 8, 8), 3380 3054 [VOP2_WIN_R2Y_EN] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 9, 9), 3381 3055 [VOP2_WIN_CSC_MODE] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL0, 10, 11), 3056 + [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 0, 3), 3057 + [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_CLUSTER_WIN_CTRL2, 5, 8), 3058 + /* RK3588 only, reserved bit on rk3568*/ 3059 + [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3568_CLUSTER_CTRL, 13, 13), 3382 3060 3383 3061 /* Scale */ 3384 3062 [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_CLUSTER_WIN_SCL_FACTOR_YRGB, 0, 15), ··· 3475 3145 [VOP2_WIN_YMIRROR] = REG_FIELD(RK3568_SMART_CTRL1, 31, 31), 3476 3146 [VOP2_WIN_COLOR_KEY] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 0, 29), 3477 3147 [VOP2_WIN_COLOR_KEY_EN] = REG_FIELD(RK3568_SMART_COLOR_KEY_CTRL, 31, 31), 3148 + [VOP2_WIN_AXI_YRGB_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 4, 8), 3149 + [VOP2_WIN_AXI_UV_R_ID] = REG_FIELD(RK3568_SMART_CTRL1, 12, 16), 3150 + /* RK3588 only, reserved register on rk3568 */ 3151 + [VOP2_WIN_AXI_BUS_ID] = REG_FIELD(RK3588_SMART_AXI_CTRL, 1, 1), 3478 3152 3479 3153 /* Scale */ 3480 3154 [VOP2_WIN_SCALE_YRGB_X] = REG_FIELD(RK3568_SMART_REGION0_SCL_FACTOR_YRGB, 0, 15), ··· 3628 3294 return -EINVAL; 3629 3295 } 3630 3296 3297 + vop2->res = res; 3631 3298 vop2->regs = devm_ioremap_resource(dev, res); 3632 3299 if (IS_ERR(vop2->regs)) 3633 3300 return PTR_ERR(vop2->regs);
+22 -1
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6 ··· 9 9 10 10 #include <linux/regmap.h> 11 11 #include <drm/drm_modes.h> 12 + #include "rockchip_drm_drv.h" 12 13 #include "rockchip_drm_vop.h" 13 14 14 15 #define VOP2_VP_FEATURE_OUTPUT_10BIT BIT(0) ··· 79 78 VOP2_WIN_COLOR_KEY, 80 79 VOP2_WIN_COLOR_KEY_EN, 81 80 VOP2_WIN_DITHER_UP, 81 + VOP2_WIN_AXI_BUS_ID, 82 + VOP2_WIN_AXI_YRGB_R_ID, 83 + VOP2_WIN_AXI_UV_R_ID, 82 84 83 85 /* scale regs */ 84 86 VOP2_WIN_SCALE_YRGB_X, ··· 126 122 VOP2_WIN_MAX_REG, 127 123 }; 128 124 125 + struct vop2_regs_dump { 126 + const char *name; 127 + u32 base; 128 + u32 size; 129 + u32 en_reg; 130 + u32 en_val; 131 + u32 en_mask; 132 + }; 133 + 129 134 struct vop2_win_data { 130 135 const char *name; 131 136 unsigned int phys_id; ··· 152 139 */ 153 140 unsigned int layer_sel_id; 154 141 uint64_t feature; 142 + 143 + uint8_t axi_bus_id; 144 + uint8_t axi_yrgb_r_id; 145 + uint8_t axi_uv_r_id; 155 146 156 147 unsigned int max_upscale_factor; 157 148 unsigned int max_downscale_factor; ··· 177 160 u64 feature; 178 161 const struct vop2_win_data *win; 179 162 const struct vop2_video_port_data *vp; 163 + const struct vop2_regs_dump *regs_dump; 180 164 struct vop_rect max_input; 181 165 struct vop_rect max_output; 182 166 183 167 unsigned int win_size; 168 + unsigned int regs_dump_size; 184 169 unsigned int soc_id; 185 170 }; 186 171 ··· 327 308 328 309 #define RK3568_CLUSTER_WIN_CTRL0 0x00 329 310 #define RK3568_CLUSTER_WIN_CTRL1 0x04 311 + #define RK3568_CLUSTER_WIN_CTRL2 0x08 330 312 #define RK3568_CLUSTER_WIN_YRGB_MST 0x10 331 313 #define RK3568_CLUSTER_WIN_CBR_MST 0x14 332 314 #define RK3568_CLUSTER_WIN_VIR 0x18 ··· 350 330 /* (E)smart register definition, offset relative to window base */ 351 331 #define RK3568_SMART_CTRL0 0x00 352 332 #define RK3568_SMART_CTRL1 0x04 333 + #define RK3588_SMART_AXI_CTRL 0x08 353 334 #define RK3568_SMART_REGION0_CTRL 0x10 354 335 #define RK3568_SMART_REGION0_YRGB_MST 0x14 355 336 #define RK3568_SMART_REGION0_CBR_MST 0x18
+1 -1
drivers/gpu/drm/rockchip/rockchip_lvds.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: 5 5 * Mark Yao <mark.yao@rock-chips.com> 6 6 * Sandy Huang <hjc@rock-chips.com>
+1 -1
drivers/gpu/drm/rockchip/rockchip_lvds.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: 5 5 * Sandy Huang <hjc@rock-chips.com> 6 6 * Mark Yao <mark.yao@rock-chips.com>
+1 -1
drivers/gpu/drm/rockchip/rockchip_rgb.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: 5 5 * Sandy Huang <hjc@rock-chips.com> 6 6 */
+1 -1
drivers/gpu/drm/rockchip/rockchip_rgb.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: 5 5 * Sandy Huang <hjc@rock-chips.com> 6 6 */
+217 -2
drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author: Andy Yan <andy.yan@rock-chips.com> 5 5 */ 6 6 ··· 258 258 }, 259 259 }; 260 260 261 + static const struct vop2_regs_dump rk3568_regs_dump[] = { 262 + { 263 + .name = "SYS", 264 + .base = RK3568_REG_CFG_DONE, 265 + .size = 0x100, 266 + .en_reg = 0, 267 + .en_val = 0, 268 + .en_mask = 0 269 + }, { 270 + .name = "OVL", 271 + .base = RK3568_OVL_CTRL, 272 + .size = 0x100, 273 + .en_reg = 0, 274 + .en_val = 0, 275 + .en_mask = 0, 276 + }, { 277 + .name = "VP0", 278 + .base = RK3568_VP0_CTRL_BASE, 279 + .size = 0x100, 280 + .en_reg = RK3568_VP_DSP_CTRL, 281 + .en_val = 0, 282 + .en_mask = RK3568_VP_DSP_CTRL__STANDBY, 283 + }, { 284 + .name = "VP1", 285 + .base = RK3568_VP1_CTRL_BASE, 286 + .size = 0x100, 287 + .en_reg = RK3568_VP_DSP_CTRL, 288 + .en_val = 0, 289 + .en_mask = RK3568_VP_DSP_CTRL__STANDBY, 290 + }, { 291 + .name = "VP2", 292 + .base = RK3568_VP2_CTRL_BASE, 293 + .size = 0x100, 294 + .en_reg = RK3568_VP_DSP_CTRL, 295 + .en_val = 0, 296 + .en_mask = RK3568_VP_DSP_CTRL__STANDBY, 297 + 298 + }, { 299 + .name = "Cluster0", 300 + .base = RK3568_CLUSTER0_CTRL_BASE, 301 + .size = 0x110, 302 + .en_reg = RK3568_CLUSTER_WIN_CTRL0, 303 + .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 304 + .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 305 + }, { 306 + .name = "Cluster1", 307 + .base = RK3568_CLUSTER1_CTRL_BASE, 308 + .size = 0x110, 309 + .en_reg = RK3568_CLUSTER_WIN_CTRL0, 310 + .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 311 + .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 312 + }, { 313 + .name = "Esmart0", 314 + .base = RK3568_ESMART0_CTRL_BASE, 315 + .size = 0xf0, 316 + .en_reg = RK3568_SMART_REGION0_CTRL, 317 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 318 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 319 + }, { 320 + .name = "Esmart1", 321 + .base = RK3568_ESMART1_CTRL_BASE, 322 + .size = 0xf0, 323 + .en_reg = RK3568_SMART_REGION0_CTRL, 324 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 325 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 326 + }, { 327 + .name = "Smart0", 328 + .base = RK3568_SMART0_CTRL_BASE, 329 + .size = 0xf0, 330 + .en_reg = RK3568_SMART_REGION0_CTRL, 331 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 332 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 333 + }, { 334 + .name = "Smart1", 335 + .base = RK3568_SMART1_CTRL_BASE, 336 + .size = 0xf0, 337 + .en_reg = RK3568_SMART_REGION0_CTRL, 338 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 339 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 340 + }, 341 + }; 342 + 261 343 static const struct vop2_video_port_data rk3588_vop_video_ports[] = { 262 344 { 263 345 .id = 0, ··· 395 313 * AXI1 is a read only bus. 396 314 * 397 315 * Every window on a AXI bus must assigned two unique 398 - * read id(yrgb_id/uv_id, valid id are 0x1~0xe). 316 + * read id(yrgb_r_id/uv_r_id, valid id are 0x1~0xe). 399 317 * 400 318 * AXI0: 401 319 * Cluster0/1, Esmart0/1, WriteBack ··· 415 333 .layer_sel_id = 0, 416 334 .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | 417 335 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, 336 + .axi_bus_id = 0, 337 + .axi_yrgb_r_id = 2, 338 + .axi_uv_r_id = 3, 418 339 .max_upscale_factor = 4, 419 340 .max_downscale_factor = 4, 420 341 .dly = { 4, 26, 29 }, ··· 434 349 .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | 435 350 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, 436 351 .type = DRM_PLANE_TYPE_PRIMARY, 352 + .axi_bus_id = 0, 353 + .axi_yrgb_r_id = 6, 354 + .axi_uv_r_id = 7, 437 355 .max_upscale_factor = 4, 438 356 .max_downscale_factor = 4, 439 357 .dly = { 4, 26, 29 }, ··· 452 364 .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | 453 365 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, 454 366 .type = DRM_PLANE_TYPE_PRIMARY, 367 + .axi_bus_id = 1, 368 + .axi_yrgb_r_id = 2, 369 + .axi_uv_r_id = 3, 455 370 .max_upscale_factor = 4, 456 371 .max_downscale_factor = 4, 457 372 .dly = { 4, 26, 29 }, ··· 470 379 .supported_rotations = DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270 | 471 380 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y, 472 381 .type = DRM_PLANE_TYPE_PRIMARY, 382 + .axi_bus_id = 1, 383 + .axi_yrgb_r_id = 6, 384 + .axi_uv_r_id = 7, 473 385 .max_upscale_factor = 4, 474 386 .max_downscale_factor = 4, 475 387 .dly = { 4, 26, 29 }, ··· 487 393 .layer_sel_id = 2, 488 394 .supported_rotations = DRM_MODE_REFLECT_Y, 489 395 .type = DRM_PLANE_TYPE_OVERLAY, 396 + .axi_bus_id = 0, 397 + .axi_yrgb_r_id = 0x0a, 398 + .axi_uv_r_id = 0x0b, 490 399 .max_upscale_factor = 8, 491 400 .max_downscale_factor = 8, 492 401 .dly = { 23, 45, 48 }, ··· 503 406 .layer_sel_id = 3, 504 407 .supported_rotations = DRM_MODE_REFLECT_Y, 505 408 .type = DRM_PLANE_TYPE_OVERLAY, 409 + .axi_bus_id = 0, 410 + .axi_yrgb_r_id = 0x0c, 411 + .axi_uv_r_id = 0x01, 506 412 .max_upscale_factor = 8, 507 413 .max_downscale_factor = 8, 508 414 .dly = { 23, 45, 48 }, ··· 519 419 .layer_sel_id = 6, 520 420 .supported_rotations = DRM_MODE_REFLECT_Y, 521 421 .type = DRM_PLANE_TYPE_OVERLAY, 422 + .axi_bus_id = 1, 423 + .axi_yrgb_r_id = 0x0a, 424 + .axi_uv_r_id = 0x0b, 522 425 .max_upscale_factor = 8, 523 426 .max_downscale_factor = 8, 524 427 .dly = { 23, 45, 48 }, ··· 535 432 .layer_sel_id = 7, 536 433 .supported_rotations = DRM_MODE_REFLECT_Y, 537 434 .type = DRM_PLANE_TYPE_OVERLAY, 435 + .axi_bus_id = 1, 436 + .axi_yrgb_r_id = 0x0c, 437 + .axi_uv_r_id = 0x0d, 538 438 .max_upscale_factor = 8, 539 439 .max_downscale_factor = 8, 540 440 .dly = { 23, 45, 48 }, 441 + }, 442 + }; 443 + 444 + static const struct vop2_regs_dump rk3588_regs_dump[] = { 445 + { 446 + .name = "SYS", 447 + .base = RK3568_REG_CFG_DONE, 448 + .size = 0x100, 449 + .en_reg = 0, 450 + .en_val = 0, 451 + .en_mask = 0 452 + }, { 453 + .name = "OVL", 454 + .base = RK3568_OVL_CTRL, 455 + .size = 0x100, 456 + .en_reg = 0, 457 + .en_val = 0, 458 + .en_mask = 0, 459 + }, { 460 + .name = "VP0", 461 + .base = RK3568_VP0_CTRL_BASE, 462 + .size = 0x100, 463 + .en_reg = RK3568_VP_DSP_CTRL, 464 + .en_val = 0, 465 + .en_mask = RK3568_VP_DSP_CTRL__STANDBY, 466 + }, { 467 + .name = "VP1", 468 + .base = RK3568_VP1_CTRL_BASE, 469 + .size = 0x100, 470 + .en_reg = RK3568_VP_DSP_CTRL, 471 + .en_val = 0, 472 + .en_mask = RK3568_VP_DSP_CTRL__STANDBY, 473 + }, { 474 + .name = "VP2", 475 + .base = RK3568_VP2_CTRL_BASE, 476 + .size = 0x100, 477 + .en_reg = RK3568_VP_DSP_CTRL, 478 + .en_val = 0, 479 + .en_mask = RK3568_VP_DSP_CTRL__STANDBY, 480 + 481 + }, { 482 + .name = "VP3", 483 + .base = RK3588_VP3_CTRL_BASE, 484 + .size = 0x100, 485 + .en_reg = RK3568_VP_DSP_CTRL, 486 + .en_val = 0, 487 + .en_mask = RK3568_VP_DSP_CTRL__STANDBY, 488 + }, { 489 + .name = "Cluster0", 490 + .base = RK3568_CLUSTER0_CTRL_BASE, 491 + .size = 0x110, 492 + .en_reg = RK3568_CLUSTER_WIN_CTRL0, 493 + .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 494 + .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 495 + }, { 496 + .name = "Cluster1", 497 + .base = RK3568_CLUSTER1_CTRL_BASE, 498 + .size = 0x110, 499 + .en_reg = RK3568_CLUSTER_WIN_CTRL0, 500 + .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 501 + .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 502 + }, { 503 + .name = "Cluster2", 504 + .base = RK3588_CLUSTER2_CTRL_BASE, 505 + .size = 0x110, 506 + .en_reg = RK3568_CLUSTER_WIN_CTRL0, 507 + .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 508 + .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 509 + }, { 510 + .name = "Cluster3", 511 + .base = RK3588_CLUSTER3_CTRL_BASE, 512 + .size = 0x110, 513 + .en_reg = RK3568_CLUSTER_WIN_CTRL0, 514 + .en_val = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 515 + .en_mask = RK3568_CLUSTER_WIN_CTRL0__WIN0_EN, 516 + }, { 517 + .name = "Esmart0", 518 + .base = RK3568_ESMART0_CTRL_BASE, 519 + .size = 0xf0, 520 + .en_reg = RK3568_SMART_REGION0_CTRL, 521 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 522 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 523 + }, { 524 + .name = "Esmart1", 525 + .base = RK3568_ESMART1_CTRL_BASE, 526 + .size = 0xf0, 527 + .en_reg = RK3568_SMART_REGION0_CTRL, 528 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 529 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 530 + }, { 531 + .name = "Esmart2", 532 + .base = RK3588_ESMART2_CTRL_BASE, 533 + .size = 0xf0, 534 + .en_reg = RK3568_SMART_REGION0_CTRL, 535 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 536 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 537 + }, { 538 + .name = "Esmart3", 539 + .base = RK3588_ESMART3_CTRL_BASE, 540 + .size = 0xf0, 541 + .en_reg = RK3568_SMART_REGION0_CTRL, 542 + .en_val = RK3568_SMART_REGION0_CTRL__WIN0_EN, 543 + .en_mask = RK3568_SMART_REGION0_CTRL__WIN0_EN, 541 544 }, 542 545 }; 543 546 ··· 655 446 .vp = rk3568_vop_video_ports, 656 447 .win = rk3568_vop_win_data, 657 448 .win_size = ARRAY_SIZE(rk3568_vop_win_data), 449 + .regs_dump = rk3568_regs_dump, 450 + .regs_dump_size = ARRAY_SIZE(rk3568_regs_dump), 658 451 .soc_id = 3566, 659 452 }; 660 453 ··· 668 457 .vp = rk3568_vop_video_ports, 669 458 .win = rk3568_vop_win_data, 670 459 .win_size = ARRAY_SIZE(rk3568_vop_win_data), 460 + .regs_dump = rk3568_regs_dump, 461 + .regs_dump_size = ARRAY_SIZE(rk3568_regs_dump), 671 462 .soc_id = 3568, 672 463 }; 673 464 ··· 682 469 .vp = rk3588_vop_video_ports, 683 470 .win = rk3588_vop_win_data, 684 471 .win_size = ARRAY_SIZE(rk3588_vop_win_data), 472 + .regs_dump = rk3588_regs_dump, 473 + .regs_dump_size = ARRAY_SIZE(rk3588_regs_dump), 685 474 .soc_id = 3588, 686 475 }; 687 476
+1 -1
drivers/gpu/drm/rockchip/rockchip_vop_reg.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+1 -1
drivers/gpu/drm/rockchip/rockchip_vop_reg.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 + * Copyright (C) Rockchip Electronics Co., Ltd. 4 4 * Author:Mark Yao <mark.yao@rock-chips.com> 5 5 */ 6 6
+2
drivers/gpu/drm/sti/sti_hdmi.c
··· 1225 1225 struct drm_connector *connector = hdmi->drm_connector; 1226 1226 1227 1227 DRM_DEBUG_DRIVER("\n"); 1228 + mutex_lock(&connector->eld_mutex); 1228 1229 memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); 1230 + mutex_unlock(&connector->eld_mutex); 1229 1231 1230 1232 return 0; 1231 1233 }
+2 -30
drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
··· 187 187 return MODE_NOCLOCK; 188 188 } 189 189 190 - static int sun4i_hdmi_connector_atomic_check(struct drm_connector *connector, 191 - struct drm_atomic_state *state) 192 - { 193 - struct drm_connector_state *conn_state = 194 - drm_atomic_get_new_connector_state(state, connector); 195 - struct drm_crtc *crtc = conn_state->crtc; 196 - struct drm_crtc_state *crtc_state = crtc->state; 197 - struct drm_display_mode *mode = &crtc_state->adjusted_mode; 198 - enum drm_mode_status status; 199 - 200 - status = sun4i_hdmi_connector_clock_valid(connector, mode, 201 - conn_state->hdmi.tmds_char_rate); 202 - if (status != MODE_OK) 203 - return -EINVAL; 204 - 205 - return 0; 206 - } 207 - 208 - static enum drm_mode_status 209 - sun4i_hdmi_connector_mode_valid(struct drm_connector *connector, 210 - struct drm_display_mode *mode) 211 - { 212 - unsigned long long rate = drm_hdmi_compute_mode_clock(mode, 8, 213 - HDMI_COLORSPACE_RGB); 214 - 215 - return sun4i_hdmi_connector_clock_valid(connector, mode, rate); 216 - } 217 - 218 190 static int sun4i_hdmi_get_modes(struct drm_connector *connector) 219 191 { 220 192 struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector); ··· 240 268 }; 241 269 242 270 static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = { 243 - .atomic_check = sun4i_hdmi_connector_atomic_check, 244 - .mode_valid = sun4i_hdmi_connector_mode_valid, 271 + .atomic_check = drm_atomic_helper_connector_hdmi_check, 272 + .mode_valid = drm_hdmi_connector_mode_valid, 245 273 .get_modes = sun4i_hdmi_get_modes, 246 274 }; 247 275
+463
drivers/gpu/drm/tests/drm_connector_test.c
··· 9 9 #include <drm/drm_connector.h> 10 10 #include <drm/drm_drv.h> 11 11 #include <drm/drm_edid.h> 12 + #include <drm/drm_file.h> 12 13 #include <drm/drm_kunit_helpers.h> 13 14 #include <drm/drm_modes.h> 14 15 ··· 180 179 .name = "drmm_connector_init", 181 180 .init = drm_test_connector_init, 182 181 .test_cases = drmm_connector_init_tests, 182 + }; 183 + 184 + static const struct drm_connector_funcs dummy_dynamic_init_funcs = { 185 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 186 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 187 + .reset = drm_atomic_helper_connector_reset, 188 + .destroy = drm_connector_cleanup, 189 + }; 190 + 191 + /* 192 + * Test that the initialization of a bog standard dynamic connector works 193 + * as expected and doesn't report any error. 194 + */ 195 + static void drm_test_drm_connector_dynamic_init(struct kunit *test) 196 + { 197 + struct drm_connector_init_priv *priv = test->priv; 198 + struct drm_connector *connector = &priv->connector; 199 + int ret; 200 + 201 + ret = drm_connector_dynamic_init(&priv->drm, connector, 202 + &dummy_dynamic_init_funcs, 203 + DRM_MODE_CONNECTOR_DisplayPort, 204 + &priv->ddc); 205 + KUNIT_ASSERT_EQ(test, ret, 0); 206 + } 207 + 208 + static void drm_test_connector_dynamic_init_cleanup(struct kunit *test) 209 + { 210 + struct drm_connector_init_priv *priv = test->priv; 211 + struct drm_connector *connector = &priv->connector; 212 + 213 + drm_connector_cleanup(connector); 214 + } 215 + 216 + /* 217 + * Test that the initialization of a dynamic connector without a DDC adapter 218 + * doesn't report any error. 219 + */ 220 + static void drm_test_drm_connector_dynamic_init_null_ddc(struct kunit *test) 221 + { 222 + struct drm_connector_init_priv *priv = test->priv; 223 + struct drm_connector *connector = &priv->connector; 224 + int ret; 225 + 226 + ret = drm_connector_dynamic_init(&priv->drm, connector, 227 + &dummy_dynamic_init_funcs, 228 + DRM_MODE_CONNECTOR_DisplayPort, 229 + NULL); 230 + KUNIT_ASSERT_EQ(test, ret, 0); 231 + } 232 + 233 + /* 234 + * Test that the initialization of a dynamic connector doesn't add the 235 + * connector to the connector list. 236 + */ 237 + static void drm_test_drm_connector_dynamic_init_not_added(struct kunit *test) 238 + { 239 + struct drm_connector_init_priv *priv = test->priv; 240 + struct drm_connector *connector = &priv->connector; 241 + int ret; 242 + 243 + ret = drm_connector_dynamic_init(&priv->drm, connector, 244 + &dummy_dynamic_init_funcs, 245 + DRM_MODE_CONNECTOR_DisplayPort, 246 + &priv->ddc); 247 + KUNIT_ASSERT_EQ(test, ret, 0); 248 + KUNIT_ASSERT_PTR_EQ(test, connector->head.next, &connector->head); 249 + } 250 + 251 + static void test_connector_property(struct kunit *test, 252 + struct drm_connector *connector, 253 + const struct drm_property *expected_prop) 254 + { 255 + struct drm_property *prop; 256 + uint64_t val; 257 + int ret; 258 + 259 + KUNIT_ASSERT_NOT_NULL(test, expected_prop); 260 + prop = drm_mode_obj_find_prop_id(&connector->base, expected_prop->base.id); 261 + KUNIT_ASSERT_PTR_EQ_MSG(test, prop, expected_prop, 262 + "Can't find property %s", expected_prop->name); 263 + 264 + ret = drm_object_property_get_default_value(&connector->base, prop, &val); 265 + KUNIT_EXPECT_EQ(test, ret, 0); 266 + KUNIT_EXPECT_EQ(test, val, 0); 267 + 268 + /* TODO: Check property value in the connector state. */ 269 + } 270 + 271 + /* 272 + * Test that the initialization of a dynamic connector adds all the expected 273 + * properties to it. 274 + */ 275 + static void drm_test_drm_connector_dynamic_init_properties(struct kunit *test) 276 + { 277 + struct drm_connector_init_priv *priv = test->priv; 278 + struct drm_connector *connector = &priv->connector; 279 + struct drm_mode_config *config = &priv->drm.mode_config; 280 + const struct drm_property *props[] = { 281 + config->edid_property, 282 + config->dpms_property, 283 + config->link_status_property, 284 + config->non_desktop_property, 285 + config->tile_property, 286 + config->prop_crtc_id, 287 + }; 288 + int ret; 289 + int i; 290 + 291 + ret = drm_connector_dynamic_init(&priv->drm, connector, 292 + &dummy_dynamic_init_funcs, 293 + DRM_MODE_CONNECTOR_DisplayPort, 294 + &priv->ddc); 295 + KUNIT_ASSERT_EQ(test, ret, 0); 296 + 297 + for (i = 0; i < ARRAY_SIZE(props); i++) 298 + test_connector_property(test, connector, props[i]); 299 + } 300 + 301 + /* 302 + * Test that the initialization of a dynamic connector succeeds for all 303 + * possible connector types. 304 + */ 305 + static void drm_test_drm_connector_dynamic_init_type_valid(struct kunit *test) 306 + { 307 + struct drm_connector_init_priv *priv = test->priv; 308 + struct drm_connector *connector = &priv->connector; 309 + unsigned int connector_type = *(unsigned int *)test->param_value; 310 + int ret; 311 + 312 + ret = drm_connector_dynamic_init(&priv->drm, connector, 313 + &dummy_dynamic_init_funcs, 314 + connector_type, 315 + &priv->ddc); 316 + KUNIT_ASSERT_EQ(test, ret, 0); 317 + } 318 + 319 + /* 320 + * Test that the initialization of a dynamic connector sets the expected name 321 + * for it for all possible connector types. 322 + */ 323 + static void drm_test_drm_connector_dynamic_init_name(struct kunit *test) 324 + { 325 + struct drm_connector_init_priv *priv = test->priv; 326 + struct drm_connector *connector = &priv->connector; 327 + unsigned int connector_type = *(unsigned int *)test->param_value; 328 + char expected_name[128]; 329 + int ret; 330 + 331 + ret = drm_connector_dynamic_init(&priv->drm, connector, 332 + &dummy_dynamic_init_funcs, 333 + connector_type, 334 + &priv->ddc); 335 + KUNIT_ASSERT_EQ(test, ret, 0); 336 + 337 + snprintf(expected_name, sizeof(expected_name), "%s-%d", 338 + drm_get_connector_type_name(connector_type), connector->connector_type_id); 339 + KUNIT_ASSERT_STREQ(test, connector->name, expected_name); 340 + } 341 + 342 + static struct kunit_case drm_connector_dynamic_init_tests[] = { 343 + KUNIT_CASE(drm_test_drm_connector_dynamic_init), 344 + KUNIT_CASE(drm_test_drm_connector_dynamic_init_null_ddc), 345 + KUNIT_CASE(drm_test_drm_connector_dynamic_init_not_added), 346 + KUNIT_CASE(drm_test_drm_connector_dynamic_init_properties), 347 + KUNIT_CASE_PARAM(drm_test_drm_connector_dynamic_init_type_valid, 348 + drm_connector_init_type_valid_gen_params), 349 + KUNIT_CASE_PARAM(drm_test_drm_connector_dynamic_init_name, 350 + drm_connector_init_type_valid_gen_params), 351 + {} 352 + }; 353 + 354 + static struct kunit_suite drm_connector_dynamic_init_test_suite = { 355 + .name = "drm_connector_dynamic_init", 356 + .init = drm_test_connector_init, 357 + .exit = drm_test_connector_dynamic_init_cleanup, 358 + .test_cases = drm_connector_dynamic_init_tests, 359 + }; 360 + 361 + static int drm_test_connector_dynamic_register_early_init(struct kunit *test) 362 + { 363 + struct drm_connector_init_priv *priv; 364 + int ret; 365 + 366 + ret = drm_test_connector_init(test); 367 + KUNIT_ASSERT_EQ(test, ret, 0); 368 + 369 + priv = test->priv; 370 + 371 + ret = drm_connector_dynamic_init(&priv->drm, &priv->connector, 372 + &dummy_dynamic_init_funcs, 373 + DRM_MODE_CONNECTOR_DisplayPort, 374 + &priv->ddc); 375 + KUNIT_ASSERT_EQ(test, ret, 0); 376 + 377 + return 0; 378 + } 379 + 380 + static void drm_test_connector_dynamic_register_early_cleanup(struct kunit *test) 381 + { 382 + struct drm_connector_init_priv *priv = test->priv; 383 + struct drm_connector *connector = &priv->connector; 384 + 385 + drm_connector_unregister(connector); 386 + drm_connector_put(connector); 387 + } 388 + 389 + /* 390 + * Test that registration of a dynamic connector adds it to the connector list. 391 + */ 392 + static void drm_test_drm_connector_dynamic_register_early_on_list(struct kunit *test) 393 + { 394 + struct drm_connector_init_priv *priv = test->priv; 395 + struct drm_connector *connector = &priv->connector; 396 + int ret; 397 + 398 + KUNIT_ASSERT_TRUE(test, list_empty(&connector->head)); 399 + 400 + ret = drm_connector_dynamic_register(connector); 401 + KUNIT_ASSERT_EQ(test, ret, 0); 402 + 403 + KUNIT_ASSERT_PTR_EQ(test, connector->head.next, &priv->drm.mode_config.connector_list); 404 + } 405 + 406 + /* 407 + * Test that the registration of a dynamic connector before the drm device is 408 + * registered results in deferring the connector's user interface registration. 409 + */ 410 + static void drm_test_drm_connector_dynamic_register_early_defer(struct kunit *test) 411 + { 412 + struct drm_connector_init_priv *priv = test->priv; 413 + struct drm_connector *connector = &priv->connector; 414 + int ret; 415 + 416 + ret = drm_connector_dynamic_register(connector); 417 + KUNIT_ASSERT_EQ(test, ret, 0); 418 + 419 + KUNIT_ASSERT_EQ(test, connector->registration_state, DRM_CONNECTOR_INITIALIZING); 420 + } 421 + 422 + /* 423 + * Test that the registration of a dynamic connector fails, if this is done before 424 + * the connector is initialized. 425 + */ 426 + static void drm_test_drm_connector_dynamic_register_early_no_init(struct kunit *test) 427 + { 428 + struct drm_connector *connector; 429 + int ret; 430 + 431 + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); /* auto freed */ 432 + KUNIT_ASSERT_NOT_NULL(test, connector); 433 + 434 + ret = drm_connector_dynamic_register(connector); 435 + KUNIT_ASSERT_EQ(test, ret, -EINVAL); 436 + } 437 + 438 + /* 439 + * Test that the registration of a dynamic connector before the drm device is 440 + * registered results in deferring adding a mode object for the connector. 441 + */ 442 + static void drm_test_drm_connector_dynamic_register_early_no_mode_object(struct kunit *test) 443 + { 444 + struct drm_connector_init_priv *priv = test->priv; 445 + struct drm_connector *connector = &priv->connector; 446 + struct drm_connector *tmp_connector; 447 + int ret; 448 + 449 + ret = drm_connector_dynamic_register(&priv->connector); 450 + KUNIT_ASSERT_EQ(test, ret, 0); 451 + 452 + tmp_connector = drm_connector_lookup(connector->dev, NULL, connector->base.id); 453 + KUNIT_ASSERT_NULL(test, tmp_connector); 454 + } 455 + 456 + static struct kunit_case drm_connector_dynamic_register_early_tests[] = { 457 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_early_on_list), 458 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_early_defer), 459 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_early_no_init), 460 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_early_no_mode_object), 461 + { } 462 + }; 463 + 464 + static struct kunit_suite drm_connector_dynamic_register_early_test_suite = { 465 + .name = "drm_connector_dynamic_register_early", 466 + .init = drm_test_connector_dynamic_register_early_init, 467 + .exit = drm_test_connector_dynamic_register_early_cleanup, 468 + .test_cases = drm_connector_dynamic_register_early_tests, 469 + }; 470 + 471 + static int drm_test_connector_dynamic_register_init(struct kunit *test) 472 + { 473 + struct drm_connector_init_priv *priv; 474 + int ret; 475 + 476 + ret = drm_test_connector_dynamic_register_early_init(test); 477 + KUNIT_ASSERT_EQ(test, ret, 0); 478 + 479 + priv = test->priv; 480 + 481 + ret = drm_dev_register(priv->connector.dev, 0); 482 + KUNIT_ASSERT_EQ(test, ret, 0); 483 + 484 + return 0; 485 + } 486 + 487 + static void drm_test_connector_dynamic_register_cleanup(struct kunit *test) 488 + { 489 + struct drm_connector_init_priv *priv = test->priv; 490 + struct drm_device *dev = priv->connector.dev; 491 + 492 + drm_connector_unregister(&priv->connector); 493 + drm_connector_put(&priv->connector); 494 + 495 + drm_dev_unregister(dev); 496 + 497 + drm_test_connector_dynamic_register_early_cleanup(test); 498 + } 499 + 500 + static void drm_test_drm_connector_dynamic_register_on_list(struct kunit *test) 501 + { 502 + struct drm_connector_init_priv *priv = test->priv; 503 + int ret; 504 + 505 + KUNIT_ASSERT_TRUE(test, list_empty(&priv->connector.head)); 506 + 507 + ret = drm_connector_dynamic_register(&priv->connector); 508 + KUNIT_ASSERT_EQ(test, ret, 0); 509 + 510 + KUNIT_ASSERT_PTR_EQ(test, priv->connector.head.next, &priv->drm.mode_config.connector_list); 511 + } 512 + 513 + /* 514 + * Test that the registration of a dynamic connector doesn't get deferred if 515 + * this is done after the drm device is registered. 516 + */ 517 + static void drm_test_drm_connector_dynamic_register_no_defer(struct kunit *test) 518 + { 519 + struct drm_connector_init_priv *priv = test->priv; 520 + int ret; 521 + 522 + KUNIT_ASSERT_EQ(test, priv->connector.registration_state, DRM_CONNECTOR_INITIALIZING); 523 + 524 + ret = drm_connector_dynamic_register(&priv->connector); 525 + KUNIT_ASSERT_EQ(test, ret, 0); 526 + 527 + KUNIT_ASSERT_EQ(test, priv->connector.registration_state, DRM_CONNECTOR_REGISTERED); 528 + } 529 + 530 + /* 531 + * Test that the registration of a dynamic connector fails if this is done after the 532 + * drm device is registered, but before the connector is initialized. 533 + */ 534 + static void drm_test_drm_connector_dynamic_register_no_init(struct kunit *test) 535 + { 536 + struct drm_connector *connector; 537 + int ret; 538 + 539 + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); /* auto freed */ 540 + KUNIT_ASSERT_NOT_NULL(test, connector); 541 + 542 + ret = drm_connector_dynamic_register(connector); 543 + KUNIT_ASSERT_EQ(test, ret, -EINVAL); 544 + } 545 + 546 + /* 547 + * Test that the registration of a dynamic connector after the drm device is 548 + * registered adds the mode object for the connector. 549 + */ 550 + static void drm_test_drm_connector_dynamic_register_mode_object(struct kunit *test) 551 + { 552 + struct drm_connector_init_priv *priv = test->priv; 553 + struct drm_connector *connector = &priv->connector; 554 + struct drm_connector *tmp_connector; 555 + int ret; 556 + 557 + tmp_connector = drm_connector_lookup(connector->dev, NULL, connector->base.id); 558 + KUNIT_ASSERT_NULL(test, tmp_connector); 559 + 560 + ret = drm_connector_dynamic_register(&priv->connector); 561 + KUNIT_ASSERT_EQ(test, ret, 0); 562 + 563 + tmp_connector = drm_connector_lookup(connector->dev, NULL, connector->base.id); 564 + KUNIT_ASSERT_PTR_EQ(test, tmp_connector, connector); 565 + } 566 + 567 + /* 568 + * Test that the registration of a dynamic connector after the drm device is 569 + * registered adds the connector to sysfs. 570 + */ 571 + static void drm_test_drm_connector_dynamic_register_sysfs(struct kunit *test) 572 + { 573 + struct drm_connector_init_priv *priv = test->priv; 574 + struct drm_connector *connector = &priv->connector; 575 + int ret; 576 + 577 + KUNIT_ASSERT_NULL(test, connector->kdev); 578 + 579 + ret = drm_connector_dynamic_register(connector); 580 + KUNIT_ASSERT_EQ(test, ret, 0); 581 + 582 + KUNIT_ASSERT_NOT_NULL(test, connector->kdev); 583 + } 584 + 585 + /* 586 + * Test that the registration of a dynamic connector after the drm device is 587 + * registered sets the connector's sysfs name as expected. 588 + */ 589 + static void drm_test_drm_connector_dynamic_register_sysfs_name(struct kunit *test) 590 + { 591 + struct drm_connector_init_priv *priv = test->priv; 592 + struct drm_connector *connector = &priv->connector; 593 + char expected_name[128]; 594 + int ret; 595 + 596 + ret = drm_connector_dynamic_register(connector); 597 + KUNIT_ASSERT_EQ(test, ret, 0); 598 + 599 + snprintf(expected_name, sizeof(expected_name), "card%d-%s", 600 + connector->dev->primary->index, connector->name); 601 + 602 + KUNIT_ASSERT_STREQ(test, dev_name(connector->kdev), expected_name); 603 + } 604 + 605 + /* 606 + * Test that the registration of a dynamic connector after the drm device is 607 + * registered adds the connector to debugfs. 608 + */ 609 + static void drm_test_drm_connector_dynamic_register_debugfs(struct kunit *test) 610 + { 611 + struct drm_connector_init_priv *priv = test->priv; 612 + int ret; 613 + 614 + KUNIT_ASSERT_NULL(test, priv->connector.debugfs_entry); 615 + 616 + ret = drm_connector_dynamic_register(&priv->connector); 617 + KUNIT_ASSERT_EQ(test, ret, 0); 618 + 619 + if (IS_ENABLED(CONFIG_DEBUG_FS)) 620 + KUNIT_ASSERT_NOT_NULL(test, priv->connector.debugfs_entry); 621 + else 622 + KUNIT_ASSERT_NULL(test, priv->connector.debugfs_entry); 623 + } 624 + 625 + static struct kunit_case drm_connector_dynamic_register_tests[] = { 626 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_on_list), 627 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_no_defer), 628 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_no_init), 629 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_mode_object), 630 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_sysfs), 631 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_sysfs_name), 632 + KUNIT_CASE(drm_test_drm_connector_dynamic_register_debugfs), 633 + { } 634 + }; 635 + 636 + static struct kunit_suite drm_connector_dynamic_register_test_suite = { 637 + .name = "drm_connector_dynamic_register", 638 + .init = drm_test_connector_dynamic_register_init, 639 + .exit = drm_test_connector_dynamic_register_cleanup, 640 + .test_cases = drm_connector_dynamic_register_tests, 183 641 }; 184 642 185 643 /* ··· 1743 1283 kunit_test_suites( 1744 1284 &drmm_connector_hdmi_init_test_suite, 1745 1285 &drmm_connector_init_test_suite, 1286 + &drm_connector_dynamic_init_test_suite, 1287 + &drm_connector_dynamic_register_early_test_suite, 1288 + &drm_connector_dynamic_register_test_suite, 1746 1289 &drm_connector_attach_broadcast_rgb_property_test_suite, 1747 1290 &drm_get_tv_mode_from_name_test_suite, 1748 1291 &drm_hdmi_compute_mode_clock_test_suite,
+289 -120
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
··· 46 46 struct drm_display_mode *mode, *preferred; 47 47 48 48 mutex_lock(&drm->mode_config.mutex); 49 - preferred = list_first_entry(&connector->modes, struct drm_display_mode, head); 49 + preferred = list_first_entry_or_null(&connector->modes, struct drm_display_mode, head); 50 50 list_for_each_entry(mode, &connector->modes, head) 51 51 if (mode->type & DRM_MODE_TYPE_PREFERRED) 52 52 preferred = mode; ··· 105 105 mutex_lock(&drm->mode_config.mutex); 106 106 ret = connector->funcs->fill_modes(connector, 4096, 4096); 107 107 mutex_unlock(&drm->mode_config.mutex); 108 - KUNIT_ASSERT_GT(test, ret, 0); 109 108 110 - return 0; 109 + return ret; 111 110 } 112 111 113 112 static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = { ··· 122 123 123 124 static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = { 124 125 .tmds_char_rate_valid = reject_connector_tmds_char_rate_valid, 126 + }; 127 + 128 + static enum drm_mode_status 129 + reject_100MHz_connector_tmds_char_rate_valid(const struct drm_connector *connector, 130 + const struct drm_display_mode *mode, 131 + unsigned long long tmds_rate) 132 + { 133 + return (tmds_rate > 100ULL * 1000 * 1000) ? MODE_BAD : MODE_OK; 134 + } 135 + 136 + static const struct drm_connector_hdmi_funcs reject_100_MHz_connector_hdmi_funcs = { 137 + .tmds_char_rate_valid = reject_100MHz_connector_tmds_char_rate_valid, 125 138 }; 126 139 127 140 static int dummy_connector_get_modes(struct drm_connector *connector) ··· 158 147 static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = { 159 148 .atomic_check = drm_atomic_helper_connector_hdmi_check, 160 149 .get_modes = dummy_connector_get_modes, 150 + .mode_valid = drm_hdmi_connector_mode_valid, 161 151 }; 162 152 163 153 static void dummy_hdmi_connector_reset(struct drm_connector *connector) ··· 176 164 177 165 static 178 166 struct drm_atomic_helper_connector_hdmi_priv * 179 - drm_atomic_helper_connector_hdmi_init(struct kunit *test, 180 - unsigned int formats, 181 - unsigned int max_bpc) 167 + drm_kunit_helper_connector_hdmi_init_funcs(struct kunit *test, 168 + unsigned int formats, 169 + unsigned int max_bpc, 170 + const struct drm_connector_hdmi_funcs *hdmi_funcs) 182 171 { 183 172 struct drm_atomic_helper_connector_hdmi_priv *priv; 184 173 struct drm_connector *conn; ··· 221 208 ret = drmm_connector_hdmi_init(drm, conn, 222 209 "Vendor", "Product", 223 210 &dummy_connector_funcs, 224 - &dummy_connector_hdmi_funcs, 211 + hdmi_funcs, 225 212 DRM_MODE_CONNECTOR_HDMIA, 226 213 NULL, 227 214 formats, ··· 233 220 234 221 drm_mode_config_reset(drm); 235 222 236 - ret = set_connector_edid(test, conn, 223 + return priv; 224 + } 225 + 226 + static 227 + struct drm_atomic_helper_connector_hdmi_priv * 228 + drm_kunit_helper_connector_hdmi_init(struct kunit *test, 229 + unsigned int formats, 230 + unsigned int max_bpc) 231 + { 232 + struct drm_atomic_helper_connector_hdmi_priv *priv; 233 + int ret; 234 + 235 + priv = drm_kunit_helper_connector_hdmi_init_funcs(test, 236 + formats, max_bpc, 237 + &dummy_connector_hdmi_funcs); 238 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); 239 + 240 + ret = set_connector_edid(test, &priv->connector, 237 241 test_edid_hdmi_1080p_rgb_max_200mhz, 238 242 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); 239 - KUNIT_ASSERT_EQ(test, ret, 0); 243 + KUNIT_ASSERT_GT(test, ret, 0); 240 244 241 245 return priv; 242 246 } ··· 277 247 struct drm_crtc *crtc; 278 248 int ret; 279 249 280 - priv = drm_atomic_helper_connector_hdmi_init(test, 281 - BIT(HDMI_COLORSPACE_RGB), 282 - 8); 250 + priv = drm_kunit_helper_connector_hdmi_init(test, 251 + BIT(HDMI_COLORSPACE_RGB), 252 + 8); 283 253 KUNIT_ASSERT_NOT_NULL(test, priv); 284 254 285 255 ctx = drm_kunit_helper_acquire_ctx_alloc(test); ··· 340 310 struct drm_crtc *crtc; 341 311 int ret; 342 312 343 - priv = drm_atomic_helper_connector_hdmi_init(test, 344 - BIT(HDMI_COLORSPACE_RGB), 345 - 8); 313 + priv = drm_kunit_helper_connector_hdmi_init(test, 314 + BIT(HDMI_COLORSPACE_RGB), 315 + 8); 346 316 KUNIT_ASSERT_NOT_NULL(test, priv); 347 317 348 318 ctx = drm_kunit_helper_acquire_ctx_alloc(test); ··· 403 373 struct drm_crtc *crtc; 404 374 int ret; 405 375 406 - priv = drm_atomic_helper_connector_hdmi_init(test, 407 - BIT(HDMI_COLORSPACE_RGB), 408 - 8); 376 + priv = drm_kunit_helper_connector_hdmi_init(test, 377 + BIT(HDMI_COLORSPACE_RGB), 378 + 8); 409 379 KUNIT_ASSERT_NOT_NULL(test, priv); 410 380 411 381 conn = &priv->connector; ··· 459 429 struct drm_crtc *crtc; 460 430 int ret; 461 431 462 - priv = drm_atomic_helper_connector_hdmi_init(test, 463 - BIT(HDMI_COLORSPACE_RGB), 464 - 8); 432 + priv = drm_kunit_helper_connector_hdmi_init(test, 433 + BIT(HDMI_COLORSPACE_RGB), 434 + 8); 465 435 KUNIT_ASSERT_NOT_NULL(test, priv); 466 436 467 437 drm = &priv->drm; ··· 515 485 struct drm_crtc *crtc; 516 486 int ret; 517 487 518 - priv = drm_atomic_helper_connector_hdmi_init(test, 519 - BIT(HDMI_COLORSPACE_RGB), 520 - 8); 488 + priv = drm_kunit_helper_connector_hdmi_init(test, 489 + BIT(HDMI_COLORSPACE_RGB), 490 + 8); 521 491 KUNIT_ASSERT_NOT_NULL(test, priv); 522 492 523 493 conn = &priv->connector; ··· 573 543 struct drm_crtc *crtc; 574 544 int ret; 575 545 576 - priv = drm_atomic_helper_connector_hdmi_init(test, 577 - BIT(HDMI_COLORSPACE_RGB), 578 - 8); 546 + priv = drm_kunit_helper_connector_hdmi_init(test, 547 + BIT(HDMI_COLORSPACE_RGB), 548 + 8); 579 549 KUNIT_ASSERT_NOT_NULL(test, priv); 580 550 581 551 drm = &priv->drm; ··· 631 601 struct drm_crtc *crtc; 632 602 int ret; 633 603 634 - priv = drm_atomic_helper_connector_hdmi_init(test, 635 - BIT(HDMI_COLORSPACE_RGB), 636 - 8); 604 + priv = drm_kunit_helper_connector_hdmi_init(test, 605 + BIT(HDMI_COLORSPACE_RGB), 606 + 8); 637 607 KUNIT_ASSERT_NOT_NULL(test, priv); 638 608 639 609 conn = &priv->connector; ··· 689 659 struct drm_crtc *crtc; 690 660 int ret; 691 661 692 - priv = drm_atomic_helper_connector_hdmi_init(test, 693 - BIT(HDMI_COLORSPACE_RGB), 694 - 8); 662 + priv = drm_kunit_helper_connector_hdmi_init(test, 663 + BIT(HDMI_COLORSPACE_RGB), 664 + 8); 695 665 KUNIT_ASSERT_NOT_NULL(test, priv); 696 666 697 667 drm = &priv->drm; ··· 749 719 struct drm_crtc *crtc; 750 720 int ret; 751 721 752 - priv = drm_atomic_helper_connector_hdmi_init(test, 753 - BIT(HDMI_COLORSPACE_RGB), 754 - 10); 722 + priv = drm_kunit_helper_connector_hdmi_init(test, 723 + BIT(HDMI_COLORSPACE_RGB), 724 + 10); 755 725 KUNIT_ASSERT_NOT_NULL(test, priv); 756 726 757 727 conn = &priv->connector; 758 728 ret = set_connector_edid(test, conn, 759 729 test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 760 730 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 761 - KUNIT_ASSERT_EQ(test, ret, 0); 731 + KUNIT_ASSERT_GT(test, ret, 0); 762 732 763 733 ctx = drm_kunit_helper_acquire_ctx_alloc(test); 764 734 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); ··· 823 793 struct drm_crtc *crtc; 824 794 int ret; 825 795 826 - priv = drm_atomic_helper_connector_hdmi_init(test, 827 - BIT(HDMI_COLORSPACE_RGB), 828 - 10); 796 + priv = drm_kunit_helper_connector_hdmi_init(test, 797 + BIT(HDMI_COLORSPACE_RGB), 798 + 10); 829 799 KUNIT_ASSERT_NOT_NULL(test, priv); 830 800 831 801 conn = &priv->connector; 832 802 ret = set_connector_edid(test, conn, 833 803 test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 834 804 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 835 - KUNIT_ASSERT_EQ(test, ret, 0); 805 + KUNIT_ASSERT_GT(test, ret, 0); 836 806 837 807 ctx = drm_kunit_helper_acquire_ctx_alloc(test); 838 808 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); ··· 892 862 struct drm_crtc *crtc; 893 863 int ret; 894 864 895 - priv = drm_atomic_helper_connector_hdmi_init(test, 896 - BIT(HDMI_COLORSPACE_RGB) | 897 - BIT(HDMI_COLORSPACE_YUV422) | 898 - BIT(HDMI_COLORSPACE_YUV444), 899 - 12); 865 + priv = drm_kunit_helper_connector_hdmi_init(test, 866 + BIT(HDMI_COLORSPACE_RGB) | 867 + BIT(HDMI_COLORSPACE_YUV422) | 868 + BIT(HDMI_COLORSPACE_YUV444), 869 + 12); 900 870 KUNIT_ASSERT_NOT_NULL(test, priv); 901 871 902 872 conn = &priv->connector; 903 873 ret = set_connector_edid(test, conn, 904 874 test_edid_dvi_1080p, 905 875 ARRAY_SIZE(test_edid_dvi_1080p)); 906 - KUNIT_ASSERT_EQ(test, ret, 0); 876 + KUNIT_ASSERT_GT(test, ret, 0); 907 877 908 878 info = &conn->display_info; 909 879 KUNIT_ASSERT_FALSE(test, info->is_hdmi); ··· 941 911 struct drm_crtc *crtc; 942 912 int ret; 943 913 944 - priv = drm_atomic_helper_connector_hdmi_init(test, 945 - BIT(HDMI_COLORSPACE_RGB), 946 - 8); 914 + priv = drm_kunit_helper_connector_hdmi_init(test, 915 + BIT(HDMI_COLORSPACE_RGB), 916 + 8); 947 917 KUNIT_ASSERT_NOT_NULL(test, priv); 948 918 949 919 conn = &priv->connector; 950 920 ret = set_connector_edid(test, conn, 951 921 test_edid_hdmi_1080p_rgb_max_200mhz, 952 922 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); 953 - KUNIT_ASSERT_EQ(test, ret, 0); 923 + KUNIT_ASSERT_GT(test, ret, 0); 954 924 955 925 ctx = drm_kunit_helper_acquire_ctx_alloc(test); 956 926 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); ··· 988 958 struct drm_crtc *crtc; 989 959 int ret; 990 960 991 - priv = drm_atomic_helper_connector_hdmi_init(test, 992 - BIT(HDMI_COLORSPACE_RGB), 993 - 10); 961 + priv = drm_kunit_helper_connector_hdmi_init(test, 962 + BIT(HDMI_COLORSPACE_RGB), 963 + 10); 994 964 KUNIT_ASSERT_NOT_NULL(test, priv); 995 965 996 966 conn = &priv->connector; 997 967 ret = set_connector_edid(test, conn, 998 968 test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, 999 969 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); 1000 - KUNIT_ASSERT_EQ(test, ret, 0); 970 + KUNIT_ASSERT_GT(test, ret, 0); 1001 971 1002 972 ctx = drm_kunit_helper_acquire_ctx_alloc(test); 1003 973 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); ··· 1035 1005 struct drm_crtc *crtc; 1036 1006 int ret; 1037 1007 1038 - priv = drm_atomic_helper_connector_hdmi_init(test, 1039 - BIT(HDMI_COLORSPACE_RGB), 1040 - 12); 1008 + priv = drm_kunit_helper_connector_hdmi_init(test, 1009 + BIT(HDMI_COLORSPACE_RGB), 1010 + 12); 1041 1011 KUNIT_ASSERT_NOT_NULL(test, priv); 1042 1012 1043 1013 conn = &priv->connector; 1044 1014 ret = set_connector_edid(test, conn, 1045 1015 test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, 1046 1016 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); 1047 - KUNIT_ASSERT_EQ(test, ret, 0); 1017 + KUNIT_ASSERT_GT(test, ret, 0); 1048 1018 1049 1019 ctx = drm_kunit_helper_acquire_ctx_alloc(test); 1050 1020 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); ··· 1086 1056 struct drm_crtc *crtc; 1087 1057 int ret; 1088 1058 1089 - priv = drm_atomic_helper_connector_hdmi_init(test, 1090 - BIT(HDMI_COLORSPACE_RGB), 1091 - 8); 1059 + priv = drm_kunit_helper_connector_hdmi_init(test, 1060 + BIT(HDMI_COLORSPACE_RGB), 1061 + 8); 1092 1062 KUNIT_ASSERT_NOT_NULL(test, priv); 1093 1063 1094 1064 ctx = drm_kunit_helper_acquire_ctx_alloc(test); ··· 1142 1112 struct drm_crtc *crtc; 1143 1113 int ret; 1144 1114 1145 - priv = drm_atomic_helper_connector_hdmi_init(test, 1146 - BIT(HDMI_COLORSPACE_RGB), 1147 - 12); 1115 + priv = drm_kunit_helper_connector_hdmi_init(test, 1116 + BIT(HDMI_COLORSPACE_RGB), 1117 + 12); 1148 1118 KUNIT_ASSERT_NOT_NULL(test, priv); 1149 1119 1150 1120 conn = &priv->connector; 1151 1121 ret = set_connector_edid(test, conn, 1152 1122 test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 1153 1123 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 1154 - KUNIT_ASSERT_EQ(test, ret, 0); 1124 + KUNIT_ASSERT_GT(test, ret, 0); 1155 1125 1156 1126 info = &conn->display_info; 1157 1127 KUNIT_ASSERT_TRUE(test, info->is_hdmi); ··· 1209 1179 struct drm_crtc *crtc; 1210 1180 int ret; 1211 1181 1212 - priv = drm_atomic_helper_connector_hdmi_init(test, 1213 - BIT(HDMI_COLORSPACE_RGB) | 1214 - BIT(HDMI_COLORSPACE_YUV422) | 1215 - BIT(HDMI_COLORSPACE_YUV444), 1216 - 12); 1182 + priv = drm_kunit_helper_connector_hdmi_init(test, 1183 + BIT(HDMI_COLORSPACE_RGB) | 1184 + BIT(HDMI_COLORSPACE_YUV422) | 1185 + BIT(HDMI_COLORSPACE_YUV444), 1186 + 12); 1217 1187 KUNIT_ASSERT_NOT_NULL(test, priv); 1218 1188 1219 1189 conn = &priv->connector; 1220 1190 ret = set_connector_edid(test, conn, 1221 1191 test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 1222 1192 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 1223 - KUNIT_ASSERT_EQ(test, ret, 0); 1193 + KUNIT_ASSERT_GT(test, ret, 0); 1224 1194 1225 1195 info = &conn->display_info; 1226 1196 KUNIT_ASSERT_TRUE(test, info->is_hdmi); ··· 1272 1242 struct drm_crtc *crtc; 1273 1243 int ret; 1274 1244 1275 - priv = drm_atomic_helper_connector_hdmi_init(test, 1276 - BIT(HDMI_COLORSPACE_RGB) | 1277 - BIT(HDMI_COLORSPACE_YUV422) | 1278 - BIT(HDMI_COLORSPACE_YUV444), 1279 - 12); 1245 + priv = drm_kunit_helper_connector_hdmi_init(test, 1246 + BIT(HDMI_COLORSPACE_RGB) | 1247 + BIT(HDMI_COLORSPACE_YUV422) | 1248 + BIT(HDMI_COLORSPACE_YUV444), 1249 + 12); 1280 1250 KUNIT_ASSERT_NOT_NULL(test, priv); 1281 1251 1282 1252 drm = &priv->drm; ··· 1284 1254 ret = set_connector_edid(test, conn, 1285 1255 test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 1286 1256 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 1287 - KUNIT_ASSERT_EQ(test, ret, 0); 1257 + KUNIT_ASSERT_GT(test, ret, 0); 1288 1258 1289 1259 info = &conn->display_info; 1290 1260 KUNIT_ASSERT_TRUE(test, info->is_hdmi); ··· 1335 1305 struct drm_crtc *crtc; 1336 1306 int ret; 1337 1307 1338 - priv = drm_atomic_helper_connector_hdmi_init(test, 1339 - BIT(HDMI_COLORSPACE_RGB), 1340 - 12); 1308 + priv = drm_kunit_helper_connector_hdmi_init(test, 1309 + BIT(HDMI_COLORSPACE_RGB), 1310 + 12); 1341 1311 KUNIT_ASSERT_NOT_NULL(test, priv); 1342 1312 1343 1313 conn = &priv->connector; 1344 1314 ret = set_connector_edid(test, conn, 1345 1315 test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 1346 1316 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 1347 - KUNIT_ASSERT_EQ(test, ret, 0); 1317 + KUNIT_ASSERT_GT(test, ret, 0); 1348 1318 1349 1319 info = &conn->display_info; 1350 1320 KUNIT_ASSERT_TRUE(test, info->is_hdmi); ··· 1400 1370 struct drm_crtc *crtc; 1401 1371 int ret; 1402 1372 1403 - priv = drm_atomic_helper_connector_hdmi_init(test, 1404 - BIT(HDMI_COLORSPACE_RGB) | 1405 - BIT(HDMI_COLORSPACE_YUV422) | 1406 - BIT(HDMI_COLORSPACE_YUV444), 1407 - 12); 1373 + priv = drm_kunit_helper_connector_hdmi_init(test, 1374 + BIT(HDMI_COLORSPACE_RGB) | 1375 + BIT(HDMI_COLORSPACE_YUV422) | 1376 + BIT(HDMI_COLORSPACE_YUV444), 1377 + 12); 1408 1378 KUNIT_ASSERT_NOT_NULL(test, priv); 1409 1379 1410 1380 conn = &priv->connector; 1411 1381 ret = set_connector_edid(test, conn, 1412 1382 test_edid_hdmi_1080p_rgb_max_200mhz, 1413 1383 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); 1414 - KUNIT_ASSERT_EQ(test, ret, 0); 1384 + KUNIT_ASSERT_GT(test, ret, 0); 1415 1385 1416 1386 info = &conn->display_info; 1417 1387 KUNIT_ASSERT_TRUE(test, info->is_hdmi); ··· 1468 1438 struct drm_crtc *crtc; 1469 1439 int ret; 1470 1440 1471 - priv = drm_atomic_helper_connector_hdmi_init(test, 1472 - BIT(HDMI_COLORSPACE_RGB), 1473 - 8); 1441 + priv = drm_kunit_helper_connector_hdmi_init(test, 1442 + BIT(HDMI_COLORSPACE_RGB), 1443 + 8); 1474 1444 KUNIT_ASSERT_NOT_NULL(test, priv); 1475 1445 1476 1446 conn = &priv->connector; 1477 1447 ret = set_connector_edid(test, conn, 1478 1448 test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz, 1479 1449 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz)); 1480 - KUNIT_ASSERT_EQ(test, ret, 0); 1450 + KUNIT_ASSERT_GT(test, ret, 0); 1481 1451 1482 1452 info = &conn->display_info; 1483 1453 KUNIT_ASSERT_TRUE(test, info->is_hdmi); ··· 1526 1496 struct drm_crtc *crtc; 1527 1497 int ret; 1528 1498 1529 - priv = drm_atomic_helper_connector_hdmi_init(test, 1530 - BIT(HDMI_COLORSPACE_RGB) | 1531 - BIT(HDMI_COLORSPACE_YUV422) | 1532 - BIT(HDMI_COLORSPACE_YUV444), 1533 - 12); 1499 + priv = drm_kunit_helper_connector_hdmi_init(test, 1500 + BIT(HDMI_COLORSPACE_RGB) | 1501 + BIT(HDMI_COLORSPACE_YUV422) | 1502 + BIT(HDMI_COLORSPACE_YUV444), 1503 + 12); 1534 1504 KUNIT_ASSERT_NOT_NULL(test, priv); 1535 1505 1536 1506 conn = &priv->connector; 1537 1507 ret = set_connector_edid(test, conn, 1538 1508 test_edid_hdmi_1080p_rgb_max_340mhz, 1539 1509 ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_340mhz)); 1540 - KUNIT_ASSERT_EQ(test, ret, 0); 1510 + KUNIT_ASSERT_GT(test, ret, 0); 1541 1511 1542 1512 info = &conn->display_info; 1543 1513 KUNIT_ASSERT_TRUE(test, info->is_hdmi); ··· 1623 1593 struct drm_connector_state *conn_state; 1624 1594 struct drm_connector *conn; 1625 1595 1626 - priv = drm_atomic_helper_connector_hdmi_init(test, 1627 - BIT(HDMI_COLORSPACE_RGB), 1628 - 8); 1596 + priv = drm_kunit_helper_connector_hdmi_init(test, 1597 + BIT(HDMI_COLORSPACE_RGB), 1598 + 8); 1629 1599 KUNIT_ASSERT_NOT_NULL(test, priv); 1630 1600 1631 1601 conn = &priv->connector; ··· 1645 1615 struct drm_connector_state *conn_state; 1646 1616 struct drm_connector *conn; 1647 1617 1648 - priv = drm_atomic_helper_connector_hdmi_init(test, 1649 - BIT(HDMI_COLORSPACE_RGB), 1650 - 8); 1618 + priv = drm_kunit_helper_connector_hdmi_init(test, 1619 + BIT(HDMI_COLORSPACE_RGB), 1620 + 8); 1651 1621 KUNIT_ASSERT_NOT_NULL(test, priv); 1652 1622 1653 1623 conn = &priv->connector; ··· 1669 1639 struct drm_connector_state *conn_state; 1670 1640 struct drm_connector *conn; 1671 1641 1672 - priv = drm_atomic_helper_connector_hdmi_init(test, 1673 - BIT(HDMI_COLORSPACE_RGB), 1674 - 10); 1642 + priv = drm_kunit_helper_connector_hdmi_init(test, 1643 + BIT(HDMI_COLORSPACE_RGB), 1644 + 10); 1675 1645 KUNIT_ASSERT_NOT_NULL(test, priv); 1676 1646 1677 1647 conn = &priv->connector; ··· 1693 1663 struct drm_connector_state *conn_state; 1694 1664 struct drm_connector *conn; 1695 1665 1696 - priv = drm_atomic_helper_connector_hdmi_init(test, 1697 - BIT(HDMI_COLORSPACE_RGB), 1698 - 12); 1666 + priv = drm_kunit_helper_connector_hdmi_init(test, 1667 + BIT(HDMI_COLORSPACE_RGB), 1668 + 12); 1699 1669 KUNIT_ASSERT_NOT_NULL(test, priv); 1700 1670 1701 1671 conn = &priv->connector; ··· 1715 1685 struct drm_connector_state *conn_state; 1716 1686 struct drm_connector *conn; 1717 1687 1718 - priv = drm_atomic_helper_connector_hdmi_init(test, 1719 - BIT(HDMI_COLORSPACE_RGB) | 1720 - BIT(HDMI_COLORSPACE_YUV422) | 1721 - BIT(HDMI_COLORSPACE_YUV444), 1722 - 8); 1688 + priv = drm_kunit_helper_connector_hdmi_init(test, 1689 + BIT(HDMI_COLORSPACE_RGB) | 1690 + BIT(HDMI_COLORSPACE_YUV422) | 1691 + BIT(HDMI_COLORSPACE_YUV444), 1692 + 8); 1723 1693 KUNIT_ASSERT_NOT_NULL(test, priv); 1724 1694 1725 1695 conn = &priv->connector; ··· 1737 1707 struct drm_connector_state *conn_state; 1738 1708 struct drm_connector *conn; 1739 1709 1740 - priv = drm_atomic_helper_connector_hdmi_init(test, 1741 - BIT(HDMI_COLORSPACE_RGB) | 1742 - BIT(HDMI_COLORSPACE_YUV422) | 1743 - BIT(HDMI_COLORSPACE_YUV444), 1744 - 12); 1710 + priv = drm_kunit_helper_connector_hdmi_init(test, 1711 + BIT(HDMI_COLORSPACE_RGB) | 1712 + BIT(HDMI_COLORSPACE_YUV422) | 1713 + BIT(HDMI_COLORSPACE_YUV444), 1714 + 12); 1745 1715 KUNIT_ASSERT_NOT_NULL(test, priv); 1746 1716 1747 1717 conn = &priv->connector; ··· 1764 1734 .test_cases = drm_atomic_helper_connector_hdmi_reset_tests, 1765 1735 }; 1766 1736 1737 + /* 1738 + * Test that the default behaviour for drm_hdmi_connector_mode_valid() is not 1739 + * to reject any modes. Pass a correct EDID and verify that preferred mode 1740 + * matches the expectations (1080p). 1741 + */ 1742 + static void drm_test_check_mode_valid(struct kunit *test) 1743 + { 1744 + struct drm_atomic_helper_connector_hdmi_priv *priv; 1745 + struct drm_connector *conn; 1746 + struct drm_display_mode *preferred; 1747 + 1748 + priv = drm_kunit_helper_connector_hdmi_init(test, 1749 + BIT(HDMI_COLORSPACE_RGB), 1750 + 8); 1751 + KUNIT_ASSERT_NOT_NULL(test, priv); 1752 + 1753 + conn = &priv->connector; 1754 + preferred = find_preferred_mode(conn); 1755 + KUNIT_ASSERT_NOT_NULL(test, preferred); 1756 + 1757 + KUNIT_EXPECT_EQ(test, preferred->hdisplay, 1920); 1758 + KUNIT_EXPECT_EQ(test, preferred->vdisplay, 1080); 1759 + KUNIT_EXPECT_EQ(test, preferred->clock, 148500); 1760 + } 1761 + 1762 + /* 1763 + * Test that the drm_hdmi_connector_mode_valid() will reject modes depending on 1764 + * the .tmds_char_rate_valid() behaviour. 1765 + * Pass a correct EDID and verify that high-rate modes are filtered. 1766 + */ 1767 + static void drm_test_check_mode_valid_reject_rate(struct kunit *test) 1768 + { 1769 + struct drm_atomic_helper_connector_hdmi_priv *priv; 1770 + struct drm_connector *conn; 1771 + struct drm_display_mode *preferred; 1772 + int ret; 1773 + 1774 + priv = drm_kunit_helper_connector_hdmi_init_funcs(test, 1775 + BIT(HDMI_COLORSPACE_RGB), 1776 + 8, 1777 + &reject_100_MHz_connector_hdmi_funcs); 1778 + KUNIT_ASSERT_NOT_NULL(test, priv); 1779 + 1780 + conn = &priv->connector; 1781 + 1782 + ret = set_connector_edid(test, conn, 1783 + test_edid_hdmi_1080p_rgb_max_200mhz, 1784 + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); 1785 + KUNIT_ASSERT_GT(test, ret, 0); 1786 + 1787 + /* 1788 + * Unlike the drm_test_check_mode_valid() here 1080p is rejected, but 1789 + * 480p is allowed. 1790 + */ 1791 + preferred = find_preferred_mode(conn); 1792 + KUNIT_ASSERT_NOT_NULL(test, preferred); 1793 + KUNIT_EXPECT_EQ(test, preferred->hdisplay, 640); 1794 + KUNIT_EXPECT_EQ(test, preferred->vdisplay, 480); 1795 + KUNIT_EXPECT_EQ(test, preferred->clock, 25200); 1796 + } 1797 + 1798 + /* 1799 + * Test that the drm_hdmi_connector_mode_valid() will not mark any modes as 1800 + * valid if .tmds_char_rate_valid() rejects all of them. Pass a correct EDID 1801 + * and verify that there is no preferred mode and no modes were set for the 1802 + * connector. 1803 + */ 1804 + static void drm_test_check_mode_valid_reject(struct kunit *test) 1805 + { 1806 + struct drm_atomic_helper_connector_hdmi_priv *priv; 1807 + struct drm_connector *conn; 1808 + struct drm_display_mode *preferred; 1809 + int ret; 1810 + 1811 + priv = drm_kunit_helper_connector_hdmi_init_funcs(test, 1812 + BIT(HDMI_COLORSPACE_RGB), 1813 + 8, 1814 + &reject_connector_hdmi_funcs); 1815 + KUNIT_ASSERT_NOT_NULL(test, priv); 1816 + 1817 + conn = &priv->connector; 1818 + 1819 + /* should reject all modes */ 1820 + ret = set_connector_edid(test, conn, 1821 + test_edid_hdmi_1080p_rgb_max_200mhz, 1822 + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_200mhz)); 1823 + KUNIT_ASSERT_EQ(test, ret, 0); 1824 + 1825 + preferred = find_preferred_mode(conn); 1826 + KUNIT_ASSERT_NULL(test, preferred); 1827 + } 1828 + 1829 + /* 1830 + * Test that the drm_hdmi_connector_mode_valid() will reject modes that don't 1831 + * pass the info.max_tmds_clock filter. Pass crafted EDID and verify that 1832 + * high-rate modes are filtered. 1833 + */ 1834 + static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test) 1835 + { 1836 + struct drm_atomic_helper_connector_hdmi_priv *priv; 1837 + struct drm_connector *conn; 1838 + struct drm_display_mode *preferred; 1839 + int ret; 1840 + 1841 + priv = drm_kunit_helper_connector_hdmi_init(test, 1842 + BIT(HDMI_COLORSPACE_RGB), 1843 + 8); 1844 + KUNIT_ASSERT_NOT_NULL(test, priv); 1845 + 1846 + conn = &priv->connector; 1847 + 1848 + ret = set_connector_edid(test, conn, 1849 + test_edid_hdmi_1080p_rgb_max_100mhz, 1850 + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_max_100mhz)); 1851 + KUNIT_ASSERT_GT(test, ret, 0); 1852 + 1853 + KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 100 * 1000); 1854 + 1855 + preferred = find_preferred_mode(conn); 1856 + KUNIT_ASSERT_NOT_NULL(test, preferred); 1857 + KUNIT_EXPECT_EQ(test, preferred->hdisplay, 640); 1858 + KUNIT_EXPECT_EQ(test, preferred->vdisplay, 480); 1859 + KUNIT_EXPECT_EQ(test, preferred->clock, 25200); 1860 + } 1861 + 1862 + static struct kunit_case drm_atomic_helper_connector_hdmi_mode_valid_tests[] = { 1863 + KUNIT_CASE(drm_test_check_mode_valid), 1864 + KUNIT_CASE(drm_test_check_mode_valid_reject), 1865 + KUNIT_CASE(drm_test_check_mode_valid_reject_rate), 1866 + KUNIT_CASE(drm_test_check_mode_valid_reject_max_clock), 1867 + { } 1868 + }; 1869 + 1870 + static struct kunit_suite drm_atomic_helper_connector_hdmi_mode_valid_test_suite = { 1871 + .name = "drm_atomic_helper_connector_hdmi_mode_valid", 1872 + .test_cases = drm_atomic_helper_connector_hdmi_mode_valid_tests, 1873 + }; 1874 + 1767 1875 kunit_test_suites( 1768 1876 &drm_atomic_helper_connector_hdmi_check_test_suite, 1769 1877 &drm_atomic_helper_connector_hdmi_reset_test_suite, 1878 + &drm_atomic_helper_connector_hdmi_mode_valid_test_suite, 1770 1879 ); 1771 1880 1772 1881 MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
+102
drivers/gpu/drm/tests/drm_kunit_edid.h
··· 74 74 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 75 75 * 76 76 * 02 03 1b 81 e3 05 00 20 41 10 e2 00 4a 6d 03 0c 77 + * 00 12 34 00 14 20 00 00 00 00 00 00 00 00 00 00 78 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 79 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 81 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 82 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 83 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e4 84 + * 85 + * ---------------- 86 + * 87 + * Block 0, Base EDID: 88 + * EDID Structure Version & Revision: 1.3 89 + * Vendor & Product Identification: 90 + * Manufacturer: LNX 91 + * Model: 42 92 + * Made in: 2023 93 + * Basic Display Parameters & Features: 94 + * Digital display 95 + * DFP 1.x compatible TMDS 96 + * Maximum image size: 160 cm x 90 cm 97 + * Gamma: 2.20 98 + * Monochrome or grayscale display 99 + * First detailed timing is the preferred timing 100 + * Color Characteristics: 101 + * Red : 0.0000, 0.0000 102 + * Green: 0.0000, 0.0000 103 + * Blue : 0.0000, 0.0000 104 + * White: 0.0000, 0.0000 105 + * Established Timings I & II: 106 + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz 107 + * Standard Timings: none 108 + * Detailed Timing Descriptors: 109 + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) 110 + * Hfront 88 Hsync 44 Hback 148 Hpol P 111 + * Vfront 4 Vsync 5 Vback 36 Vpol P 112 + * Display Product Name: 'Test EDID' 113 + * Display Range Limits: 114 + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz 115 + * Dummy Descriptor: 116 + * Extension blocks: 1 117 + * Checksum: 0x92 118 + * 119 + * ---------------- 120 + * 121 + * Block 1, CTA-861 Extension Block: 122 + * Revision: 3 123 + * Underscans IT Video Formats by default 124 + * Native detailed modes: 1 125 + * Colorimetry Data Block: 126 + * sRGB 127 + * Video Data Block: 128 + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz 129 + * Video Capability Data Block: 130 + * YCbCr quantization: No Data 131 + * RGB quantization: Selectable (via AVI Q) 132 + * PT scan behavior: No Data 133 + * IT scan behavior: Always Underscanned 134 + * CE scan behavior: Always Underscanned 135 + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: 136 + * Source physical address: 1.2.3.4 137 + * Maximum TMDS clock: 100 MHz 138 + * Extended HDMI video details: 139 + * Checksum: 0xe4 Unused space in Extension Block: 100 bytes 140 + */ 141 + static const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[] = { 142 + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, 143 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, 144 + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 145 + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 146 + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 147 + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, 148 + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, 149 + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, 150 + 0x46, 0x00, 0x00, 0xc4, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 151 + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 152 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x02, 0x03, 0x1b, 0x81, 153 + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x6d, 0x03, 0x0c, 154 + 0x00, 0x12, 0x34, 0x00, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 158 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 160 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 161 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 162 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 + 0x00, 0x00, 0x00, 0xe4 164 + }; 165 + 166 + /* 167 + * edid-decode (hex): 168 + * 169 + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 170 + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 171 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 172 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c 173 + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 174 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 175 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 176 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 177 + * 178 + * 02 03 1b 81 e3 05 00 20 41 10 e2 00 4a 6d 03 0c 77 179 * 00 12 34 00 28 20 00 00 00 00 00 00 00 00 00 00 78 180 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 79 181 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+1
drivers/gpu/drm/tiny/panel-mipi-dbi.c
··· 10 10 #include <linux/firmware.h> 11 11 #include <linux/gpio/consumer.h> 12 12 #include <linux/module.h> 13 + #include <linux/of.h> 13 14 #include <linux/property.h> 14 15 #include <linux/regulator/consumer.h> 15 16 #include <linux/spi/spi.h>
-4
drivers/gpu/drm/v3d/v3d_bo.c
··· 13 13 * Display engines requiring physically contiguous allocations should 14 14 * look into Mesa's "renderonly" support (as used by the Mesa pl111 15 15 * driver) for an example of how to integrate with V3D. 16 - * 17 - * Long term, we should support evicting pages from the MMU when under 18 - * memory pressure (thus the v3d_bo_get_pages() refcounting), but 19 - * that's not a high priority since our systems tend to not have swap. 20 16 */ 21 17 22 18 #include <linux/dma-buf.h>
+4 -4
drivers/gpu/drm/v3d/v3d_mmu.c
··· 4 4 /** 5 5 * DOC: Broadcom V3D MMU 6 6 * 7 - * The V3D 3.x hardware (compared to VC4) now includes an MMU. It has 7 + * The V3D 3.x hardware (compared to VC4) now includes an MMU. It has 8 8 * a single level of page tables for the V3D's 4GB address space to 9 9 * map to AXI bus addresses, thus it could need up to 4MB of 10 10 * physically contiguous memory to store the PTEs. ··· 15 15 * 16 16 * To protect clients from each other, we should use the GMP to 17 17 * quickly mask out (at 128kb granularity) what pages are available to 18 - * each client. This is not yet implemented. 18 + * each client. This is not yet implemented. 19 19 */ 20 20 21 21 #include "v3d_drv.h" 22 22 #include "v3d_regs.h" 23 23 24 - /* Note: All PTEs for the 1MB superpage must be filled with the 25 - * superpage bit set. 24 + /* Note: All PTEs for the 64KB bigpage or 1MB superpage must be filled 25 + * with the bigpage/superpage bit set. 26 26 */ 27 27 #define V3D_PTE_SUPERPAGE BIT(31) 28 28 #define V3D_PTE_BIGPAGE BIT(30)
+5 -7
drivers/gpu/drm/v3d/v3d_performance_counters.h
··· 2 2 /* 3 3 * Copyright (C) 2024 Raspberry Pi 4 4 */ 5 + 5 6 #ifndef V3D_PERFORMANCE_COUNTERS_H 6 7 #define V3D_PERFORMANCE_COUNTERS_H 7 8 8 - /* Holds a description of a given performance counter. The index of performance 9 - * counter is given by the array on v3d_performance_counter.h 9 + /* Holds a description of a given performance counter. The index of 10 + * performance counter is given by the array on `v3d_performance_counter.c`. 10 11 */ 11 12 struct v3d_perf_counter_desc { 12 13 /* Category of the counter */ ··· 21 20 }; 22 21 23 22 struct v3d_perfmon_info { 24 - /* 25 - * Different revisions of V3D have different total number of 23 + /* Different revisions of V3D have different total number of 26 24 * performance counters. 27 25 */ 28 26 unsigned int max_counters; 29 27 30 - /* 31 - * Array of counters valid for the platform. 32 - */ 28 + /* Array of counters valid for the platform. */ 33 29 const struct v3d_perf_counter_desc *counters; 34 30 }; 35 31
+6 -6
drivers/gpu/drm/v3d/v3d_sched.c
··· 5 5 * DOC: Broadcom V3D scheduling 6 6 * 7 7 * The shared DRM GPU scheduler is used to coordinate submitting jobs 8 - * to the hardware. Each DRM fd (roughly a client process) gets its 9 - * own scheduler entity, which will process jobs in order. The GPU 10 - * scheduler will round-robin between clients to submit the next job. 8 + * to the hardware. Each DRM fd (roughly a client process) gets its 9 + * own scheduler entity, which will process jobs in order. The GPU 10 + * scheduler will schedule the clients with a FIFO scheduling algorithm. 11 11 * 12 12 * For simplicity, and in order to keep latency low for interactive 13 13 * jobs when bulk background jobs are queued up, we submit a new job 14 14 * to the HW only when it has completed the last one, instead of 15 - * filling up the CT[01]Q FIFOs with jobs. Similarly, we use 16 - * drm_sched_job_add_dependency() to manage the dependency between bin and 17 - * render, instead of having the clients submit jobs using the HW's 15 + * filling up the CT[01]Q FIFOs with jobs. Similarly, we use 16 + * `drm_sched_job_add_dependency()` to manage the dependency between bin 17 + * and render, instead of having the clients submit jobs using the HW's 18 18 * semaphores to interlock between them. 19 19 */ 20 20
+5 -4
drivers/gpu/drm/v3d/v3d_submit.c
··· 11 11 #include "v3d_trace.h" 12 12 13 13 /* Takes the reservation lock on all the BOs being referenced, so that 14 - * at queue submit time we can update the reservations. 14 + * we can attach fences and update the reservations after pushing the job 15 + * to the queue. 15 16 * 16 17 * We don't lock the RCL the tile alloc/state BOs, or overflow memory 17 - * (all of which are on exec->unref_list). They're entirely private 18 + * (all of which are on render->unref_list). They're entirely private 18 19 * to v3d, so we don't attach dma-buf fences to them. 19 20 */ 20 21 static int ··· 56 55 * @bo_count: Number of GEM handles passed in 57 56 * 58 57 * The command validator needs to reference BOs by their index within 59 - * the submitted job's BO list. This does the validation of the job's 58 + * the submitted job's BO list. This does the validation of the job's 60 59 * BO list and reference counting for the lifetime of the job. 61 60 * 62 61 * Note that this function doesn't need to unreference the BOs on 63 - * failure, because that will happen at v3d_exec_cleanup() time. 62 + * failure, because that will happen at `v3d_job_free()`. 64 63 */ 65 64 static int 66 65 v3d_lookup_bos(struct drm_device *dev,
+4 -5
drivers/gpu/drm/vc4/vc4_hdmi.c
··· 580 580 .detect_ctx = vc4_hdmi_connector_detect_ctx, 581 581 .get_modes = vc4_hdmi_connector_get_modes, 582 582 .atomic_check = vc4_hdmi_connector_atomic_check, 583 + .mode_valid = drm_hdmi_connector_mode_valid, 583 584 }; 584 585 585 586 static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs; ··· 1766 1765 const struct drm_display_mode *mode) 1767 1766 { 1768 1767 struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); 1769 - unsigned long long rate; 1770 1768 1771 1769 if (vc4_hdmi->variant->unsupported_odd_h_timings && 1772 1770 !(mode->flags & DRM_MODE_FLAG_DBLCLK) && ··· 1773 1773 (mode->hsync_end % 2) || (mode->htotal % 2))) 1774 1774 return MODE_H_ILLEGAL; 1775 1775 1776 - rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); 1777 - return vc4_hdmi_connector_clock_valid(&vc4_hdmi->connector, mode, rate); 1776 + return MODE_OK; 1778 1777 } 1779 1778 1780 1779 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { ··· 2220 2221 struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); 2221 2222 struct drm_connector *connector = &vc4_hdmi->connector; 2222 2223 2223 - mutex_lock(&vc4_hdmi->mutex); 2224 + mutex_lock(&connector->eld_mutex); 2224 2225 memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); 2225 - mutex_unlock(&vc4_hdmi->mutex); 2226 + mutex_unlock(&connector->eld_mutex); 2226 2227 2227 2228 return 0; 2228 2229 }
+1 -1
drivers/gpu/drm/vc4/vc4_hvs.c
··· 522 522 break; 523 523 default: 524 524 drm_err(drm, "Unknown VC4 generation: %d", vc4->gen); 525 - return 0; 525 + break; 526 526 } 527 527 528 528 drm_dev_exit(idx);
+4 -1
drivers/gpu/drm/vc4/vc4_plane.c
··· 368 368 { 369 369 struct vc4_plane_state *vc4_state; 370 370 371 - WARN_ON(plane->state); 371 + if (plane->state) 372 + __drm_atomic_helper_plane_destroy_state(plane->state); 373 + 374 + kfree(plane->state); 372 375 373 376 vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); 374 377 if (!vc4_state)
+9
drivers/gpu/drm/xlnx/Kconfig
··· 17 17 This is a DRM/KMS driver for ZynqMP DisplayPort controller. Choose 18 18 this option if you have a Xilinx ZynqMP SoC with DisplayPort 19 19 subsystem. 20 + 21 + config DRM_ZYNQMP_DPSUB_AUDIO 22 + bool "ZynqMP DisplayPort Audio Support" 23 + depends on DRM_ZYNQMP_DPSUB 24 + depends on SND && SND_SOC 25 + select SND_SOC_GENERIC_DMAENGINE_PCM 26 + help 27 + Choose this option to enable DisplayPort audio support in the ZynqMP 28 + DisplayPort driver.
+1
drivers/gpu/drm/xlnx/Makefile
··· 1 1 zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o zynqmp_kms.o 2 + zynqmp-dpsub-$(CONFIG_DRM_ZYNQMP_DPSUB_AUDIO) += zynqmp_dp_audio.o 2 3 obj-$(CONFIG_DRM_ZYNQMP_DPSUB) += zynqmp-dpsub.o
-48
drivers/gpu/drm/xlnx/zynqmp_disp.c
··· 143 143 * @dpsub: Display subsystem 144 144 * @blend: Register I/O base address for the blender 145 145 * @avbuf: Register I/O base address for the audio/video buffer manager 146 - * @audio: Registers I/O base address for the audio mixer 147 146 * @layers: Layers (planes) 148 147 */ 149 148 struct zynqmp_disp { ··· 151 152 152 153 void __iomem *blend; 153 154 void __iomem *avbuf; 154 - void __iomem *audio; 155 155 156 156 struct zynqmp_disp_layer layers[ZYNQMP_DPSUB_NUM_LAYERS]; 157 157 }; ··· 864 866 } 865 867 866 868 /* ----------------------------------------------------------------------------- 867 - * Audio Mixer 868 - */ 869 - 870 - static void zynqmp_disp_audio_write(struct zynqmp_disp *disp, int reg, u32 val) 871 - { 872 - writel(val, disp->audio + reg); 873 - } 874 - 875 - /** 876 - * zynqmp_disp_audio_enable - Enable the audio mixer 877 - * @disp: Display controller 878 - * 879 - * Enable the audio mixer by de-asserting the soft reset. The audio state is set to 880 - * default values by the reset, set the default mixer volume explicitly. 881 - */ 882 - static void zynqmp_disp_audio_enable(struct zynqmp_disp *disp) 883 - { 884 - /* Clear the audio soft reset register as it's an non-reset flop. */ 885 - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, 0); 886 - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_MIXER_VOLUME, 887 - ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); 888 - } 889 - 890 - /** 891 - * zynqmp_disp_audio_disable - Disable the audio mixer 892 - * @disp: Display controller 893 - * 894 - * Disable the audio mixer by asserting its soft reset. 895 - */ 896 - static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp) 897 - { 898 - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, 899 - ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); 900 - } 901 - 902 - /* ----------------------------------------------------------------------------- 903 869 * ZynqMP Display Layer & DRM Plane 904 870 */ 905 871 ··· 1303 1341 disp->dpsub->vid_clk_from_ps); 1304 1342 zynqmp_disp_avbuf_enable_channels(disp); 1305 1343 zynqmp_disp_avbuf_enable_audio(disp); 1306 - 1307 - zynqmp_disp_audio_enable(disp); 1308 1344 } 1309 1345 1310 1346 /** ··· 1311 1351 */ 1312 1352 void zynqmp_disp_disable(struct zynqmp_disp *disp) 1313 1353 { 1314 - zynqmp_disp_audio_disable(disp); 1315 - 1316 1354 zynqmp_disp_avbuf_disable_audio(disp); 1317 1355 zynqmp_disp_avbuf_disable_channels(disp); 1318 1356 zynqmp_disp_avbuf_disable(disp); ··· 1376 1418 disp->avbuf = devm_platform_ioremap_resource_byname(pdev, "av_buf"); 1377 1419 if (IS_ERR(disp->avbuf)) { 1378 1420 ret = PTR_ERR(disp->avbuf); 1379 - goto error; 1380 - } 1381 - 1382 - disp->audio = devm_platform_ioremap_resource_byname(pdev, "aud"); 1383 - if (IS_ERR(disp->audio)) { 1384 - ret = PTR_ERR(disp->audio); 1385 1421 goto error; 1386 1422 } 1387 1423
+1 -6
drivers/gpu/drm/xlnx/zynqmp_disp_regs.h
··· 177 177 #define ZYNQMP_DISP_AUD_MIXER_VOLUME 0x0 178 178 #define ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE 0x20002000 179 179 #define ZYNQMP_DISP_AUD_MIXER_META_DATA 0x4 180 - #define ZYNQMP_DISP_AUD_CH_STATUS0 0x8 181 - #define ZYNQMP_DISP_AUD_CH_STATUS1 0xc 182 - #define ZYNQMP_DISP_AUD_CH_STATUS2 0x10 183 - #define ZYNQMP_DISP_AUD_CH_STATUS3 0x14 184 - #define ZYNQMP_DISP_AUD_CH_STATUS4 0x18 185 - #define ZYNQMP_DISP_AUD_CH_STATUS5 0x1c 180 + #define ZYNQMP_DISP_AUD_CH_STATUS(x) (0x8 + ((x) * 4)) 186 181 #define ZYNQMP_DISP_AUD_CH_A_DATA0 0x20 187 182 #define ZYNQMP_DISP_AUD_CH_A_DATA1 0x24 188 183 #define ZYNQMP_DISP_AUD_CH_A_DATA2 0x28
+40 -16
drivers/gpu/drm/xlnx/zynqmp_dp.c
··· 1342 1342 { 1343 1343 u8 lane_cnt = dp->mode.lane_cnt; 1344 1344 u32 reg, wpl; 1345 - unsigned int rate; 1346 1345 1347 1346 zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HTOTAL, mode->htotal); 1348 1347 zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VTOTAL, mode->vtotal); ··· 1366 1367 reg = drm_dp_bw_code_to_link_rate(dp->mode.bw_code); 1367 1368 zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_N_VID, reg); 1368 1369 zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_M_VID, mode->clock); 1369 - rate = zynqmp_dpsub_get_audio_clk_rate(dp->dpsub); 1370 - if (rate) { 1371 - dev_dbg(dp->dev, "Audio rate: %d\n", rate / 512); 1372 - zynqmp_dp_write(dp, ZYNQMP_DP_TX_N_AUD, reg); 1373 - zynqmp_dp_write(dp, ZYNQMP_DP_TX_M_AUD, rate / 1000); 1374 - } 1375 1370 } 1376 - 1377 - /* Only 2 channel audio is supported now */ 1378 - if (zynqmp_dpsub_audio_enabled(dp->dpsub)) 1379 - zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CHANNELS, 1); 1380 1371 1381 1372 zynqmp_dp_write(dp, ZYNQMP_DP_USER_PIX_WIDTH, 1); 1382 1373 ··· 1374 1385 wpl = (mode->hdisplay * dp->config.bpp + 15) / 16; 1375 1386 reg = wpl + wpl % lane_cnt - lane_cnt; 1376 1387 zynqmp_dp_write(dp, ZYNQMP_DP_USER_DATA_COUNT_PER_LANE, reg); 1388 + } 1389 + 1390 + /* ----------------------------------------------------------------------------- 1391 + * Audio 1392 + */ 1393 + 1394 + void zynqmp_dp_audio_set_channels(struct zynqmp_dp *dp, 1395 + unsigned int num_channels) 1396 + { 1397 + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CHANNELS, num_channels - 1); 1398 + } 1399 + 1400 + void zynqmp_dp_audio_enable(struct zynqmp_dp *dp) 1401 + { 1402 + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1); 1403 + } 1404 + 1405 + void zynqmp_dp_audio_disable(struct zynqmp_dp *dp) 1406 + { 1407 + zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); 1408 + } 1409 + 1410 + void zynqmp_dp_audio_write_n_m(struct zynqmp_dp *dp) 1411 + { 1412 + unsigned int rate; 1413 + u32 link_rate; 1414 + 1415 + if (!(dp->config.misc0 & ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK)) 1416 + return; 1417 + 1418 + link_rate = drm_dp_bw_code_to_link_rate(dp->mode.bw_code); 1419 + 1420 + rate = clk_get_rate(dp->dpsub->aud_clk); 1421 + 1422 + dev_dbg(dp->dev, "Audio rate: %d\n", rate / 512); 1423 + 1424 + zynqmp_dp_write(dp, ZYNQMP_DP_TX_N_AUD, link_rate); 1425 + zynqmp_dp_write(dp, ZYNQMP_DP_TX_M_AUD, rate / 1000); 1377 1426 } 1378 1427 1379 1428 /* ----------------------------------------------------------------------------- ··· 1604 1577 /* Enable the encoder */ 1605 1578 dp->enabled = true; 1606 1579 zynqmp_dp_update_misc(dp); 1607 - if (zynqmp_dpsub_audio_enabled(dp->dpsub)) 1608 - zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1); 1580 + 1609 1581 zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0); 1610 1582 if (dp->status == connector_status_connected) { 1611 1583 for (i = 0; i < 3; i++) { ··· 1639 1613 drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3); 1640 1614 zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 1641 1615 ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL); 1642 - if (zynqmp_dpsub_audio_enabled(dp->dpsub)) 1643 - zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); 1644 1616 1645 1617 zynqmp_dp_disp_disable(dp, old_bridge_state); 1646 1618 mutex_unlock(&dp->lock); ··· 2214 2190 struct zynqmp_dp *dp = data; 2215 2191 2216 2192 mutex_lock(&dp->lock); 2217 - *val = drm_dp_bw_code_to_link_rate(dp->test.bw_code) * 10000; 2193 + *val = drm_dp_bw_code_to_link_rate(dp->test.bw_code) * 10000ULL; 2218 2194 mutex_unlock(&dp->lock); 2219 2195 return 0; 2220 2196 }
+7
drivers/gpu/drm/xlnx/zynqmp_dp.h
··· 22 22 int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub); 23 23 void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub); 24 24 25 + void zynqmp_dp_audio_set_channels(struct zynqmp_dp *dp, 26 + unsigned int num_channels); 27 + void zynqmp_dp_audio_enable(struct zynqmp_dp *dp); 28 + void zynqmp_dp_audio_disable(struct zynqmp_dp *dp); 29 + 30 + void zynqmp_dp_audio_write_n_m(struct zynqmp_dp *dp); 31 + 25 32 #endif /* _ZYNQMP_DP_H_ */
+447
drivers/gpu/drm/xlnx/zynqmp_dp_audio.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * ZynqMP DisplayPort Subsystem Driver - Audio support 4 + * 5 + * Copyright (C) 2015 - 2024 Xilinx, Inc. 6 + * 7 + * Authors: 8 + * - Hyun Woo Kwon <hyun.kwon@xilinx.com> 9 + * - Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> 10 + */ 11 + 12 + #include <linux/clk.h> 13 + #include <linux/device.h> 14 + #include <linux/mutex.h> 15 + #include <linux/pm_runtime.h> 16 + 17 + #include <sound/asoundef.h> 18 + #include <sound/core.h> 19 + #include <sound/dmaengine_pcm.h> 20 + #include <sound/initval.h> 21 + #include <sound/pcm.h> 22 + #include <sound/soc.h> 23 + #include <sound/tlv.h> 24 + 25 + #include "zynqmp_disp_regs.h" 26 + #include "zynqmp_dp.h" 27 + #include "zynqmp_dpsub.h" 28 + 29 + #define ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK 512 30 + #define ZYNQMP_NUM_PCMS 2 31 + 32 + struct zynqmp_dpsub_audio { 33 + void __iomem *base; 34 + 35 + struct snd_soc_card card; 36 + 37 + const char *dai_name; 38 + const char *link_names[ZYNQMP_NUM_PCMS]; 39 + const char *pcm_names[ZYNQMP_NUM_PCMS]; 40 + 41 + struct snd_soc_dai_driver dai_driver; 42 + struct snd_dmaengine_pcm_config pcm_configs[2]; 43 + 44 + struct snd_soc_dai_link links[ZYNQMP_NUM_PCMS]; 45 + 46 + struct { 47 + struct snd_soc_dai_link_component cpu; 48 + struct snd_soc_dai_link_component codec; 49 + struct snd_soc_dai_link_component platform; 50 + } components[ZYNQMP_NUM_PCMS]; 51 + 52 + /* 53 + * Protects: 54 + * - enabled_streams 55 + * - volumes 56 + * - current_rate 57 + */ 58 + struct mutex enable_lock; 59 + 60 + u32 enabled_streams; 61 + u32 current_rate; 62 + 63 + u16 volumes[2]; 64 + }; 65 + 66 + static const struct snd_pcm_hardware zynqmp_dp_pcm_hw = { 67 + .info = SNDRV_PCM_INFO_MMAP | 68 + SNDRV_PCM_INFO_MMAP_VALID | 69 + SNDRV_PCM_INFO_INTERLEAVED | 70 + SNDRV_PCM_INFO_PAUSE | 71 + SNDRV_PCM_INFO_RESUME | 72 + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, 73 + 74 + .buffer_bytes_max = 128 * 1024, 75 + .period_bytes_min = 256, 76 + .period_bytes_max = 1024 * 1024, 77 + .periods_min = 2, 78 + .periods_max = 256, 79 + }; 80 + 81 + static int zynqmp_dp_startup(struct snd_pcm_substream *substream) 82 + { 83 + struct snd_pcm_runtime *runtime = substream->runtime; 84 + 85 + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 86 + 256); 87 + 88 + return 0; 89 + } 90 + 91 + static const struct snd_soc_ops zynqmp_dp_ops = { 92 + .startup = zynqmp_dp_startup, 93 + }; 94 + 95 + static void zynqmp_dp_audio_write(struct zynqmp_dpsub_audio *audio, int reg, 96 + u32 val) 97 + { 98 + writel(val, audio->base + reg); 99 + } 100 + 101 + static int dp_dai_hw_params(struct snd_pcm_substream *substream, 102 + struct snd_pcm_hw_params *params, 103 + struct snd_soc_dai *socdai) 104 + { 105 + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 106 + struct zynqmp_dpsub *dpsub = 107 + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); 108 + struct zynqmp_dpsub_audio *audio = dpsub->audio; 109 + int ret; 110 + u32 sample_rate; 111 + struct snd_aes_iec958 iec = { 0 }; 112 + unsigned long rate; 113 + 114 + sample_rate = params_rate(params); 115 + 116 + if (sample_rate != 48000 && sample_rate != 44100) 117 + return -EINVAL; 118 + 119 + guard(mutex)(&audio->enable_lock); 120 + 121 + if (audio->enabled_streams && audio->current_rate != sample_rate) { 122 + dev_err(dpsub->dev, 123 + "Can't change rate while playback enabled\n"); 124 + return -EINVAL; 125 + } 126 + 127 + if (audio->enabled_streams > 0) { 128 + /* Nothing to do */ 129 + audio->enabled_streams++; 130 + return 0; 131 + } 132 + 133 + audio->current_rate = sample_rate; 134 + 135 + /* Note: clock rate can only be changed if the clock is disabled */ 136 + ret = clk_set_rate(dpsub->aud_clk, 137 + sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK); 138 + if (ret) { 139 + dev_err(dpsub->dev, "can't set aud_clk to %u err:%d\n", 140 + sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK, ret); 141 + return ret; 142 + } 143 + 144 + clk_prepare_enable(dpsub->aud_clk); 145 + 146 + rate = clk_get_rate(dpsub->aud_clk); 147 + 148 + /* Ignore some offset +- 10 */ 149 + if (abs(sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK - rate) > 10) { 150 + dev_err(dpsub->dev, "aud_clk offset is higher: %ld\n", 151 + sample_rate * ZYNQMP_DISP_AUD_SMPL_RATE_TO_CLK - rate); 152 + clk_disable_unprepare(dpsub->aud_clk); 153 + return -EINVAL; 154 + } 155 + 156 + pm_runtime_get_sync(dpsub->dev); 157 + 158 + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_MIXER_VOLUME, 159 + audio->volumes[0] | (audio->volumes[1] << 16)); 160 + 161 + /* Clear the audio soft reset register as it's an non-reset flop. */ 162 + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, 0); 163 + 164 + /* Only 2 channel audio is supported now */ 165 + zynqmp_dp_audio_set_channels(dpsub->dp, 2); 166 + 167 + zynqmp_dp_audio_write_n_m(dpsub->dp); 168 + 169 + /* Channel status */ 170 + 171 + if (sample_rate == 48000) 172 + iec.status[3] = IEC958_AES3_CON_FS_48000; 173 + else 174 + iec.status[3] = IEC958_AES3_CON_FS_44100; 175 + 176 + for (unsigned int i = 0; i < AES_IEC958_STATUS_SIZE / 4; ++i) { 177 + u32 v; 178 + 179 + v = (iec.status[(i * 4) + 0] << 0) | 180 + (iec.status[(i * 4) + 1] << 8) | 181 + (iec.status[(i * 4) + 2] << 16) | 182 + (iec.status[(i * 4) + 3] << 24); 183 + 184 + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_CH_STATUS(i), v); 185 + } 186 + 187 + zynqmp_dp_audio_enable(dpsub->dp); 188 + 189 + audio->enabled_streams++; 190 + 191 + return 0; 192 + } 193 + 194 + static int dp_dai_hw_free(struct snd_pcm_substream *substream, 195 + struct snd_soc_dai *socdai) 196 + { 197 + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 198 + struct zynqmp_dpsub *dpsub = 199 + snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); 200 + struct zynqmp_dpsub_audio *audio = dpsub->audio; 201 + 202 + guard(mutex)(&audio->enable_lock); 203 + 204 + /* Nothing to do */ 205 + if (audio->enabled_streams > 1) { 206 + audio->enabled_streams--; 207 + return 0; 208 + } 209 + 210 + pm_runtime_put(dpsub->dev); 211 + 212 + zynqmp_dp_audio_disable(dpsub->dp); 213 + 214 + /* 215 + * Reset doesn't work. If we assert reset between audio stop and start, 216 + * the audio won't start anymore. Probably we are missing writing 217 + * some audio related registers. A/B buf? 218 + */ 219 + /* 220 + zynqmp_disp_audio_write(audio, ZYNQMP_DISP_AUD_SOFT_RESET, 221 + ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); 222 + */ 223 + 224 + clk_disable_unprepare(dpsub->aud_clk); 225 + 226 + audio->current_rate = 0; 227 + audio->enabled_streams--; 228 + 229 + return 0; 230 + } 231 + 232 + static const struct snd_soc_dai_ops zynqmp_dp_dai_ops = { 233 + .hw_params = dp_dai_hw_params, 234 + .hw_free = dp_dai_hw_free, 235 + }; 236 + 237 + /* 238 + * Min = 10 * log10(0x1 / 0x2000) = -39.13 239 + * Max = 10 * log10(0xffffff / 0x2000) = 9.03 240 + */ 241 + static const DECLARE_TLV_DB_RANGE(zynqmp_dp_tlv, 242 + 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, -3913, 1), 243 + 0x1, 0x2000, TLV_DB_LINEAR_ITEM(-3913, 0), 244 + 0x2000, 0xffff, TLV_DB_LINEAR_ITEM(0, 903), 245 + ); 246 + 247 + static const struct snd_kcontrol_new zynqmp_dp_snd_controls[] = { 248 + SOC_SINGLE_TLV("Input0 Playback Volume", 0, 249 + 0, 0xffff, 0, zynqmp_dp_tlv), 250 + SOC_SINGLE_TLV("Input1 Playback Volume", 1, 251 + 0, 0xffff, 0, zynqmp_dp_tlv), 252 + }; 253 + 254 + /* 255 + * Note: these read & write functions only support two "registers", 0 and 1, 256 + * for volume 0 and 1. In other words, these are not real register read/write 257 + * functions. 258 + * 259 + * This is done to support caching the volume value for the case where the 260 + * hardware is not enabled, and also to support locking as volumes 0 and 1 261 + * are in the same register. 262 + */ 263 + static unsigned int zynqmp_dp_dai_read(struct snd_soc_component *component, 264 + unsigned int reg) 265 + { 266 + struct zynqmp_dpsub *dpsub = dev_get_drvdata(component->dev); 267 + struct zynqmp_dpsub_audio *audio = dpsub->audio; 268 + 269 + return audio->volumes[reg]; 270 + } 271 + 272 + static int zynqmp_dp_dai_write(struct snd_soc_component *component, 273 + unsigned int reg, unsigned int val) 274 + { 275 + struct zynqmp_dpsub *dpsub = dev_get_drvdata(component->dev); 276 + struct zynqmp_dpsub_audio *audio = dpsub->audio; 277 + 278 + guard(mutex)(&audio->enable_lock); 279 + 280 + audio->volumes[reg] = val; 281 + 282 + if (audio->enabled_streams) 283 + zynqmp_dp_audio_write(audio, ZYNQMP_DISP_AUD_MIXER_VOLUME, 284 + audio->volumes[0] | 285 + (audio->volumes[1] << 16)); 286 + 287 + return 0; 288 + } 289 + 290 + static const struct snd_soc_component_driver zynqmp_dp_component_driver = { 291 + .idle_bias_on = 1, 292 + .use_pmdown_time = 1, 293 + .endianness = 1, 294 + .controls = zynqmp_dp_snd_controls, 295 + .num_controls = ARRAY_SIZE(zynqmp_dp_snd_controls), 296 + .read = zynqmp_dp_dai_read, 297 + .write = zynqmp_dp_dai_write, 298 + }; 299 + 300 + int zynqmp_audio_init(struct zynqmp_dpsub *dpsub) 301 + { 302 + struct platform_device *pdev = to_platform_device(dpsub->dev); 303 + struct device *dev = dpsub->dev; 304 + struct zynqmp_dpsub_audio *audio; 305 + struct snd_soc_card *card; 306 + void *dev_data; 307 + int ret; 308 + 309 + if (!dpsub->aud_clk) 310 + return 0; 311 + 312 + audio = devm_kzalloc(dev, sizeof(*audio), GFP_KERNEL); 313 + if (!audio) 314 + return -ENOMEM; 315 + 316 + dpsub->audio = audio; 317 + 318 + mutex_init(&audio->enable_lock); 319 + 320 + /* 0x2000 is the zero level, no change */ 321 + audio->volumes[0] = 0x2000; 322 + audio->volumes[1] = 0x2000; 323 + 324 + audio->dai_name = devm_kasprintf(dev, GFP_KERNEL, 325 + "%s-dai", dev_name(dev)); 326 + 327 + for (unsigned int i = 0; i < ZYNQMP_NUM_PCMS; ++i) { 328 + audio->link_names[i] = devm_kasprintf(dev, GFP_KERNEL, 329 + "%s-dp-%u", dev_name(dev), i); 330 + audio->pcm_names[i] = devm_kasprintf(dev, GFP_KERNEL, 331 + "%s-pcm-%u", dev_name(dev), i); 332 + } 333 + 334 + audio->base = devm_platform_ioremap_resource_byname(pdev, "aud"); 335 + if (IS_ERR(audio->base)) 336 + return PTR_ERR(audio->base); 337 + 338 + /* Create CPU DAI */ 339 + 340 + audio->dai_driver = (struct snd_soc_dai_driver) { 341 + .name = audio->dai_name, 342 + .ops = &zynqmp_dp_dai_ops, 343 + .playback = { 344 + .channels_min = 2, 345 + .channels_max = 2, 346 + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 347 + .formats = SNDRV_PCM_FMTBIT_S16_LE, 348 + }, 349 + }; 350 + 351 + ret = devm_snd_soc_register_component(dev, &zynqmp_dp_component_driver, 352 + &audio->dai_driver, 1); 353 + if (ret) { 354 + dev_err(dev, "Failed to register CPU DAI\n"); 355 + return ret; 356 + } 357 + 358 + /* Create PCMs */ 359 + 360 + for (unsigned int i = 0; i < ZYNQMP_NUM_PCMS; ++i) { 361 + struct snd_dmaengine_pcm_config *pcm_config = 362 + &audio->pcm_configs[i]; 363 + 364 + *pcm_config = (struct snd_dmaengine_pcm_config){ 365 + .name = audio->pcm_names[i], 366 + .pcm_hardware = &zynqmp_dp_pcm_hw, 367 + .prealloc_buffer_size = 64 * 1024, 368 + .chan_names[SNDRV_PCM_STREAM_PLAYBACK] = 369 + i == 0 ? "aud0" : "aud1", 370 + }; 371 + 372 + ret = devm_snd_dmaengine_pcm_register(dev, pcm_config, 0); 373 + if (ret) { 374 + dev_err(dev, "Failed to register PCM %u\n", i); 375 + return ret; 376 + } 377 + } 378 + 379 + /* Create card */ 380 + 381 + card = &audio->card; 382 + card->name = "DisplayPort"; 383 + card->long_name = "DisplayPort Monitor"; 384 + card->driver_name = "zynqmp_dpsub"; 385 + card->dev = dev; 386 + card->owner = THIS_MODULE; 387 + card->num_links = ZYNQMP_NUM_PCMS; 388 + card->dai_link = audio->links; 389 + 390 + for (unsigned int i = 0; i < ZYNQMP_NUM_PCMS; ++i) { 391 + struct snd_soc_dai_link *link = &card->dai_link[i]; 392 + 393 + link->ops = &zynqmp_dp_ops; 394 + 395 + link->name = audio->link_names[i]; 396 + link->stream_name = audio->link_names[i]; 397 + 398 + link->cpus = &audio->components[i].cpu; 399 + link->num_cpus = 1; 400 + link->cpus[0].dai_name = audio->dai_name; 401 + 402 + link->codecs = &audio->components[i].codec; 403 + link->num_codecs = 1; 404 + link->codecs[0].name = "snd-soc-dummy"; 405 + link->codecs[0].dai_name = "snd-soc-dummy-dai"; 406 + 407 + link->platforms = &audio->components[i].platform; 408 + link->num_platforms = 1; 409 + link->platforms[0].name = audio->pcm_names[i]; 410 + } 411 + 412 + /* 413 + * HACK: devm_snd_soc_register_card() overwrites current drvdata 414 + * so we need to hack it back. 415 + */ 416 + dev_data = dev_get_drvdata(dev); 417 + ret = devm_snd_soc_register_card(dev, card); 418 + dev_set_drvdata(dev, dev_data); 419 + if (ret) { 420 + /* 421 + * As older dtbs may not have the audio channel dmas defined, 422 + * instead of returning an error here we'll continue and just 423 + * mark the audio as disabled. 424 + */ 425 + dev_err(dev, "Failed to register sound card, disabling audio support\n"); 426 + 427 + devm_kfree(dev, audio); 428 + dpsub->audio = NULL; 429 + 430 + return 0; 431 + } 432 + 433 + return 0; 434 + } 435 + 436 + void zynqmp_audio_uninit(struct zynqmp_dpsub *dpsub) 437 + { 438 + struct zynqmp_dpsub_audio *audio = dpsub->audio; 439 + 440 + if (!audio) 441 + return; 442 + 443 + if (!dpsub->aud_clk) 444 + return; 445 + 446 + mutex_destroy(&audio->enable_lock); 447 + }
+9 -30
drivers/gpu/drm/xlnx/zynqmp_dpsub.c
··· 57 57 }; 58 58 59 59 /* ----------------------------------------------------------------------------- 60 - * DPSUB Configuration 61 - */ 62 - 63 - /** 64 - * zynqmp_dpsub_audio_enabled - If the audio is enabled 65 - * @dpsub: DisplayPort subsystem 66 - * 67 - * Return if the audio is enabled depending on the audio clock. 68 - * 69 - * Return: true if audio is enabled, or false. 70 - */ 71 - bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub) 72 - { 73 - return !!dpsub->aud_clk; 74 - } 75 - 76 - /** 77 - * zynqmp_dpsub_get_audio_clk_rate - Get the current audio clock rate 78 - * @dpsub: DisplayPort subsystem 79 - * 80 - * Return: the current audio clock rate. 81 - */ 82 - unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub) 83 - { 84 - if (zynqmp_dpsub_audio_enabled(dpsub)) 85 - return 0; 86 - return clk_get_rate(dpsub->aud_clk); 87 - } 88 - 89 - /* ----------------------------------------------------------------------------- 90 60 * Probe & Remove 91 61 */ 92 62 ··· 234 264 goto err_disp; 235 265 } 236 266 267 + ret = zynqmp_audio_init(dpsub); 268 + if (ret) 269 + goto err_drm_cleanup; 270 + 237 271 dev_info(&pdev->dev, "ZynqMP DisplayPort Subsystem driver probed"); 238 272 239 273 return 0; 240 274 275 + err_drm_cleanup: 276 + if (dpsub->drm) 277 + zynqmp_dpsub_drm_cleanup(dpsub); 241 278 err_disp: 242 279 drm_bridge_remove(dpsub->bridge); 243 280 zynqmp_disp_remove(dpsub); ··· 263 286 static void zynqmp_dpsub_remove(struct platform_device *pdev) 264 287 { 265 288 struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev); 289 + 290 + zynqmp_audio_uninit(dpsub); 266 291 267 292 if (dpsub->drm) 268 293 zynqmp_dpsub_drm_cleanup(dpsub);
+13 -2
drivers/gpu/drm/xlnx/zynqmp_dpsub.h
··· 12 12 #ifndef _ZYNQMP_DPSUB_H_ 13 13 #define _ZYNQMP_DPSUB_H_ 14 14 15 + #include <linux/types.h> 16 + 15 17 struct clk; 16 18 struct device; 17 19 struct drm_bridge; ··· 40 38 ZYNQMP_DPSUB_FORMAT_YCRCB422, 41 39 ZYNQMP_DPSUB_FORMAT_YONLY, 42 40 }; 41 + 42 + struct zynqmp_dpsub_audio; 43 43 44 44 /** 45 45 * struct zynqmp_dpsub - ZynqMP DisplayPort Subsystem ··· 81 77 struct zynqmp_dp *dp; 82 78 83 79 unsigned int dma_align; 80 + 81 + struct zynqmp_dpsub_audio *audio; 84 82 }; 85 83 86 - bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub); 87 - unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub); 84 + #ifdef CONFIG_DRM_ZYNQMP_DPSUB_AUDIO 85 + int zynqmp_audio_init(struct zynqmp_dpsub *dpsub); 86 + void zynqmp_audio_uninit(struct zynqmp_dpsub *dpsub); 87 + #else 88 + static inline int zynqmp_audio_init(struct zynqmp_dpsub *dpsub) { return 0; } 89 + static inline void zynqmp_audio_uninit(struct zynqmp_dpsub *dpsub) { } 90 + #endif 88 91 89 92 void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub); 90 93
+95
include/drm/bridge/dw_mipi_dsi2.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (c) 2024, Fuzhou Rockchip Electronics Co., Ltd 4 + * 5 + * Authors: Guochun Huang <hero.huang@rock-chips.com> 6 + * Heiko Stuebner <heiko.stuebner@cherry.de> 7 + */ 8 + 9 + #ifndef __DW_MIPI_DSI2__ 10 + #define __DW_MIPI_DSI2__ 11 + 12 + #include <linux/regmap.h> 13 + #include <linux/types.h> 14 + 15 + #include <drm/drm_atomic.h> 16 + #include <drm/drm_bridge.h> 17 + #include <drm/drm_connector.h> 18 + #include <drm/drm_crtc.h> 19 + #include <drm/drm_modes.h> 20 + 21 + struct drm_display_mode; 22 + struct drm_encoder; 23 + struct dw_mipi_dsi2; 24 + struct mipi_dsi_device; 25 + struct platform_device; 26 + 27 + enum dw_mipi_dsi2_phy_type { 28 + DW_MIPI_DSI2_DPHY, 29 + DW_MIPI_DSI2_CPHY, 30 + }; 31 + 32 + struct dw_mipi_dsi2_phy_iface { 33 + int ppi_width; 34 + enum dw_mipi_dsi2_phy_type phy_type; 35 + }; 36 + 37 + struct dw_mipi_dsi2_phy_timing { 38 + u32 data_hs2lp; 39 + u32 data_lp2hs; 40 + }; 41 + 42 + struct dw_mipi_dsi2_phy_ops { 43 + int (*init)(void *priv_data); 44 + void (*power_on)(void *priv_data); 45 + void (*power_off)(void *priv_data); 46 + void (*get_interface)(void *priv_data, struct dw_mipi_dsi2_phy_iface *iface); 47 + int (*get_lane_mbps)(void *priv_data, 48 + const struct drm_display_mode *mode, 49 + unsigned long mode_flags, u32 lanes, u32 format, 50 + unsigned int *lane_mbps); 51 + int (*get_timing)(void *priv_data, unsigned int lane_mbps, 52 + struct dw_mipi_dsi2_phy_timing *timing); 53 + int (*get_esc_clk_rate)(void *priv_data, unsigned int *esc_clk_rate); 54 + }; 55 + 56 + struct dw_mipi_dsi2_host_ops { 57 + int (*attach)(void *priv_data, 58 + struct mipi_dsi_device *dsi); 59 + int (*detach)(void *priv_data, 60 + struct mipi_dsi_device *dsi); 61 + }; 62 + 63 + struct dw_mipi_dsi2_plat_data { 64 + struct regmap *regmap; 65 + unsigned int max_data_lanes; 66 + 67 + enum drm_mode_status (*mode_valid)(void *priv_data, 68 + const struct drm_display_mode *mode, 69 + unsigned long mode_flags, 70 + u32 lanes, u32 format); 71 + 72 + bool (*mode_fixup)(void *priv_data, const struct drm_display_mode *mode, 73 + struct drm_display_mode *adjusted_mode); 74 + 75 + u32 *(*get_input_bus_fmts)(void *priv_data, 76 + struct drm_bridge *bridge, 77 + struct drm_bridge_state *bridge_state, 78 + struct drm_crtc_state *crtc_state, 79 + struct drm_connector_state *conn_state, 80 + u32 output_fmt, 81 + unsigned int *num_input_fmts); 82 + 83 + const struct dw_mipi_dsi2_phy_ops *phy_ops; 84 + const struct dw_mipi_dsi2_host_ops *host_ops; 85 + 86 + void *priv_data; 87 + }; 88 + 89 + struct dw_mipi_dsi2 *dw_mipi_dsi2_probe(struct platform_device *pdev, 90 + const struct dw_mipi_dsi2_plat_data *plat_data); 91 + void dw_mipi_dsi2_remove(struct dw_mipi_dsi2 *dsi2); 92 + int dw_mipi_dsi2_bind(struct dw_mipi_dsi2 *dsi2, struct drm_encoder *encoder); 93 + void dw_mipi_dsi2_unbind(struct dw_mipi_dsi2 *dsi2); 94 + 95 + #endif /* __DW_MIPI_DSI2__ */
+4
include/drm/display/drm_hdmi_state_helper.h
··· 20 20 int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector, 21 21 struct drm_atomic_state *state); 22 22 23 + enum drm_mode_status 24 + drm_hdmi_connector_mode_valid(struct drm_connector *connector, 25 + struct drm_display_mode *mode); 26 + 23 27 #endif // DRM_HDMI_STATE_HELPER_H_
+10 -1
include/drm/drm_connector.h
··· 2001 2001 struct drm_encoder *encoder; 2002 2002 2003 2003 #define MAX_ELD_BYTES 128 2004 - /** @eld: EDID-like data, if present */ 2004 + /** @eld: EDID-like data, if present, protected by @eld_mutex */ 2005 2005 uint8_t eld[MAX_ELD_BYTES]; 2006 + /** @eld_mutex: protection for concurrenct access to @eld */ 2007 + struct mutex eld_mutex; 2008 + 2006 2009 /** @latency_present: AV delay info from ELD, if found */ 2007 2010 bool latency_present[2]; 2008 2011 /** ··· 2129 2126 struct drm_connector *connector, 2130 2127 const struct drm_connector_funcs *funcs, 2131 2128 int connector_type); 2129 + int drm_connector_dynamic_init(struct drm_device *dev, 2130 + struct drm_connector *connector, 2131 + const struct drm_connector_funcs *funcs, 2132 + int connector_type, 2133 + struct i2c_adapter *ddc); 2132 2134 int drm_connector_init_with_ddc(struct drm_device *dev, 2133 2135 struct drm_connector *connector, 2134 2136 const struct drm_connector_funcs *funcs, ··· 2155 2147 unsigned int max_bpc); 2156 2148 void drm_connector_attach_edid_property(struct drm_connector *connector); 2157 2149 int drm_connector_register(struct drm_connector *connector); 2150 + int drm_connector_dynamic_register(struct drm_connector *connector); 2158 2151 void drm_connector_unregister(struct drm_connector *connector); 2159 2152 int drm_connector_attach_encoder(struct drm_connector *connector, 2160 2153 struct drm_encoder *encoder);
+72 -7
include/uapi/drm/amdxdna_accel.h
··· 33 33 DRM_AMDXDNA_SYNC_BO, 34 34 DRM_AMDXDNA_EXEC_CMD, 35 35 DRM_AMDXDNA_GET_INFO, 36 + DRM_AMDXDNA_SET_STATE, 36 37 }; 37 38 38 39 /** ··· 87 86 /** 88 87 * struct amdxdna_drm_destroy_hwctx - Destroy hardware context. 89 88 * @handle: Hardware context handle. 90 - * @pad: Structure padding. 89 + * @pad: MBZ. 91 90 */ 92 91 struct amdxdna_drm_destroy_hwctx { 93 92 __u32 handle; ··· 98 97 * struct amdxdna_cu_config - configuration for one CU 99 98 * @cu_bo: CU configuration buffer bo handle. 100 99 * @cu_func: Function of a CU. 101 - * @pad: Structure padding. 100 + * @pad: MBZ. 102 101 */ 103 102 struct amdxdna_cu_config { 104 103 __u32 cu_bo; ··· 109 108 /** 110 109 * struct amdxdna_hwctx_param_config_cu - configuration for CUs in hardware context 111 110 * @num_cus: Number of CUs to configure. 112 - * @pad: Structure padding. 111 + * @pad: MBZ. 113 112 * @cu_configs: Array of CU configurations of struct amdxdna_cu_config. 114 113 */ 115 114 struct amdxdna_hwctx_param_config_cu { ··· 122 121 DRM_AMDXDNA_HWCTX_CONFIG_CU, 123 122 DRM_AMDXDNA_HWCTX_ASSIGN_DBG_BUF, 124 123 DRM_AMDXDNA_HWCTX_REMOVE_DBG_BUF, 125 - DRM_AMDXDNA_HWCTX_CONFIG_NUM 126 124 }; 127 125 128 126 /** ··· 132 132 * @param_val: A structure specified by the param_type struct member. 133 133 * @param_val_size: Size of the parameter buffer pointed to by the param_val. 134 134 * If param_val is not a pointer, driver can ignore this. 135 - * @pad: Structure padding. 135 + * @pad: MBZ. 136 136 * 137 137 * Note: if the param_val is a pointer pointing to a buffer, the maximum size 138 138 * of the buffer is 4KiB(PAGE_SIZE). ··· 174 174 * @ext: MBZ. 175 175 * @ext_flags: MBZ. 176 176 * @handle: DRM buffer object handle. 177 - * @pad: Structure padding. 177 + * @pad: MBZ. 178 178 * @map_offset: Returned DRM fake offset for mmap(). 179 179 * @vaddr: Returned user VA of buffer. 0 in case user needs mmap(). 180 180 * @xdna_addr: Returned XDNA device virtual address. ··· 375 375 __u64 errors; 376 376 }; 377 377 378 + enum amdxdna_power_mode_type { 379 + POWER_MODE_DEFAULT, /* Fallback to calculated DPM */ 380 + POWER_MODE_LOW, /* Set frequency to lowest DPM */ 381 + POWER_MODE_MEDIUM, /* Set frequency to medium DPM */ 382 + POWER_MODE_HIGH, /* Set frequency to highest DPM */ 383 + POWER_MODE_TURBO, /* Maximum power */ 384 + }; 385 + 386 + /** 387 + * struct amdxdna_drm_get_power_mode - Get the configured power mode 388 + * @power_mode: The mode type from enum amdxdna_power_mode_type 389 + * @pad: Structure padding. 390 + */ 391 + struct amdxdna_drm_get_power_mode { 392 + __u8 power_mode; 393 + __u8 pad[7]; 394 + }; 395 + 396 + /** 397 + * struct amdxdna_drm_query_firmware_version - Query the firmware version 398 + * @major: The major version number 399 + * @minor: The minor version number 400 + * @patch: The patch level version number 401 + * @build: The build ID 402 + */ 403 + struct amdxdna_drm_query_firmware_version { 404 + __u32 major; /* out */ 405 + __u32 minor; /* out */ 406 + __u32 patch; /* out */ 407 + __u32 build; /* out */ 408 + }; 409 + 378 410 enum amdxdna_drm_get_param { 379 411 DRM_AMDXDNA_QUERY_AIE_STATUS, 380 412 DRM_AMDXDNA_QUERY_AIE_METADATA, ··· 414 382 DRM_AMDXDNA_QUERY_CLOCK_METADATA, 415 383 DRM_AMDXDNA_QUERY_SENSORS, 416 384 DRM_AMDXDNA_QUERY_HW_CONTEXTS, 417 - DRM_AMDXDNA_NUM_GET_PARAM, 385 + DRM_AMDXDNA_QUERY_FIRMWARE_VERSION = 8, 386 + DRM_AMDXDNA_GET_POWER_MODE, 418 387 }; 419 388 420 389 /** ··· 428 395 __u32 param; /* in */ 429 396 __u32 buffer_size; /* in/out */ 430 397 __u64 buffer; /* in/out */ 398 + }; 399 + 400 + enum amdxdna_drm_set_param { 401 + DRM_AMDXDNA_SET_POWER_MODE, 402 + DRM_AMDXDNA_WRITE_AIE_MEM, 403 + DRM_AMDXDNA_WRITE_AIE_REG, 404 + }; 405 + 406 + /** 407 + * struct amdxdna_drm_set_state - Set the state of the AIE hardware. 408 + * @param: Value in enum amdxdna_drm_set_param. 409 + * @buffer_size: Size of the input param. 410 + * @buffer: Pointer to the input param. 411 + */ 412 + struct amdxdna_drm_set_state { 413 + __u32 param; /* in */ 414 + __u32 buffer_size; /* in */ 415 + __u64 buffer; /* in */ 416 + }; 417 + 418 + /** 419 + * struct amdxdna_drm_set_power_mode - Set the power mode of the AIE hardware 420 + * @power_mode: The sensor type from enum amdxdna_power_mode_type 421 + * @pad: MBZ. 422 + */ 423 + struct amdxdna_drm_set_power_mode { 424 + __u8 power_mode; 425 + __u8 pad[7]; 431 426 }; 432 427 433 428 #define DRM_IOCTL_AMDXDNA_CREATE_HWCTX \ ··· 489 428 #define DRM_IOCTL_AMDXDNA_GET_INFO \ 490 429 DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDXDNA_GET_INFO, \ 491 430 struct amdxdna_drm_get_info) 431 + 432 + #define DRM_IOCTL_AMDXDNA_SET_STATE \ 433 + DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDXDNA_SET_STATE, \ 434 + struct amdxdna_drm_set_state) 492 435 493 436 #if defined(__cplusplus) 494 437 } /* extern c end */
+9
include/uapi/drm/panthor_drm.h
··· 923 923 * When a group ends up with this flag set, no jobs can be submitted to its queues. 924 924 */ 925 925 DRM_PANTHOR_GROUP_STATE_FATAL_FAULT = 1 << 1, 926 + 927 + /** 928 + * @DRM_PANTHOR_GROUP_STATE_INNOCENT: Group was killed during a reset caused by other 929 + * groups. 930 + * 931 + * This flag can only be set if DRM_PANTHOR_GROUP_STATE_TIMEDOUT is set and 932 + * DRM_PANTHOR_GROUP_STATE_FATAL_FAULT is not. 933 + */ 934 + DRM_PANTHOR_GROUP_STATE_INNOCENT = 1 << 2, 926 935 }; 927 936 928 937 /**
+1 -1
include/uapi/drm/qaic_accel.h
··· 64 64 /** 65 65 * struct qaic_manage_trans_passthrough - Defines a passthrough transaction. 66 66 * @hdr: In. Header to identify this transaction. 67 - * @data: In. Payload of this ransaction. Opaque to the driver. Userspace must 67 + * @data: In. Payload of this transaction. Opaque to the driver. Userspace must 68 68 * encode in little endian and align/pad to 64-bit. 69 69 */ 70 70 struct qaic_manage_trans_passthrough {
+1 -1
lib/fonts/Kconfig
··· 10 10 11 11 config FONTS 12 12 bool "Select compiled-in fonts" 13 - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC 13 + depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC || DRM_CLIENT_LOG 14 14 help 15 15 Say Y here if you would like to use fonts other than the default 16 16 your frame buffer console usually use.