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

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

drm-misc-next for 6.20:

Core Changes:

- dma-buf: Add tracepoints
- sched: Introduce new helpers

Driver Changes:

- amdxdna: Enable hardware context priority, Remove (obsolete and
never public) NPU2 Support, Race condition fix
- rockchip: Add RK3368 HDMI Support
- rz-du: Add RZ/V2H(P) MIPI-DSI Support

- panels:
- st7571: Introduce SPI support
- New panels: Sitronix ST7920, Samsung LTL106HL02, LG LH546WF1-ED01, HannStar HSD156JUW2

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

From: Maxime Ripard <mripard@redhat.com>
Link: https://patch.msgid.link/20251219-arcane-quaint-skunk-e383b0@houat

+3540 -1444
+91 -29
Documentation/devicetree/bindings/display/bridge/renesas,dsi.yaml
··· 14 14 RZ/G2L alike family of SoC's. The encoder can operate in DSI mode, with 15 15 up to four data lanes. 16 16 17 - allOf: 18 - - $ref: /schemas/display/dsi-controller.yaml# 19 - 20 17 properties: 21 18 compatible: 22 - items: 19 + oneOf: 20 + - items: 21 + - enum: 22 + - renesas,r9a07g044-mipi-dsi # RZ/G2{L,LC} 23 + - renesas,r9a07g054-mipi-dsi # RZ/V2L 24 + - const: renesas,rzg2l-mipi-dsi 25 + 26 + - items: 27 + - const: renesas,r9a09g056-mipi-dsi # RZ/V2N 28 + - const: renesas,r9a09g057-mipi-dsi 29 + 23 30 - enum: 24 - - renesas,r9a07g044-mipi-dsi # RZ/G2{L,LC} 25 - - renesas,r9a07g054-mipi-dsi # RZ/V2L 26 - - const: renesas,rzg2l-mipi-dsi 31 + - renesas,r9a09g057-mipi-dsi # RZ/V2H(P) 27 32 28 33 reg: 29 34 maxItems: 1 ··· 54 49 - const: debug 55 50 56 51 clocks: 57 - items: 58 - - description: DSI D-PHY PLL multiplied clock 59 - - description: DSI D-PHY system clock 60 - - description: DSI AXI bus clock 61 - - description: DSI Register access clock 62 - - description: DSI Video clock 63 - - description: DSI D-PHY Escape mode transmit clock 52 + oneOf: 53 + - items: 54 + - description: DSI D-PHY PLL multiplied clock 55 + - description: DSI D-PHY system clock 56 + - description: DSI AXI bus clock 57 + - description: DSI Register access clock 58 + - description: DSI Video clock 59 + - description: DSI D-PHY Escape mode transmit clock 60 + - items: 61 + - description: DSI D-PHY PLL reference clock 62 + - description: DSI AXI bus clock 63 + - description: DSI Register access clock 64 + - description: DSI Video clock 65 + - description: DSI D-PHY Escape mode transmit clock 64 66 65 67 clock-names: 66 - items: 67 - - const: pllclk 68 - - const: sysclk 69 - - const: aclk 70 - - const: pclk 71 - - const: vclk 72 - - const: lpclk 68 + oneOf: 69 + - items: 70 + - const: pllclk 71 + - const: sysclk 72 + - const: aclk 73 + - const: pclk 74 + - const: vclk 75 + - const: lpclk 76 + - items: 77 + - const: pllrefclk 78 + - const: aclk 79 + - const: pclk 80 + - const: vclk 81 + - const: lpclk 73 82 74 83 resets: 75 - items: 76 - - description: MIPI_DSI_CMN_RSTB 77 - - description: MIPI_DSI_ARESET_N 78 - - description: MIPI_DSI_PRESET_N 84 + oneOf: 85 + - items: 86 + - description: MIPI_DSI_CMN_RSTB 87 + - description: MIPI_DSI_ARESET_N 88 + - description: MIPI_DSI_PRESET_N 89 + - items: 90 + - description: MIPI_DSI_ARESET_N 91 + - description: MIPI_DSI_PRESET_N 79 92 80 93 reset-names: 81 - items: 82 - - const: rst 83 - - const: arst 84 - - const: prst 94 + oneOf: 95 + - items: 96 + - const: rst 97 + - const: arst 98 + - const: prst 99 + - items: 100 + - const: arst 101 + - const: prst 85 102 86 103 power-domains: 87 104 maxItems: 1 ··· 156 129 - ports 157 130 158 131 unevaluatedProperties: false 132 + 133 + allOf: 134 + - $ref: ../dsi-controller.yaml# 135 + 136 + - if: 137 + properties: 138 + compatible: 139 + contains: 140 + const: renesas,r9a09g057-mipi-dsi 141 + then: 142 + properties: 143 + clocks: 144 + maxItems: 5 145 + 146 + clock-names: 147 + maxItems: 5 148 + 149 + resets: 150 + maxItems: 2 151 + 152 + reset-names: 153 + maxItems: 2 154 + else: 155 + properties: 156 + clocks: 157 + minItems: 6 158 + 159 + clock-names: 160 + minItems: 6 161 + 162 + resets: 163 + minItems: 3 164 + 165 + reset-names: 166 + minItems: 3 159 167 160 168 examples: 161 169 - |
+9 -4
Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
··· 4 4 $id: http://devicetree.org/schemas/display/panel/lg,sw43408.yaml# 5 5 $schema: http://devicetree.org/meta-schemas/core.yaml# 6 6 7 - title: LG SW43408 1080x2160 DSI panel 7 + title: LG SW43408 AMOLED DDIC 8 8 9 9 maintainers: 10 10 - Casey Connolly <casey.connolly@linaro.org> 11 11 12 12 description: 13 - This panel is used on the Pixel 3, it is a 60hz OLED panel which 14 - required DSC (Display Stream Compression) and has rounded corners. 13 + The SW43408 is display driver IC with connected panel. 14 + 15 + LG LH546WF1-ED01 panel is used on the Pixel 3, it is a 60hz OLED panel 16 + which required DSC (Display Stream Compression) and has rounded corners. 15 17 16 18 allOf: 17 19 - $ref: panel-common.yaml# ··· 21 19 properties: 22 20 compatible: 23 21 items: 22 + - enum: 23 + # LG 5.46 inch, 1080x2160 pixels, 18:9 ratio 24 + - lg,sw43408-lh546wf1-ed01 24 25 - const: lg,sw43408 25 26 26 27 reg: ··· 51 46 #size-cells = <0>; 52 47 53 48 panel@0 { 54 - compatible = "lg,sw43408"; 49 + compatible = "lg,sw43408-lh546wf1-ed01", "lg,sw43408"; 55 50 reg = <0>; 56 51 57 52 vddi-supply = <&vreg_l14a_1p88>;
+2
Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml
··· 55 55 - panasonic,vvx10f004b00 56 56 # Panasonic 10" WUXGA TFT LCD panel 57 57 - panasonic,vvx10f034n00 58 + # Samsung ltl106hl02 10.6" Full HD TFT LCD panel 59 + - samsung,ltl106hl02-001 58 60 # Samsung s6e3fa7 1080x2220 based AMS559NK06 AMOLED panel 59 61 - samsung,s6e3fa7-ams559nk06 60 62 # Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel
+2
Documentation/devicetree/bindings/display/panel/panel-simple.yaml
··· 154 154 - hannstar,hsd070pww1 155 155 # HannStar Display Corp. HSD100PXN1 10.1" XGA LVDS panel 156 156 - hannstar,hsd100pxn1 157 + # HannStar Display Corp. HSD156JUW2 15.6" FHD (1920x1080) TFT LCD panel 158 + - hannstar,hsd156juw2 157 159 # Hitachi Ltd. Corporation 9" WVGA (800x480) TFT LCD panel 158 160 - hit,tx23d38vm0caa 159 161 # Innolux AT043TN24 4.3" WQVGA TFT LCD panel
+8 -12
Documentation/devicetree/bindings/display/panel/samsung,s6e3fc2x01.yaml
··· 6 6 7 7 title: Samsung S6E3FC2X01 AMOLED DDIC 8 8 9 - description: The S6E3FC2X01 is display driver IC with connected panel. 10 - 11 9 maintainers: 12 10 - David Heidelberg <david@ixit.cz> 11 + 12 + description: The S6E3FC2X01 is display driver IC with connected panel. 13 13 14 14 allOf: 15 15 - $ref: panel-common.yaml# ··· 25 25 reg: 26 26 maxItems: 1 27 27 28 - reset-gpios: true 29 - 30 - port: true 31 - 32 - vddio-supply: 33 - description: VDD regulator 28 + poc-supply: 29 + description: POC regulator 34 30 35 31 vci-supply: 36 32 description: VCI regulator 37 33 38 - poc-supply: 39 - description: POC regulator 34 + vddio-supply: 35 + description: VDD regulator 40 36 41 37 required: 42 38 - compatible 43 39 - reset-gpios 44 - - vddio-supply 45 - - vci-supply 46 40 - poc-supply 41 + - vci-supply 42 + - vddio-supply 47 43 48 44 unevaluatedProperties: false 49 45
+1
Documentation/devicetree/bindings/display/rockchip/rockchip,dw-hdmi.yaml
··· 23 23 - rockchip,rk3228-dw-hdmi 24 24 - rockchip,rk3288-dw-hdmi 25 25 - rockchip,rk3328-dw-hdmi 26 + - rockchip,rk3368-dw-hdmi 26 27 - rockchip,rk3399-dw-hdmi 27 28 - rockchip,rk3568-dw-hdmi 28 29
+25
Documentation/devicetree/bindings/display/sitronix,st7571.yaml
··· 76 76 }; 77 77 }; 78 78 }; 79 + 80 + spi { 81 + #address-cells = <1>; 82 + #size-cells = <0>; 83 + 84 + display@0 { 85 + compatible = "sitronix,st7571"; 86 + reg = <0>; 87 + reset-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>; 88 + width-mm = <37>; 89 + height-mm = <27>; 90 + 91 + panel-timing { 92 + hactive = <128>; 93 + vactive = <96>; 94 + hback-porch = <0>; 95 + vback-porch = <0>; 96 + clock-frequency = <0>; 97 + hfront-porch = <0>; 98 + hsync-len = <0>; 99 + vfront-porch = <0>; 100 + vsync-len = <0>; 101 + }; 102 + }; 103 + };
+58
Documentation/devicetree/bindings/display/sitronix,st7920.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/sitronix,st7920.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Sitronix ST7920 LCD Display Controllers 8 + 9 + maintainers: 10 + - Iker Pedrosa <ikerpedrosam@gmail.com> 11 + 12 + description: 13 + The Sitronix ST7920 is a controller for monochrome dot-matrix graphical LCDs, 14 + most commonly used for 128x64 pixel displays. 15 + 16 + properties: 17 + compatible: 18 + const: sitronix,st7920 19 + 20 + reg: 21 + maxItems: 1 22 + 23 + vdd-supply: 24 + description: Regulator that provides 5V Vdd power supply 25 + 26 + reset-gpios: 27 + maxItems: 1 28 + 29 + spi-max-frequency: 30 + maximum: 600000 31 + 32 + required: 33 + - compatible 34 + - reg 35 + - spi-max-frequency 36 + 37 + allOf: 38 + - $ref: /schemas/spi/spi-peripheral-props.yaml# 39 + 40 + unevaluatedProperties: false 41 + 42 + examples: 43 + - | 44 + #include <dt-bindings/gpio/gpio.h> 45 + 46 + spi { 47 + #address-cells = <1>; 48 + #size-cells = <0>; 49 + 50 + display@0 { 51 + compatible = "sitronix,st7920"; 52 + reg = <0>; 53 + vdd-supply = <&reg_5v>; 54 + reset-gpios = <&gpio 25 GPIO_ACTIVE_LOW>; 55 + spi-max-frequency = <600000>; 56 + spi-cs-high; 57 + }; 58 + };
+10
MAINTAINERS
··· 8201 8201 F: Documentation/devicetree/bindings/display/sitronix,st7567.yaml 8202 8202 F: Documentation/devicetree/bindings/display/sitronix,st7571.yaml 8203 8203 F: drivers/gpu/drm/sitronix/st7571-i2c.c 8204 + F: drivers/gpu/drm/sitronix/st7571-spi.c 8205 + F: drivers/gpu/drm/sitronix/st7571.c 8206 + F: drivers/gpu/drm/sitronix/st7571.h 8204 8207 8205 8208 DRM DRIVER FOR SITRONIX ST7701 PANELS 8206 8209 M: Jagan Teki <jagan@amarulasolutions.com> ··· 8225 8222 T: git https://gitlab.freedesktop.org/drm/misc/kernel.git 8226 8223 F: Documentation/devicetree/bindings/display/sitronix,st7735r.yaml 8227 8224 F: drivers/gpu/drm/sitronix/st7735r.c 8225 + 8226 + DRM DRIVER FOR SITRONIX ST7920 LCD DISPLAYS 8227 + M: Iker Pedrosa <ikerpedrosam@gmail.com> 8228 + S: Maintained 8229 + T: git https://gitlab.freedesktop.org/drm/misc/kernel.git 8230 + F: Documentation/devicetree/bindings/display/sitronix,st7920.yaml 8231 + F: drivers/gpu/drm/sitronix/st7920.c 8228 8232 8229 8233 DRM DRIVER FOR SOLOMON SSD130X OLED DISPLAYS 8230 8234 M: Javier Martinez Canillas <javierm@redhat.com>
-1
drivers/accel/amdxdna/Makefile
··· 18 18 amdxdna_sysfs.o \ 19 19 amdxdna_ubuf.o \ 20 20 npu1_regs.o \ 21 - npu2_regs.o \ 22 21 npu4_regs.o \ 23 22 npu5_regs.o \ 24 23 npu6_regs.o
+15 -3
drivers/accel/amdxdna/aie2_ctx.c
··· 468 468 struct alloc_requests *xrs_req; 469 469 int ret; 470 470 471 + if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) { 472 + hwctx->num_unused_col = xdna->dev_handle->total_col - hwctx->num_col; 473 + hwctx->num_col = xdna->dev_handle->total_col; 474 + return aie2_create_context(xdna->dev_handle, hwctx); 475 + } 476 + 471 477 xrs_req = kzalloc(sizeof(*xrs_req), GFP_KERNEL); 472 478 if (!xrs_req) 473 479 return -ENOMEM; ··· 505 499 struct amdxdna_dev *xdna = hwctx->client->xdna; 506 500 int ret; 507 501 508 - ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx); 509 - if (ret) 510 - XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret); 502 + if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) { 503 + ret = aie2_destroy_context(xdna->dev_handle, hwctx); 504 + if (ret) 505 + XDNA_ERR(xdna, "Destroy temporal only context failed, ret %d", ret); 506 + } else { 507 + ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx); 508 + if (ret) 509 + XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret); 510 + } 511 511 } 512 512 513 513 static int aie2_ctx_syncobj_create(struct amdxdna_hwctx *hwctx)
+49 -25
drivers/accel/amdxdna/aie2_message.c
··· 192 192 return 0; 193 193 } 194 194 195 + static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id) 196 + { 197 + DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT); 198 + struct amdxdna_dev *xdna = ndev->xdna; 199 + int ret; 200 + 201 + req.context_id = id; 202 + ret = aie2_send_mgmt_msg_wait(ndev, &msg); 203 + if (ret) 204 + XDNA_WARN(xdna, "Destroy context failed, ret %d", ret); 205 + 206 + return ret; 207 + } 208 + 209 + static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev, 210 + struct amdxdna_hwctx *hwctx) 211 + { 212 + if (!AIE2_FEATURE_ON(ndev, AIE2_PREEMPT)) 213 + return PRIORITY_HIGH; 214 + 215 + switch (hwctx->qos.priority) { 216 + case AMDXDNA_QOS_REALTIME_PRIORITY: 217 + return PRIORITY_REALTIME; 218 + case AMDXDNA_QOS_HIGH_PRIORITY: 219 + return PRIORITY_HIGH; 220 + case AMDXDNA_QOS_NORMAL_PRIORITY: 221 + return PRIORITY_NORMAL; 222 + case AMDXDNA_QOS_LOW_PRIORITY: 223 + return PRIORITY_LOW; 224 + default: 225 + return PRIORITY_HIGH; 226 + } 227 + } 228 + 195 229 int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx) 196 230 { 197 231 DECLARE_AIE2_MSG(create_ctx, MSG_OP_CREATE_CONTEXT); ··· 239 205 req.aie_type = 1; 240 206 req.start_col = hwctx->start_col; 241 207 req.num_col = hwctx->num_col; 208 + req.num_unused_col = hwctx->num_unused_col; 242 209 req.num_cq_pairs_requested = 1; 243 210 req.pasid = hwctx->client->pasid; 244 - req.context_priority = 2; 211 + req.context_priority = aie2_get_context_priority(ndev, hwctx); 245 212 246 213 ret = aie2_send_mgmt_msg_wait(ndev, &msg); 247 214 if (ret) 248 215 return ret; 249 216 250 217 hwctx->fw_ctx_id = resp.context_id; 251 - WARN_ONCE(hwctx->fw_ctx_id == -1, "Unexpected context id"); 218 + if (WARN_ON_ONCE(hwctx->fw_ctx_id == -1)) 219 + return -EINVAL; 252 220 253 221 if (ndev->force_preempt_enabled) { 254 222 ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FORCE_PREEMPT, &hwctx->fw_ctx_id); 255 223 if (ret) { 256 224 XDNA_ERR(xdna, "failed to enable force preempt %d", ret); 257 - return ret; 225 + goto del_ctx_req; 258 226 } 259 227 } 260 228 ··· 273 237 274 238 ret = pci_irq_vector(to_pci_dev(xdna->ddev.dev), resp.msix_id); 275 239 if (ret == -EINVAL) { 276 - XDNA_ERR(xdna, "not able to create channel"); 277 - goto out_destroy_context; 240 + XDNA_ERR(xdna, "Alloc IRQ failed %d", ret); 241 + goto del_ctx_req; 278 242 } 279 243 280 244 intr_reg = i2x.mb_head_ptr_reg + 4; 281 245 hwctx->priv->mbox_chann = xdna_mailbox_create_channel(ndev->mbox, &x2i, &i2x, 282 246 intr_reg, ret); 283 247 if (!hwctx->priv->mbox_chann) { 284 - XDNA_ERR(xdna, "not able to create channel"); 248 + XDNA_ERR(xdna, "Not able to create channel"); 285 249 ret = -EINVAL; 286 - goto out_destroy_context; 250 + goto del_ctx_req; 287 251 } 288 252 ndev->hwctx_num++; 289 253 290 - XDNA_DBG(xdna, "%s mailbox channel irq: %d, msix_id: %d", 291 - hwctx->name, ret, resp.msix_id); 292 - XDNA_DBG(xdna, "%s created fw ctx %d pasid %d", hwctx->name, 293 - hwctx->fw_ctx_id, hwctx->client->pasid); 254 + XDNA_DBG(xdna, "Mailbox channel irq: %d, msix_id: %d", ret, resp.msix_id); 255 + XDNA_DBG(xdna, "Created fw ctx %d pasid %d", hwctx->fw_ctx_id, hwctx->client->pasid); 294 256 295 257 return 0; 296 258 297 - out_destroy_context: 298 - aie2_destroy_context(ndev, hwctx); 259 + del_ctx_req: 260 + aie2_destroy_context_req(ndev, hwctx->fw_ctx_id); 299 261 return ret; 300 262 } 301 263 302 264 int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx) 303 265 { 304 - DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT); 305 266 struct amdxdna_dev *xdna = ndev->xdna; 306 267 int ret; 307 268 308 - if (hwctx->fw_ctx_id == -1) 309 - return 0; 310 - 311 269 xdna_mailbox_stop_channel(hwctx->priv->mbox_chann); 312 - 313 - req.context_id = hwctx->fw_ctx_id; 314 - ret = aie2_send_mgmt_msg_wait(ndev, &msg); 315 - if (ret) 316 - XDNA_WARN(xdna, "%s destroy context failed, ret %d", hwctx->name, ret); 317 - 270 + ret = aie2_destroy_context_req(ndev, hwctx->fw_ctx_id); 318 271 xdna_mailbox_destroy_channel(hwctx->priv->mbox_chann); 319 - XDNA_DBG(xdna, "%s destroyed fw ctx %d", hwctx->name, 320 - hwctx->fw_ctx_id); 272 + XDNA_DBG(xdna, "Destroyed fw ctx %d", hwctx->fw_ctx_id); 321 273 hwctx->priv->mbox_chann = NULL; 322 274 hwctx->fw_ctx_id = -1; 323 275 ndev->hwctx_num--;
+7 -1
drivers/accel/amdxdna/aie2_msg_priv.h
··· 108 108 struct cq_info i2x_q; 109 109 }; 110 110 111 + #define PRIORITY_REALTIME 1 112 + #define PRIORITY_HIGH 2 113 + #define PRIORITY_NORMAL 3 114 + #define PRIORITY_LOW 4 115 + 111 116 struct create_ctx_req { 112 117 __u32 aie_type; 113 118 __u8 start_col; 114 119 __u8 num_col; 115 - __u16 reserved; 120 + __u8 num_unused_col; 121 + __u8 reserved; 116 122 __u8 num_cq_pairs_requested; 117 123 __u8 reserved1; 118 124 __u16 pasid;
+1
drivers/accel/amdxdna/aie2_pci.h
··· 232 232 enum aie2_fw_feature { 233 233 AIE2_NPU_COMMAND, 234 234 AIE2_PREEMPT, 235 + AIE2_TEMPORAL_ONLY, 235 236 AIE2_FEATURE_MAX 236 237 }; 237 238
+1
drivers/accel/amdxdna/amdxdna_ctx.h
··· 98 98 u32 *col_list; 99 99 u32 start_col; 100 100 u32 num_col; 101 + u32 num_unused_col; 101 102 #define HWCTX_STAT_INIT 0 102 103 #define HWCTX_STAT_READY 1 103 104 #define HWCTX_STAT_STOP 2
+16 -11
drivers/accel/amdxdna/amdxdna_mailbox.c
··· 191 191 u32 head, tail; 192 192 u32 start_addr; 193 193 u32 tmp_tail; 194 + int ret; 194 195 195 196 head = mailbox_get_headptr(mb_chann, CHAN_RES_X2I); 196 197 tail = mb_chann->x2i_tail; 197 - ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I); 198 + ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_X2I) - sizeof(u32); 198 199 start_addr = mb_chann->res[CHAN_RES_X2I].rb_start_addr; 199 200 tmp_tail = tail + mb_msg->pkg_size; 200 201 201 - if (tail < head && tmp_tail >= head) 202 - goto no_space; 203 202 204 - if (tail >= head && (tmp_tail > ringbuf_size - sizeof(u32) && 205 - mb_msg->pkg_size >= head)) 206 - goto no_space; 207 - 208 - if (tail >= head && tmp_tail > ringbuf_size - sizeof(u32)) { 203 + check_again: 204 + if (tail >= head && tmp_tail > ringbuf_size) { 209 205 write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail; 210 206 writel(TOMBSTONE, write_addr); 211 207 212 208 /* tombstone is set. Write from the start of the ringbuf */ 213 209 tail = 0; 210 + tmp_tail = tail + mb_msg->pkg_size; 211 + } 212 + 213 + if (tail < head && tmp_tail >= head) { 214 + ret = read_poll_timeout(mailbox_get_headptr, head, 215 + tmp_tail < head || tail >= head, 216 + 1, 100, false, mb_chann, CHAN_RES_X2I); 217 + if (ret) 218 + return ret; 219 + 220 + if (tail >= head) 221 + goto check_again; 214 222 } 215 223 216 224 write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail; ··· 230 222 mb_msg->pkg.header.id); 231 223 232 224 return 0; 233 - 234 - no_space: 235 - return -ENOSPC; 236 225 } 237 226 238 227 static int
+11 -19
drivers/accel/amdxdna/amdxdna_pci_drv.c
··· 51 51 52 52 static const struct amdxdna_device_id amdxdna_ids[] = { 53 53 { 0x1502, 0x0, &dev_npu1_info }, 54 - { 0x17f0, 0x0, &dev_npu2_info }, 55 54 { 0x17f0, 0x10, &dev_npu4_info }, 56 55 { 0x17f0, 0x11, &dev_npu5_info }, 57 56 { 0x17f0, 0x20, &dev_npu6_info }, ··· 104 105 return ret; 105 106 } 106 107 107 - static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp) 108 + static void amdxdna_client_cleanup(struct amdxdna_client *client) 108 109 { 109 - struct amdxdna_client *client = filp->driver_priv; 110 - struct amdxdna_dev *xdna = to_xdna_dev(ddev); 111 - 112 - XDNA_DBG(xdna, "closing pid %d", client->pid); 113 - 110 + list_del(&client->node); 111 + amdxdna_hwctx_remove_all(client); 114 112 xa_destroy(&client->hwctx_xa); 115 113 cleanup_srcu_struct(&client->hwctx_srcu); 116 114 mutex_destroy(&client->mm_lock); 115 + 117 116 if (client->dev_heap) 118 117 drm_gem_object_put(to_gobj(client->dev_heap)); 119 118 120 119 iommu_sva_unbind_device(client->sva); 121 120 122 - XDNA_DBG(xdna, "pid %d closed", client->pid); 123 121 kfree(client); 124 122 } 125 123 126 - static int amdxdna_flush(struct file *f, fl_owner_t id) 124 + static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp) 127 125 { 128 - struct drm_file *filp = f->private_data; 129 126 struct amdxdna_client *client = filp->driver_priv; 130 - struct amdxdna_dev *xdna = client->xdna; 127 + struct amdxdna_dev *xdna = to_xdna_dev(ddev); 131 128 int idx; 132 129 133 - XDNA_DBG(xdna, "PID %d flushing...", client->pid); 130 + XDNA_DBG(xdna, "closing pid %d", client->pid); 131 + 134 132 if (!drm_dev_enter(&xdna->ddev, &idx)) 135 - return 0; 133 + return; 136 134 137 135 mutex_lock(&xdna->dev_lock); 138 - list_del_init(&client->node); 139 - amdxdna_hwctx_remove_all(client); 136 + amdxdna_client_cleanup(client); 140 137 mutex_unlock(&xdna->dev_lock); 141 138 142 139 drm_dev_exit(idx); 143 - return 0; 144 140 } 145 141 146 142 static int amdxdna_drm_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) ··· 211 217 .owner = THIS_MODULE, 212 218 .open = accel_open, 213 219 .release = drm_release, 214 - .flush = amdxdna_flush, 215 220 .unlocked_ioctl = drm_ioctl, 216 221 .compat_ioctl = drm_compat_ioctl, 217 222 .poll = drm_poll, ··· 326 333 client = list_first_entry_or_null(&xdna->client_list, 327 334 struct amdxdna_client, node); 328 335 while (client) { 329 - list_del_init(&client->node); 330 - amdxdna_hwctx_remove_all(client); 336 + amdxdna_client_cleanup(client); 331 337 332 338 client = list_first_entry_or_null(&xdna->client_list, 333 339 struct amdxdna_client, node);
-1
drivers/accel/amdxdna/amdxdna_pci_drv.h
··· 137 137 138 138 /* Add device info below */ 139 139 extern const struct amdxdna_dev_info dev_npu1_info; 140 - extern const struct amdxdna_dev_info dev_npu2_info; 141 140 extern const struct amdxdna_dev_info dev_npu4_info; 142 141 extern const struct amdxdna_dev_info dev_npu5_info; 143 142 extern const struct amdxdna_dev_info dev_npu6_info;
-117
drivers/accel/amdxdna/npu2_regs.c
··· 1 - // SPDX-License-Identifier: GPL-2.0 2 - /* 3 - * Copyright (C) 2023-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_PWAITMODE 0x301003C 17 - #define MPNPU_PUB_SEC_INTR 0x3010060 18 - #define MPNPU_PUB_PWRMGMT_INTR 0x3010064 19 - #define MPNPU_PUB_SCRATCH0 0x301006C 20 - #define MPNPU_PUB_SCRATCH1 0x3010070 21 - #define MPNPU_PUB_SCRATCH2 0x3010074 22 - #define MPNPU_PUB_SCRATCH3 0x3010078 23 - #define MPNPU_PUB_SCRATCH4 0x301007C 24 - #define MPNPU_PUB_SCRATCH5 0x3010080 25 - #define MPNPU_PUB_SCRATCH6 0x3010084 26 - #define MPNPU_PUB_SCRATCH7 0x3010088 27 - #define MPNPU_PUB_SCRATCH8 0x301008C 28 - #define MPNPU_PUB_SCRATCH9 0x3010090 29 - #define MPNPU_PUB_SCRATCH10 0x3010094 30 - #define MPNPU_PUB_SCRATCH11 0x3010098 31 - #define MPNPU_PUB_SCRATCH12 0x301009C 32 - #define MPNPU_PUB_SCRATCH13 0x30100A0 33 - #define MPNPU_PUB_SCRATCH14 0x30100A4 34 - #define MPNPU_PUB_SCRATCH15 0x30100A8 35 - #define MP0_C2PMSG_73 0x3810A24 36 - #define MP0_C2PMSG_123 0x3810AEC 37 - 38 - #define MP1_C2PMSG_0 0x3B10900 39 - #define MP1_C2PMSG_60 0x3B109F0 40 - #define MP1_C2PMSG_61 0x3B109F4 41 - 42 - #define MPNPU_SRAM_X2I_MAILBOX_0 0x3600000 43 - #define MPNPU_SRAM_X2I_MAILBOX_15 0x361E000 44 - #define MPNPU_SRAM_X2I_MAILBOX_31 0x363E000 45 - #define MPNPU_SRAM_I2X_MAILBOX_31 0x363F000 46 - 47 - #define MMNPU_APERTURE0_BASE 0x3000000 48 - #define MMNPU_APERTURE1_BASE 0x3600000 49 - #define MMNPU_APERTURE3_BASE 0x3810000 50 - #define MMNPU_APERTURE4_BASE 0x3B10000 51 - 52 - /* PCIe BAR Index for NPU2 */ 53 - #define NPU2_REG_BAR_INDEX 0 54 - #define NPU2_MBOX_BAR_INDEX 0 55 - #define NPU2_PSP_BAR_INDEX 4 56 - #define NPU2_SMU_BAR_INDEX 5 57 - #define NPU2_SRAM_BAR_INDEX 2 58 - /* Associated BARs and Apertures */ 59 - #define NPU2_REG_BAR_BASE MMNPU_APERTURE0_BASE 60 - #define NPU2_MBOX_BAR_BASE MMNPU_APERTURE0_BASE 61 - #define NPU2_PSP_BAR_BASE MMNPU_APERTURE3_BASE 62 - #define NPU2_SMU_BAR_BASE MMNPU_APERTURE4_BASE 63 - #define NPU2_SRAM_BAR_BASE MMNPU_APERTURE1_BASE 64 - 65 - static const struct amdxdna_dev_priv npu2_dev_priv = { 66 - .fw_path = "amdnpu/17f0_00/npu.sbin", 67 - .protocol_major = 0x6, 68 - .protocol_minor = 0x6, 69 - .rt_config = npu4_default_rt_cfg, 70 - .dpm_clk_tbl = npu4_dpm_clk_table, 71 - .fw_feature_tbl = npu4_fw_feature_table, 72 - .col_align = COL_ALIGN_NATURE, 73 - .mbox_dev_addr = NPU2_MBOX_BAR_BASE, 74 - .mbox_size = 0, /* Use BAR size */ 75 - .sram_dev_addr = NPU2_SRAM_BAR_BASE, 76 - .hwctx_limit = 16, 77 - .sram_offs = { 78 - DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_0), 79 - DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_15), 80 - }, 81 - .psp_regs_off = { 82 - DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU2_PSP, MP0_C2PMSG_123), 83 - DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU2_REG, MPNPU_PUB_SCRATCH3), 84 - DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU2_REG, MPNPU_PUB_SCRATCH4), 85 - DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU2_REG, MPNPU_PUB_SCRATCH9), 86 - DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU2_PSP, MP0_C2PMSG_73), 87 - DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU2_PSP, MP0_C2PMSG_123), 88 - DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU2_REG, MPNPU_PUB_SCRATCH3), 89 - DEFINE_BAR_OFFSET(PSP_PWAITMODE_REG, NPU2_REG, MPNPU_PWAITMODE), 90 - }, 91 - .smu_regs_off = { 92 - DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU2_SMU, MP1_C2PMSG_0), 93 - DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU2_SMU, MP1_C2PMSG_60), 94 - DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU2_SMU, MMNPU_APERTURE4_BASE), 95 - DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU2_SMU, MP1_C2PMSG_61), 96 - DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU2_SMU, MP1_C2PMSG_60), 97 - }, 98 - .hw_ops = { 99 - .set_dpm = npu4_set_dpm, 100 - }, 101 - }; 102 - 103 - const struct amdxdna_dev_info dev_npu2_info = { 104 - .reg_bar = NPU2_REG_BAR_INDEX, 105 - .mbox_bar = NPU2_MBOX_BAR_INDEX, 106 - .sram_bar = NPU2_SRAM_BAR_INDEX, 107 - .psp_bar = NPU2_PSP_BAR_INDEX, 108 - .smu_bar = NPU2_SMU_BAR_INDEX, 109 - .first_col = 0, 110 - .dev_mem_buf_shift = 15, /* 32 KiB aligned */ 111 - .dev_mem_base = AIE2_DEVM_BASE, 112 - .dev_mem_size = AIE2_DEVM_SIZE, 113 - .vbnv = "RyzenAI-npu2", 114 - .device_type = AMDXDNA_DEV_TYPE_KMQ, 115 - .dev_priv = &npu2_dev_priv, 116 - .ops = &aie2_ops, /* NPU2 can share NPU1's callback */ 117 - };
+1
drivers/accel/amdxdna/npu4_regs.c
··· 90 90 const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = { 91 91 { .feature = AIE2_NPU_COMMAND, .min_minor = 15 }, 92 92 { .feature = AIE2_PREEMPT, .min_minor = 12 }, 93 + { .feature = AIE2_TEMPORAL_ONLY, .min_minor = 12 }, 93 94 { 0 } 94 95 }; 95 96
+1 -1
drivers/accel/ivpu/ivpu_gem.c
··· 95 95 96 96 if (!bo->mmu_mapped) { 97 97 drm_WARN_ON(&vdev->drm, !bo->ctx); 98 - ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt, 98 + ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt, ivpu_bo_size(bo), 99 99 ivpu_bo_is_snooped(bo), ivpu_bo_is_read_only(bo)); 100 100 if (ret) { 101 101 ivpu_err(vdev, "Failed to map BO in MMU: %d\n", ret);
+17 -3
drivers/accel/ivpu/ivpu_mmu_context.c
··· 429 429 } 430 430 431 431 int 432 - ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 433 - u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only) 432 + ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, 433 + struct sg_table *sgt, size_t bo_size, bool llc_coherent, bool read_only) 434 434 { 435 435 size_t start_vpu_addr = vpu_addr; 436 436 struct scatterlist *sg; 437 + size_t sgt_size = 0; 437 438 int ret; 438 439 u64 prot; 439 440 u64 i; ··· 463 462 ivpu_dbg(vdev, MMU_MAP, "Map ctx: %u dma_addr: 0x%llx vpu_addr: 0x%llx size: %lu\n", 464 463 ctx->id, dma_addr, vpu_addr, size); 465 464 465 + if (sgt_size + size > bo_size) { 466 + ivpu_err(vdev, "Scatter-gather table size exceeds buffer object size\n"); 467 + ret = -EINVAL; 468 + goto err_unmap_pages; 469 + } 470 + 466 471 ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); 467 472 if (ret) { 468 473 ivpu_err(vdev, "Failed to map context pages\n"); 469 474 goto err_unmap_pages; 470 475 } 471 476 vpu_addr += size; 477 + sgt_size += size; 478 + } 479 + 480 + if (sgt_size < bo_size) { 481 + ivpu_err(vdev, "Scatter-gather table size too small to cover buffer object size\n"); 482 + ret = -EINVAL; 483 + goto err_unmap_pages; 472 484 } 473 485 474 486 if (!ctx->is_cd_valid) { ··· 507 493 return 0; 508 494 509 495 err_unmap_pages: 510 - ivpu_mmu_context_unmap_pages(ctx, start_vpu_addr, vpu_addr - start_vpu_addr); 496 + ivpu_mmu_context_unmap_pages(ctx, start_vpu_addr, sgt_size); 511 497 mutex_unlock(&ctx->lock); 512 498 return ret; 513 499 }
+3 -2
drivers/accel/ivpu/ivpu_mmu_context.h
··· 41 41 u64 size, struct drm_mm_node *node); 42 42 void ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node); 43 43 44 - int ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 45 - u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only); 44 + int 45 + ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, 46 + struct sg_table *sgt, size_t bo_size, bool llc_coherent, bool read_only); 46 47 void ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 47 48 u64 vpu_addr, struct sg_table *sgt); 48 49 int ivpu_mmu_context_set_pages_ro(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
+49 -2
drivers/dma-buf/dma-buf.c
··· 35 35 36 36 #include "dma-buf-sysfs-stats.h" 37 37 38 + #define CREATE_TRACE_POINTS 39 + #include <trace/events/dma_buf.h> 40 + 41 + /* 42 + * dmabuf->name must be accessed with holding dmabuf->name_lock. 43 + * we need to take the lock around the tracepoint call itself where 44 + * it is called in the code. 45 + * 46 + * Note: FUNC##_enabled() is a static branch that will only 47 + * be set when the trace event is enabled. 48 + */ 49 + #define DMA_BUF_TRACE(FUNC, ...) \ 50 + do { \ 51 + if (FUNC##_enabled()) { \ 52 + guard(spinlock)(&dmabuf->name_lock); \ 53 + FUNC(__VA_ARGS__); \ 54 + } else if (IS_ENABLED(CONFIG_LOCKDEP)) { \ 55 + /* Expose this lock when lockdep is enabled */ \ 56 + guard(spinlock)(&dmabuf->name_lock); \ 57 + } \ 58 + } while (0) 59 + 38 60 /* Wrapper to hide the sg_table page link from the importer */ 39 61 struct dma_buf_sg_table_wrapper { 40 62 struct sg_table *original; ··· 247 225 if (vma->vm_pgoff + vma_pages(vma) > 248 226 dmabuf->size >> PAGE_SHIFT) 249 227 return -EINVAL; 228 + 229 + DMA_BUF_TRACE(trace_dma_buf_mmap_internal, dmabuf); 250 230 251 231 return dmabuf->ops->mmap(dmabuf, vma); 252 232 } ··· 775 751 776 752 __dma_buf_list_add(dmabuf); 777 753 754 + DMA_BUF_TRACE(trace_dma_buf_export, dmabuf); 755 + 778 756 return dmabuf; 779 757 780 758 err_dmabuf: ··· 800 774 */ 801 775 int dma_buf_fd(struct dma_buf *dmabuf, int flags) 802 776 { 777 + int fd; 778 + 803 779 if (!dmabuf || !dmabuf->file) 804 780 return -EINVAL; 805 781 806 - return FD_ADD(flags, dmabuf->file); 782 + fd = FD_ADD(flags, dmabuf->file); 783 + if (fd >= 0) 784 + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); 785 + 786 + return fd; 807 787 } 808 788 EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); 809 789 ··· 824 792 struct dma_buf *dma_buf_get(int fd) 825 793 { 826 794 struct file *file; 795 + struct dma_buf *dmabuf; 827 796 828 797 file = fget(fd); 829 798 ··· 836 803 return ERR_PTR(-EINVAL); 837 804 } 838 805 839 - return file->private_data; 806 + dmabuf = file->private_data; 807 + 808 + DMA_BUF_TRACE(trace_dma_buf_get, dmabuf, fd); 809 + 810 + return dmabuf; 840 811 } 841 812 EXPORT_SYMBOL_NS_GPL(dma_buf_get, "DMA_BUF"); 842 813 ··· 860 823 return; 861 824 862 825 fput(dmabuf->file); 826 + 827 + DMA_BUF_TRACE(trace_dma_buf_put, dmabuf); 863 828 } 864 829 EXPORT_SYMBOL_NS_GPL(dma_buf_put, "DMA_BUF"); 865 830 ··· 1054 1015 list_add(&attach->node, &dmabuf->attachments); 1055 1016 dma_resv_unlock(dmabuf->resv); 1056 1017 1018 + DMA_BUF_TRACE(trace_dma_buf_dynamic_attach, dmabuf, attach, 1019 + dma_buf_attachment_is_dynamic(attach), dev); 1020 + 1057 1021 return attach; 1058 1022 1059 1023 err_attach: ··· 1100 1058 1101 1059 if (dmabuf->ops->detach) 1102 1060 dmabuf->ops->detach(dmabuf, attach); 1061 + 1062 + DMA_BUF_TRACE(trace_dma_buf_detach, dmabuf, attach, 1063 + dma_buf_attachment_is_dynamic(attach), attach->dev); 1103 1064 1104 1065 kfree(attach); 1105 1066 } ··· 1569 1524 /* readjust the vma */ 1570 1525 vma_set_file(vma, dmabuf->file); 1571 1526 vma->vm_pgoff = pgoff; 1527 + 1528 + DMA_BUF_TRACE(trace_dma_buf_mmap, dmabuf); 1572 1529 1573 1530 return dmabuf->ops->mmap(dmabuf, vma); 1574 1531 }
+5 -46
drivers/dma-buf/st-dma-fence.c
··· 33 33 kmem_cache_free(slab_fences, to_mock_fence(f)); 34 34 } 35 35 36 - struct wait_cb { 37 - struct dma_fence_cb cb; 38 - struct task_struct *task; 39 - }; 40 - 41 - static void mock_wakeup(struct dma_fence *f, struct dma_fence_cb *cb) 42 - { 43 - wake_up_process(container_of(cb, struct wait_cb, cb)->task); 44 - } 45 - 46 - static long mock_wait(struct dma_fence *f, bool intr, long timeout) 47 - { 48 - const int state = intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE; 49 - struct wait_cb cb = { .task = current }; 50 - 51 - if (dma_fence_add_callback(f, &cb.cb, mock_wakeup)) 52 - return timeout; 53 - 54 - while (timeout) { 55 - set_current_state(state); 56 - 57 - if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags)) 58 - break; 59 - 60 - if (signal_pending_state(state, current)) 61 - break; 62 - 63 - timeout = schedule_timeout(timeout); 64 - } 65 - __set_current_state(TASK_RUNNING); 66 - 67 - if (!dma_fence_remove_callback(f, &cb.cb)) 68 - return timeout; 69 - 70 - if (signal_pending_state(state, current)) 71 - return -ERESTARTSYS; 72 - 73 - return -ETIME; 74 - } 75 - 76 36 static const struct dma_fence_ops mock_ops = { 77 37 .get_driver_name = mock_name, 78 38 .get_timeline_name = mock_name, 79 - .wait = mock_wait, 80 39 .release = mock_fence_release, 81 40 }; 82 41 ··· 308 349 309 350 dma_fence_enable_sw_signaling(f); 310 351 311 - if (dma_fence_wait_timeout(f, false, 0) != -ETIME) { 352 + if (dma_fence_wait_timeout(f, false, 0) != 0) { 312 353 pr_err("Wait reported complete before being signaled\n"); 313 354 goto err_free; 314 355 } 315 356 316 357 dma_fence_signal(f); 317 358 318 - if (dma_fence_wait_timeout(f, false, 0) != 0) { 359 + if (dma_fence_wait_timeout(f, false, 0) != 1) { 319 360 pr_err("Wait reported incomplete after being signaled\n"); 320 361 goto err_free; 321 362 } ··· 352 393 353 394 dma_fence_enable_sw_signaling(wt.f); 354 395 355 - if (dma_fence_wait_timeout(wt.f, false, 1) != -ETIME) { 396 + if (dma_fence_wait_timeout(wt.f, false, 1) != 0) { 356 397 pr_err("Wait reported complete before being signaled\n"); 357 398 goto err_free; 358 399 } 359 400 360 401 mod_timer(&wt.timer, jiffies + 1); 361 402 362 - if (dma_fence_wait_timeout(wt.f, false, 2) == -ETIME) { 403 + if (dma_fence_wait_timeout(wt.f, false, HZ) == 0) { 363 404 if (timer_pending(&wt.timer)) { 364 - pr_notice("Timer did not fire within the jiffy!\n"); 405 + pr_notice("Timer did not fire within one HZ!\n"); 365 406 err = 0; /* not our fault! */ 366 407 } else { 367 408 pr_err("Wait reported incomplete after timeout\n");
+5 -2
drivers/gpu/drm/drm_gem_shmem_helper.c
··· 94 94 } 95 95 96 96 /** 97 - * drm_gem_shmem_init - Initialize an allocated object. 97 + * drm_gem_shmem_init - Initialize an allocated object of the given size 98 98 * @dev: DRM device 99 - * @obj: The allocated shmem GEM object. 99 + * @shmem: shmem GEM object to initialize 100 + * @size: Size of the object to initialize 101 + * 102 + * This function initializes an allocated shmem GEM object. 100 103 * 101 104 * Returns: 102 105 * 0 on success, or a negative error code on failure.
+20 -45
drivers/gpu/drm/drm_syncobj.c
··· 250 250 { 251 251 struct drm_syncobj *syncobj; 252 252 253 - spin_lock(&file_private->syncobj_table_lock); 253 + xa_lock(&file_private->syncobj_xa); 254 254 255 255 /* Check if we currently have a reference on the object */ 256 - syncobj = idr_find(&file_private->syncobj_idr, handle); 256 + syncobj = xa_load(&file_private->syncobj_xa, handle); 257 257 if (syncobj) 258 258 drm_syncobj_get(syncobj); 259 259 260 - spin_unlock(&file_private->syncobj_table_lock); 260 + xa_unlock(&file_private->syncobj_xa); 261 261 262 262 return syncobj; 263 263 } ··· 598 598 { 599 599 int ret; 600 600 601 - /* take a reference to put in the idr */ 601 + /* take a reference to put in the xarray */ 602 602 drm_syncobj_get(syncobj); 603 603 604 - idr_preload(GFP_KERNEL); 605 - spin_lock(&file_private->syncobj_table_lock); 606 - ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT); 607 - spin_unlock(&file_private->syncobj_table_lock); 608 - 609 - idr_preload_end(); 610 - 611 - if (ret < 0) { 604 + ret = xa_alloc(&file_private->syncobj_xa, handle, syncobj, xa_limit_32b, 605 + GFP_NOWAIT); 606 + if (ret) 612 607 drm_syncobj_put(syncobj); 613 - return ret; 614 - } 615 608 616 - *handle = ret; 617 - return 0; 609 + return ret; 618 610 } 619 611 EXPORT_SYMBOL(drm_syncobj_get_handle); 620 612 ··· 630 638 { 631 639 struct drm_syncobj *syncobj; 632 640 633 - spin_lock(&file_private->syncobj_table_lock); 634 - syncobj = idr_remove(&file_private->syncobj_idr, handle); 635 - spin_unlock(&file_private->syncobj_table_lock); 636 - 641 + syncobj = xa_erase(&file_private->syncobj_xa, handle); 637 642 if (!syncobj) 638 643 return -EINVAL; 639 644 ··· 711 722 if (fd_file(f)->f_op != &drm_syncobj_file_fops) 712 723 return -EINVAL; 713 724 714 - /* take a reference to put in the idr */ 725 + /* take a reference to put in the xarray */ 715 726 syncobj = fd_file(f)->private_data; 716 727 drm_syncobj_get(syncobj); 717 728 718 - idr_preload(GFP_KERNEL); 719 - spin_lock(&file_private->syncobj_table_lock); 720 - ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT); 721 - spin_unlock(&file_private->syncobj_table_lock); 722 - idr_preload_end(); 723 - 724 - if (ret > 0) { 725 - *handle = ret; 726 - ret = 0; 727 - } else 729 + ret = xa_alloc(&file_private->syncobj_xa, handle, syncobj, xa_limit_32b, 730 + GFP_NOWAIT); 731 + if (ret) 728 732 drm_syncobj_put(syncobj); 729 733 730 734 return ret; ··· 796 814 void 797 815 drm_syncobj_open(struct drm_file *file_private) 798 816 { 799 - idr_init_base(&file_private->syncobj_idr, 1); 800 - spin_lock_init(&file_private->syncobj_table_lock); 801 - } 802 - 803 - static int 804 - drm_syncobj_release_handle(int id, void *ptr, void *data) 805 - { 806 - struct drm_syncobj *syncobj = ptr; 807 - 808 - drm_syncobj_put(syncobj); 809 - return 0; 817 + xa_init_flags(&file_private->syncobj_xa, XA_FLAGS_ALLOC1); 810 818 } 811 819 812 820 /** ··· 810 838 void 811 839 drm_syncobj_release(struct drm_file *file_private) 812 840 { 813 - idr_for_each(&file_private->syncobj_idr, 814 - &drm_syncobj_release_handle, file_private); 815 - idr_destroy(&file_private->syncobj_idr); 841 + struct drm_syncobj *syncobj; 842 + unsigned long handle; 843 + 844 + xa_for_each(&file_private->syncobj_xa, handle, syncobj) 845 + drm_syncobj_put(syncobj); 846 + xa_destroy(&file_private->syncobj_xa); 816 847 } 817 848 818 849 int
+13
drivers/gpu/drm/panel/Kconfig
··· 781 781 depends on BACKLIGHT_CLASS_DEVICE 782 782 select VIDEOMODE_HELPERS 783 783 784 + config DRM_PANEL_SAMSUNG_LTL106HL02 785 + tristate "Samsung LTL106HL02 panel" 786 + depends on OF 787 + depends on DRM_MIPI_DSI 788 + depends on BACKLIGHT_CLASS_DEVICE 789 + select VIDEOMODE_HELPERS 790 + help 791 + Say Y here if you want to enable support for the Samsung LTL106HL02 792 + panel driver which is used in Microsoft Surface 2. 793 + 794 + To compile this driver as a module, choose M here: the module 795 + will be called panel-samsung-ltl106hl02. 796 + 784 797 config DRM_PANEL_SAMSUNG_S6E3FA7 785 798 tristate "Samsung S6E3FA7 panel driver" 786 799 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 76 76 obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o 77 77 obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o 78 78 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o 79 + obj-$(CONFIG_DRM_PANEL_SAMSUNG_LTL106HL02) += panel-samsung-ltl106hl02.o 79 80 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o 80 81 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D27A1) += panel-samsung-s6d27a1.o 81 82 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D7AA0) += panel-samsung-s6d7aa0.o
+34 -30
drivers/gpu/drm/panel/panel-lg-sw43408.c
··· 20 20 #include <drm/display/drm_dsc.h> 21 21 #include <drm/display/drm_dsc_helper.h> 22 22 23 - #define NUM_SUPPLIES 2 23 + static const struct regulator_bulk_data sw43408_supplies[] = { 24 + { .supply = "vddi", /* 1.8 V */ 25 + .init_load_uA = 62000 }, 26 + { .supply = "vpnl", /* 3.0 V */ 27 + .init_load_uA = 857000 }, 28 + }; 24 29 25 30 struct sw43408_panel { 26 31 struct drm_panel base; 27 32 struct mipi_dsi_device *link; 28 33 29 - struct regulator_bulk_data supplies[NUM_SUPPLIES]; 34 + struct regulator_bulk_data *supplies; 30 35 31 36 struct gpio_desc *reset_gpio; 32 37 ··· 57 52 58 53 gpiod_set_value(sw43408->reset_gpio, 1); 59 54 60 - ret = regulator_bulk_disable(ARRAY_SIZE(sw43408->supplies), sw43408->supplies); 55 + ret = regulator_bulk_disable(ARRAY_SIZE(sw43408_supplies), sw43408->supplies); 61 56 62 57 return ret ? : ctx.accum_err; 63 58 } ··· 124 119 return ctx.accum_err; 125 120 } 126 121 127 - static int sw43408_prepare(struct drm_panel *panel) 122 + static void sw43408_reset(struct sw43408_panel *ctx) 128 123 { 129 - struct sw43408_panel *ctx = to_panel_info(panel); 130 - int ret; 131 - 132 - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 133 - if (ret < 0) 134 - return ret; 135 - 136 - usleep_range(5000, 6000); 137 - 138 124 gpiod_set_value(ctx->reset_gpio, 0); 139 125 usleep_range(9000, 10000); 140 126 gpiod_set_value(ctx->reset_gpio, 1); 141 127 usleep_range(1000, 2000); 142 128 gpiod_set_value(ctx->reset_gpio, 0); 143 129 usleep_range(9000, 10000); 130 + } 131 + 132 + static int sw43408_prepare(struct drm_panel *panel) 133 + { 134 + struct sw43408_panel *ctx = to_panel_info(panel); 135 + int ret; 136 + 137 + ret = regulator_bulk_enable(ARRAY_SIZE(sw43408_supplies), ctx->supplies); 138 + if (ret < 0) 139 + return ret; 140 + 141 + usleep_range(5000, 6000); 142 + 143 + sw43408_reset(ctx); 144 144 145 145 ret = sw43408_program(panel); 146 146 if (ret) ··· 155 145 156 146 poweroff: 157 147 gpiod_set_value(ctx->reset_gpio, 1); 158 - regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 148 + regulator_bulk_disable(ARRAY_SIZE(sw43408_supplies), ctx->supplies); 159 149 return ret; 160 150 } 161 151 162 - static const struct drm_display_mode sw43408_mode = { 152 + static const struct drm_display_mode lh546wf1_ed01_mode = { 163 153 .clock = (1080 + 20 + 32 + 20) * (2160 + 20 + 4 + 20) * 60 / 1000, 164 154 165 155 .hdisplay = 1080, ··· 181 171 static int sw43408_get_modes(struct drm_panel *panel, 182 172 struct drm_connector *connector) 183 173 { 184 - return drm_connector_helper_get_modes_fixed(connector, &sw43408_mode); 174 + return drm_connector_helper_get_modes_fixed(connector, &lh546wf1_ed01_mode); 185 175 } 186 176 187 177 static int sw43408_backlight_update_status(struct backlight_device *bl) ··· 224 214 }; 225 215 226 216 static const struct of_device_id sw43408_of_match[] = { 227 - { .compatible = "lg,sw43408", }, 217 + { .compatible = "lg,sw43408", }, /* legacy */ 218 + { .compatible = "lg,sw43408-lh546wf1-ed01", }, 228 219 { /* sentinel */ } 229 220 }; 230 221 MODULE_DEVICE_TABLE(of, sw43408_of_match); ··· 235 224 struct device *dev = &ctx->link->dev; 236 225 int ret; 237 226 238 - ctx->supplies[0].supply = "vddi"; /* 1.88 V */ 239 - ctx->supplies[0].init_load_uA = 62000; 240 - ctx->supplies[1].supply = "vpnl"; /* 3.0 V */ 241 - ctx->supplies[1].init_load_uA = 857000; 242 - 243 - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 244 - ctx->supplies); 227 + ret = devm_regulator_bulk_get_const(dev, 228 + ARRAY_SIZE(sw43408_supplies), 229 + sw43408_supplies, 230 + &ctx->supplies); 245 231 if (ret < 0) 246 232 return ret; 247 233 248 234 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 249 235 if (IS_ERR(ctx->reset_gpio)) { 250 - ret = PTR_ERR(ctx->reset_gpio); 251 - return dev_err_probe(dev, ret, "cannot get reset gpio\n"); 236 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 237 + "Failed to get reset-gpios\n"); 252 238 } 253 239 254 240 ret = sw43408_backlight_init(ctx); ··· 301 293 { 302 294 struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi); 303 295 int ret; 304 - 305 - ret = sw43408_unprepare(&ctx->base); 306 - if (ret < 0) 307 - dev_err(&dsi->dev, "failed to unprepare panel: %d\n", ret); 308 296 309 297 ret = mipi_dsi_detach(dsi); 310 298 if (ret < 0)
+70 -105
drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
··· 109 109 return container_of(panel, struct otm8009a, panel); 110 110 } 111 111 112 - static void otm8009a_dcs_write_buf(struct otm8009a *ctx, const void *data, 113 - size_t len) 114 - { 115 - struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 116 - 117 - if (mipi_dsi_dcs_write_buffer(dsi, data, len) < 0) 118 - dev_warn(ctx->dev, "mipi dsi dcs write buffer failed\n"); 119 - } 120 - 121 - #define dcs_write_seq(ctx, seq...) \ 122 - ({ \ 123 - static const u8 d[] = { seq }; \ 124 - otm8009a_dcs_write_buf(ctx, d, ARRAY_SIZE(d)); \ 125 - }) 126 - 127 112 #define dcs_write_cmd_at(ctx, cmd, seq...) \ 128 113 ({ \ 129 - dcs_write_seq(ctx, MCS_ADRSFT, (cmd) & 0xFF); \ 130 - dcs_write_seq(ctx, (cmd) >> 8, seq); \ 114 + mipi_dsi_dcs_write_seq_multi(ctx, MCS_ADRSFT, (cmd) & 0xFF); \ 115 + mipi_dsi_dcs_write_seq_multi(ctx, (cmd) >> 8, seq); \ 131 116 }) 132 117 133 118 static int otm8009a_init_sequence(struct otm8009a *ctx) 134 119 { 135 120 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 136 - int ret; 121 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 137 122 138 123 /* Enter CMD2 */ 139 - dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01); 124 + dcs_write_cmd_at(&dsi_ctx, MCS_CMD2_ENA1, 0x80, 0x09, 0x01); 140 125 141 126 /* Enter Orise Command2 */ 142 - dcs_write_cmd_at(ctx, MCS_CMD2_ENA2, 0x80, 0x09); 127 + dcs_write_cmd_at(&dsi_ctx, MCS_CMD2_ENA2, 0x80, 0x09); 143 128 144 - dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL, 0x30); 145 - mdelay(10); 129 + dcs_write_cmd_at(&dsi_ctx, MCS_SD_PCH_CTRL, 0x30); 130 + mipi_dsi_msleep(&dsi_ctx, 10); 146 131 147 - dcs_write_cmd_at(ctx, MCS_NO_DOC1, 0x40); 148 - mdelay(10); 132 + dcs_write_cmd_at(&dsi_ctx, MCS_NO_DOC1, 0x40); 133 + mipi_dsi_msleep(&dsi_ctx, 10); 149 134 150 - dcs_write_cmd_at(ctx, MCS_PWR_CTRL4 + 1, 0xA9); 151 - dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 1, 0x34); 152 - dcs_write_cmd_at(ctx, MCS_P_DRV_M, 0x50); 153 - dcs_write_cmd_at(ctx, MCS_VCOMDC, 0x4E); 154 - dcs_write_cmd_at(ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */ 155 - dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 2, 0x01); 156 - dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 5, 0x34); 157 - dcs_write_cmd_at(ctx, MCS_PWR_CTRL2 + 4, 0x33); 158 - dcs_write_cmd_at(ctx, MCS_GVDDSET, 0x79, 0x79); 159 - dcs_write_cmd_at(ctx, MCS_SD_CTRL + 1, 0x1B); 160 - dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 2, 0x83); 161 - dcs_write_cmd_at(ctx, MCS_SD_PCH_CTRL + 1, 0x83); 162 - dcs_write_cmd_at(ctx, MCS_RGB_VID_SET, 0x0E); 163 - dcs_write_cmd_at(ctx, MCS_PANSET, 0x00, 0x01); 135 + dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL4 + 1, 0xA9); 136 + dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 1, 0x34); 137 + dcs_write_cmd_at(&dsi_ctx, MCS_P_DRV_M, 0x50); 138 + dcs_write_cmd_at(&dsi_ctx, MCS_VCOMDC, 0x4E); 139 + dcs_write_cmd_at(&dsi_ctx, MCS_OSC_ADJ, 0x66); /* 65Hz */ 140 + dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 2, 0x01); 141 + dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 5, 0x34); 142 + dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL2 + 4, 0x33); 143 + dcs_write_cmd_at(&dsi_ctx, MCS_GVDDSET, 0x79, 0x79); 144 + dcs_write_cmd_at(&dsi_ctx, MCS_SD_CTRL + 1, 0x1B); 145 + dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL1 + 2, 0x83); 146 + dcs_write_cmd_at(&dsi_ctx, MCS_SD_PCH_CTRL + 1, 0x83); 147 + dcs_write_cmd_at(&dsi_ctx, MCS_RGB_VID_SET, 0x0E); 148 + dcs_write_cmd_at(&dsi_ctx, MCS_PANSET, 0x00, 0x01); 164 149 165 - dcs_write_cmd_at(ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00); 166 - dcs_write_cmd_at(ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 150 + dcs_write_cmd_at(&dsi_ctx, MCS_GOAVST, 0x85, 0x01, 0x00, 0x84, 0x01, 0x00); 151 + dcs_write_cmd_at(&dsi_ctx, MCS_GOACLKA1, 0x18, 0x04, 0x03, 0x39, 0x00, 0x00, 167 152 0x00, 0x18, 0x03, 0x03, 0x3A, 0x00, 0x00, 0x00); 168 - dcs_write_cmd_at(ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 153 + dcs_write_cmd_at(&dsi_ctx, MCS_GOACLKA3, 0x18, 0x02, 0x03, 0x3B, 0x00, 0x00, 169 154 0x00, 0x18, 0x01, 0x03, 0x3C, 0x00, 0x00, 0x00); 170 - dcs_write_cmd_at(ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 155 + dcs_write_cmd_at(&dsi_ctx, MCS_GOAECLK, 0x01, 0x01, 0x20, 0x20, 0x00, 0x00, 171 156 0x01, 0x02, 0x00, 0x00); 172 157 173 - dcs_write_cmd_at(ctx, MCS_NO_DOC2, 0x00); 158 + dcs_write_cmd_at(&dsi_ctx, MCS_NO_DOC2, 0x00); 174 159 175 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 176 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 161 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177 162 0, 0, 0, 0, 0); 178 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 179 164 0, 0, 0, 0, 0); 180 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 181 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 165 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 166 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET5, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 182 167 0, 0, 0, 0, 0); 183 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 168 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET6, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 184 169 4, 0, 0, 0, 0); 185 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 186 - dcs_write_cmd_at(ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 170 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 171 + dcs_write_cmd_at(&dsi_ctx, MCS_PANCTRLSET8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 187 172 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); 188 173 189 - dcs_write_cmd_at(ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 174 + dcs_write_cmd_at(&dsi_ctx, MCS_PANU2D1, 0x00, 0x26, 0x09, 0x0B, 0x01, 0x25, 190 175 0x00, 0x00, 0x00, 0x00); 191 - dcs_write_cmd_at(ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 176 + dcs_write_cmd_at(&dsi_ctx, MCS_PANU2D2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 192 177 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x0A, 0x0C, 0x02); 193 - dcs_write_cmd_at(ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 178 + dcs_write_cmd_at(&dsi_ctx, MCS_PANU2D3, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 194 179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 195 - dcs_write_cmd_at(ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 180 + dcs_write_cmd_at(&dsi_ctx, MCS_PAND2U1, 0x00, 0x25, 0x0C, 0x0A, 0x02, 0x26, 196 181 0x00, 0x00, 0x00, 0x00); 197 - dcs_write_cmd_at(ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 182 + dcs_write_cmd_at(&dsi_ctx, MCS_PAND2U2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 198 183 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0B, 0x09, 0x01); 199 - dcs_write_cmd_at(ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 184 + dcs_write_cmd_at(&dsi_ctx, MCS_PAND2U3, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 200 185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 201 186 202 - dcs_write_cmd_at(ctx, MCS_PWR_CTRL1 + 1, 0x66); 187 + dcs_write_cmd_at(&dsi_ctx, MCS_PWR_CTRL1 + 1, 0x66); 203 188 204 - dcs_write_cmd_at(ctx, MCS_NO_DOC3, 0x06); 189 + dcs_write_cmd_at(&dsi_ctx, MCS_NO_DOC3, 0x06); 205 190 206 - dcs_write_cmd_at(ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 191 + dcs_write_cmd_at(&dsi_ctx, MCS_GMCT2_2P, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 207 192 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 208 193 0x01); 209 - dcs_write_cmd_at(ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 194 + dcs_write_cmd_at(&dsi_ctx, MCS_GMCT2_2N, 0x00, 0x09, 0x0F, 0x0E, 0x07, 0x10, 210 195 0x0B, 0x0A, 0x04, 0x07, 0x0B, 0x08, 0x0F, 0x10, 0x0A, 211 196 0x01); 212 197 213 198 /* Exit CMD2 */ 214 - dcs_write_cmd_at(ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF); 199 + dcs_write_cmd_at(&dsi_ctx, MCS_CMD2_ENA1, 0xFF, 0xFF, 0xFF); 215 200 216 - ret = mipi_dsi_dcs_nop(dsi); 217 - if (ret) 218 - return ret; 201 + mipi_dsi_dcs_nop_multi(&dsi_ctx); 219 202 220 - ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 221 - if (ret) 222 - return ret; 223 - 224 - /* Wait for sleep out exit */ 225 - mdelay(120); 203 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 204 + mipi_dsi_msleep(&dsi_ctx, 120); 226 205 227 206 /* Default portrait 480x800 rgb24 */ 228 - dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 207 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); 229 208 230 - ret = mipi_dsi_dcs_set_column_address(dsi, 0, OTM8009A_HDISPLAY - 1); 231 - if (ret) 232 - return ret; 209 + mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, OTM8009A_HDISPLAY - 1); 233 210 234 - ret = mipi_dsi_dcs_set_page_address(dsi, 0, OTM8009A_VDISPLAY - 1); 235 - if (ret) 236 - return ret; 211 + mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, OTM8009A_VDISPLAY - 1); 237 212 238 213 /* See otm8009a driver documentation for pixel format descriptions */ 239 - ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT | 214 + mipi_dsi_dcs_set_pixel_format_multi(&dsi_ctx, MIPI_DCS_PIXEL_FMT_24BIT | 240 215 MIPI_DCS_PIXEL_FMT_24BIT << 4); 241 - if (ret) 242 - return ret; 243 216 244 217 /* Disable CABC feature */ 245 - dcs_write_seq(ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 218 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00); 246 219 247 - ret = mipi_dsi_dcs_set_display_on(dsi); 248 - if (ret) 249 - return ret; 220 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 250 221 251 - ret = mipi_dsi_dcs_nop(dsi); 252 - if (ret) 253 - return ret; 222 + mipi_dsi_dcs_nop_multi(&dsi_ctx); 254 223 255 224 /* Send Command GRAM memory write (no parameters) */ 256 - dcs_write_seq(ctx, MIPI_DCS_WRITE_MEMORY_START); 225 + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_MEMORY_START); 257 226 258 227 /* Wait a short while to let the panel be ready before the 1st frame */ 259 - mdelay(10); 228 + mipi_dsi_msleep(&dsi_ctx, 10); 260 229 261 - return 0; 230 + return dsi_ctx.accum_err; 262 231 } 263 232 264 233 static int otm8009a_disable(struct drm_panel *panel) 265 234 { 266 235 struct otm8009a *ctx = panel_to_otm8009a(panel); 267 236 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 268 - int ret; 237 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 269 238 270 239 backlight_disable(ctx->bl_dev); 271 240 272 - ret = mipi_dsi_dcs_set_display_off(dsi); 273 - if (ret) 274 - return ret; 241 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 242 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 243 + mipi_dsi_msleep(&dsi_ctx, 120); 275 244 276 - ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 277 - if (ret) 278 - return ret; 279 - 280 - msleep(120); 281 - 282 - return 0; 245 + return dsi_ctx.accum_err; 283 246 } 284 247 285 248 static int otm8009a_unprepare(struct drm_panel *panel) ··· 346 383 static int otm8009a_backlight_update_status(struct backlight_device *bd) 347 384 { 348 385 struct otm8009a *ctx = bl_get_data(bd); 386 + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 387 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 349 388 u8 data[2]; 350 389 351 390 if (!ctx->prepared) { ··· 362 397 */ 363 398 data[0] = MIPI_DCS_SET_DISPLAY_BRIGHTNESS; 364 399 data[1] = bd->props.brightness; 365 - otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data)); 400 + mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, data, ARRAY_SIZE(data)); 366 401 367 402 /* set Brightness Control & Backlight on */ 368 403 data[1] = 0x24; ··· 374 409 375 410 /* Update Brightness Control & Backlight */ 376 411 data[0] = MIPI_DCS_WRITE_CONTROL_DISPLAY; 377 - otm8009a_dcs_write_buf(ctx, data, ARRAY_SIZE(data)); 412 + mipi_dsi_dcs_write_buffer_multi(&dsi_ctx, data, ARRAY_SIZE(data)); 378 413 379 - return 0; 414 + return dsi_ctx.accum_err; 380 415 } 381 416 382 417 static const struct backlight_ops otm8009a_backlight_ops = {
+179
drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/array_size.h> 4 + #include <linux/delay.h> 5 + #include <linux/err.h> 6 + #include <linux/gpio/consumer.h> 7 + #include <linux/mod_devicetable.h> 8 + #include <linux/module.h> 9 + #include <linux/property.h> 10 + #include <linux/regulator/consumer.h> 11 + 12 + #include <video/mipi_display.h> 13 + 14 + #include <drm/drm_mipi_dsi.h> 15 + #include <drm/drm_modes.h> 16 + #include <drm/drm_panel.h> 17 + #include <drm/drm_probe_helper.h> 18 + 19 + struct samsung_ltl106hl02 { 20 + struct drm_panel panel; 21 + struct mipi_dsi_device *dsi; 22 + 23 + struct regulator *supply; 24 + struct gpio_desc *reset_gpio; 25 + }; 26 + 27 + static inline struct samsung_ltl106hl02 *to_samsung_ltl106hl02(struct drm_panel *panel) 28 + { 29 + return container_of(panel, struct samsung_ltl106hl02, panel); 30 + } 31 + 32 + static void samsung_ltl106hl02_reset(struct samsung_ltl106hl02 *ctx) 33 + { 34 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 35 + usleep_range(10000, 11000); 36 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 37 + usleep_range(2000, 3000); 38 + } 39 + 40 + static int samsung_ltl106hl02_prepare(struct drm_panel *panel) 41 + { 42 + struct samsung_ltl106hl02 *ctx = to_samsung_ltl106hl02(panel); 43 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 44 + struct device *dev = &ctx->dsi->dev; 45 + int ret; 46 + 47 + ret = regulator_enable(ctx->supply); 48 + if (ret < 0) { 49 + dev_err(dev, "failed to enable power supply %d\n", ret); 50 + return ret; 51 + } 52 + 53 + if (ctx->reset_gpio) 54 + samsung_ltl106hl02_reset(ctx); 55 + 56 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 57 + mipi_dsi_msleep(&dsi_ctx, 70); 58 + 59 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 60 + mipi_dsi_msleep(&dsi_ctx, 5); 61 + 62 + return dsi_ctx.accum_err; 63 + } 64 + 65 + static int samsung_ltl106hl02_unprepare(struct drm_panel *panel) 66 + { 67 + struct samsung_ltl106hl02 *ctx = to_samsung_ltl106hl02(panel); 68 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 69 + 70 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 71 + mipi_dsi_msleep(&dsi_ctx, 50); 72 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 73 + mipi_dsi_msleep(&dsi_ctx, 150); 74 + 75 + if (ctx->reset_gpio) 76 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 77 + 78 + regulator_disable(ctx->supply); 79 + 80 + return 0; 81 + } 82 + 83 + static const struct drm_display_mode samsung_ltl106hl02_mode = { 84 + .clock = (1920 + 32 + 32 + 64) * (1080 + 6 + 3 + 22) * 60 / 1000, 85 + .hdisplay = 1920, 86 + .hsync_start = 1920 + 32, 87 + .hsync_end = 1920 + 32 + 32, 88 + .htotal = 1920 + 32 + 32 + 64, 89 + .vdisplay = 1080, 90 + .vsync_start = 1080 + 6, 91 + .vsync_end = 1080 + 6 + 3, 92 + .vtotal = 1080 + 6 + 3 + 22, 93 + .width_mm = 235, 94 + .height_mm = 132, 95 + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 96 + }; 97 + 98 + static int samsung_ltl106hl02_get_modes(struct drm_panel *panel, 99 + struct drm_connector *connector) 100 + { 101 + return drm_connector_helper_get_modes_fixed(connector, &samsung_ltl106hl02_mode); 102 + } 103 + 104 + static const struct drm_panel_funcs samsung_ltl106hl02_panel_funcs = { 105 + .prepare = samsung_ltl106hl02_prepare, 106 + .unprepare = samsung_ltl106hl02_unprepare, 107 + .get_modes = samsung_ltl106hl02_get_modes, 108 + }; 109 + 110 + static int samsung_ltl106hl02_probe(struct mipi_dsi_device *dsi) 111 + { 112 + struct device *dev = &dsi->dev; 113 + struct samsung_ltl106hl02 *ctx; 114 + int ret; 115 + 116 + ctx = devm_drm_panel_alloc(dev, struct samsung_ltl106hl02, panel, 117 + &samsung_ltl106hl02_panel_funcs, 118 + DRM_MODE_CONNECTOR_DSI); 119 + if (IS_ERR(ctx)) 120 + return PTR_ERR(ctx); 121 + 122 + ctx->supply = devm_regulator_get(dev, "power"); 123 + if (IS_ERR(ctx->supply)) 124 + return dev_err_probe(dev, PTR_ERR(ctx->supply), 125 + "Failed to get power regulator\n"); 126 + 127 + ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 128 + if (IS_ERR(ctx->reset_gpio)) 129 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 130 + "Failed to get reset-gpios\n"); 131 + 132 + ctx->dsi = dsi; 133 + mipi_dsi_set_drvdata(dsi, ctx); 134 + 135 + dsi->lanes = 4; 136 + dsi->format = MIPI_DSI_FMT_RGB888; 137 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; 138 + 139 + ret = drm_panel_of_backlight(&ctx->panel); 140 + if (ret) 141 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 142 + 143 + drm_panel_add(&ctx->panel); 144 + 145 + ret = devm_mipi_dsi_attach(dev, dsi); 146 + if (ret < 0) { 147 + drm_panel_remove(&ctx->panel); 148 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 149 + } 150 + 151 + return 0; 152 + } 153 + 154 + static void samsung_ltl106hl02_remove(struct mipi_dsi_device *dsi) 155 + { 156 + struct samsung_ltl106hl02 *ctx = mipi_dsi_get_drvdata(dsi); 157 + 158 + drm_panel_remove(&ctx->panel); 159 + } 160 + 161 + static const struct of_device_id samsung_ltl106hl02_of_match[] = { 162 + { .compatible = "samsung,ltl106hl02-001" }, 163 + { /* sentinel */ } 164 + }; 165 + MODULE_DEVICE_TABLE(of, samsung_ltl106hl02_of_match); 166 + 167 + static struct mipi_dsi_driver samsung_ltl106hl02_driver = { 168 + .driver = { 169 + .name = "panel-samsung-ltl106hl02", 170 + .of_match_table = samsung_ltl106hl02_of_match, 171 + }, 172 + .probe = samsung_ltl106hl02_probe, 173 + .remove = samsung_ltl106hl02_remove, 174 + }; 175 + module_mipi_dsi_driver(samsung_ltl106hl02_driver); 176 + 177 + MODULE_AUTHOR("Anton Bambura <jenneron@protonmail.com>"); 178 + MODULE_DESCRIPTION("DRM driver for Samsung LTL106HL02 video mode DSI panel"); 179 + MODULE_LICENSE("GPL");
+28
drivers/gpu/drm/panel/panel-simple.c
··· 2509 2509 .connector_type = DRM_MODE_CONNECTOR_LVDS, 2510 2510 }; 2511 2511 2512 + static const struct display_timing hannstar_hsd156juw2_timing = { 2513 + .pixelclock = { 66000000, 72800000, 80500000 }, 2514 + .hactive = { 1920, 1920, 1920 }, 2515 + .hfront_porch = { 20, 30, 30 }, 2516 + .hback_porch = { 20, 30, 30 }, 2517 + .hsync_len = { 50, 60, 90 }, 2518 + .vactive = { 1080, 1080, 1080 }, 2519 + .vfront_porch = { 1, 2, 4 }, 2520 + .vback_porch = { 1, 2, 4 }, 2521 + .vsync_len = { 3, 40, 80 }, 2522 + .flags = DISPLAY_FLAGS_DE_HIGH, 2523 + }; 2524 + 2525 + static const struct panel_desc hannstar_hsd156juw2 = { 2526 + .timings = &hannstar_hsd156juw2_timing, 2527 + .num_timings = 1, 2528 + .bpc = 8, 2529 + .size = { 2530 + .width = 344, 2531 + .height = 194, 2532 + }, 2533 + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 2534 + .connector_type = DRM_MODE_CONNECTOR_LVDS, 2535 + }; 2536 + 2512 2537 static const struct drm_display_mode hitachi_tx23d38vm0caa_mode = { 2513 2538 .clock = 33333, 2514 2539 .hdisplay = 800, ··· 5278 5253 }, { 5279 5254 .compatible = "hannstar,hsd101pww2", 5280 5255 .data = &hannstar_hsd101pww2, 5256 + }, { 5257 + .compatible = "hannstar,hsd156juw2", 5258 + .data = &hannstar_hsd156juw2, 5281 5259 }, { 5282 5260 .compatible = "hit,tx23d38vm0caa", 5283 5261 .data = &hitachi_tx23d38vm0caa
+4 -2
drivers/gpu/drm/panthor/panthor_drv.c
··· 923 923 } 924 924 925 925 if ((args->flags & DRM_PANTHOR_BO_NO_MMAP) && 926 - (args->flags & DRM_PANTHOR_BO_WB_MMAP)) 927 - return -EINVAL; 926 + (args->flags & DRM_PANTHOR_BO_WB_MMAP)) { 927 + ret = -EINVAL; 928 + goto out_dev_exit; 929 + } 928 930 929 931 if (args->exclusive_vm_id) { 930 932 vm = panthor_vm_pool_get_vm(pfile->vms, args->exclusive_vm_id);
-4
drivers/gpu/drm/panthor/panthor_fw.c
··· 1261 1261 if (ptdev->fw->irq.irq) 1262 1262 panthor_job_irq_suspend(&ptdev->fw->irq); 1263 1263 1264 - panthor_fw_halt_mcu(ptdev); 1265 - if (!panthor_fw_wait_mcu_halted(ptdev)) 1266 - drm_warn(&ptdev->base, "Failed to halt MCU on unplug"); 1267 - 1268 1264 panthor_fw_stop(ptdev); 1269 1265 } 1270 1266
+4
drivers/gpu/drm/panthor/panthor_sched.c
··· 23 23 #include <linux/module.h> 24 24 #include <linux/platform_device.h> 25 25 #include <linux/pm_runtime.h> 26 + #include <linux/rcupdate.h> 26 27 27 28 #include "panthor_devfreq.h" 28 29 #include "panthor_device.h" ··· 943 942 struct panthor_group, 944 943 release_work); 945 944 u32 i; 945 + 946 + /* dma-fences may still be accessing group->queues under rcu lock. */ 947 + synchronize_rcu(); 946 948 947 949 for (i = 0; i < group->queue_count; i++) 948 950 group_free_queue(group, group->queues[i]);
+453
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
··· 7 7 8 8 #include <linux/bitfield.h> 9 9 #include <linux/clk.h> 10 + #include <linux/clk/renesas.h> 10 11 #include <linux/delay.h> 11 12 #include <linux/dma-mapping.h> 12 13 #include <linux/io.h> ··· 33 32 34 33 #include "rzg2l_mipi_dsi_regs.h" 35 34 35 + MODULE_IMPORT_NS("RZV2H_CPG"); 36 + 36 37 #define RZG2L_DCS_BUF_SIZE 128 /* Maximum DCS buffer size in external memory. */ 37 38 38 39 #define RZ_MIPI_DSI_FEATURE_16BPP BIT(0) ··· 49 46 u64 *hsfreq_millihz); 50 47 unsigned int (*dphy_mode_clk_check)(struct rzg2l_mipi_dsi *dsi, 51 48 unsigned long mode_freq); 49 + struct { 50 + const struct rzv2h_pll_limits **limits; 51 + const u8 *table; 52 + const u8 table_size; 53 + } cpg_plldsi; 52 54 u32 phy_reg_offset; 53 55 u32 link_reg_offset; 54 56 unsigned long min_dclk; 55 57 unsigned long max_dclk; 56 58 u8 features; 59 + }; 60 + 61 + struct rzv2h_dsi_mode_calc { 62 + unsigned long mode_freq_khz; 63 + struct rzv2h_pll_pars dsi_parameters; 57 64 }; 58 65 59 66 struct rzg2l_mipi_dsi { ··· 81 68 struct drm_bridge *next_bridge; 82 69 83 70 struct clk *vclk; 71 + struct clk *lpclk; 84 72 85 73 enum mipi_dsi_pixel_format format; 86 74 unsigned int num_data_lanes; 87 75 unsigned int lanes; 88 76 unsigned long mode_flags; 89 77 78 + struct rzv2h_dsi_mode_calc mode_calc; 79 + 90 80 /* DCS buffer pointers when using external memory. */ 91 81 dma_addr_t dcs_buf_phys; 92 82 u8 *dcs_buf_virt; 83 + }; 84 + 85 + static const struct rzv2h_pll_limits rzv2h_plldsi_div_limits = { 86 + .fout = { .min = 80 * MEGA, .max = 1500 * MEGA }, 87 + .fvco = { .min = 1050 * MEGA, .max = 2100 * MEGA }, 88 + .m = { .min = 64, .max = 1023 }, 89 + .p = { .min = 1, .max = 4 }, 90 + .s = { .min = 0, .max = 5 }, 91 + .k = { .min = -32768, .max = 32767 }, 93 92 }; 94 93 95 94 static inline struct rzg2l_mipi_dsi * ··· 216 191 .ths_exit = 13, 217 192 .tlpx = 6, 218 193 }, 194 + }; 195 + 196 + /** 197 + * struct rzv2h_mipi_dsi_timings - Timing parameter table structure 198 + * 199 + * @hsfreq: Pointer to frequency threshold array 200 + * @len: Number of entries in the hsfreq array 201 + * @base_value: Base register value offset for this timing parameter 202 + * 203 + * Each timing parameter (TCLK*, THS*, etc.) has its own table with 204 + * frequency thresholds and corresponding base register values. 205 + */ 206 + struct rzv2h_mipi_dsi_timings { 207 + const u8 *hsfreq; 208 + u8 len; 209 + u8 base_value; 210 + }; 211 + 212 + /* 213 + * enum rzv2h_dsi_timing_idx - MIPI DSI timing parameter indices 214 + * 215 + * These enums correspond to different MIPI DSI PHY timing parameters. 216 + */ 217 + enum rzv2h_dsi_timing_idx { 218 + TCLKPRPRCTL, 219 + TCLKZEROCTL, 220 + TCLKPOSTCTL, 221 + TCLKTRAILCTL, 222 + THSPRPRCTL, 223 + THSZEROCTL, 224 + THSTRAILCTL, 225 + TLPXCTL, 226 + THSEXITCTL, 227 + }; 228 + 229 + /* 230 + * RZ/V2H(P) Frequency threshold lookup tables for D-PHY timing parameters 231 + * 232 + * - Each array contains frequency thresholds (in units of 10 Mbps), 233 + * taken directly from the table 9.5-4 hardware manual. 234 + * - These thresholds define the frequency ranges for which timing 235 + * register values must be programmed. 236 + * - The actual register value is calculated in 237 + * rzv2h_dphy_find_timings_val(): 238 + * 239 + * register_value = timings->base_value + table_index 240 + * 241 + * Example (TCLKPRPRCTL, from HW manual): 242 + * 0-150 Mbps -> index 0 -> register_value = base + 0 = 0 + 0 = 0 243 + * 151-260 Mbps -> index 1 -> register_value = base + 1 = 0 + 1 = 1 244 + * 261-370 Mbps -> index 2 -> register_value = base + 2 = 0 + 2 = 2 245 + * 246 + * Each of the following arrays corresponds to a specific timing 247 + * parameter (TCLKPRPRCTL, TCLKZEROCTL, TCLKPOSTCTL, etc.). 248 + */ 249 + static const u8 tclkprprctl[] = { 250 + 15, 26, 37, 47, 58, 69, 79, 90, 101, 111, 122, 133, 143, 150, 251 + }; 252 + 253 + static const u8 tclkzeroctl[] = { 254 + 9, 11, 13, 15, 18, 21, 23, 24, 25, 27, 29, 31, 34, 36, 38, 255 + 41, 43, 45, 47, 50, 52, 54, 57, 59, 61, 63, 66, 68, 70, 73, 256 + 75, 77, 79, 82, 84, 86, 89, 91, 93, 95, 98, 100, 102, 105, 257 + 107, 109, 111, 114, 116, 118, 121, 123, 125, 127, 130, 132, 258 + 134, 137, 139, 141, 143, 146, 148, 150, 259 + }; 260 + 261 + static const u8 tclkpostctl[] = { 262 + 8, 21, 34, 48, 61, 74, 88, 101, 114, 128, 141, 150, 263 + }; 264 + 265 + static const u8 tclktrailctl[] = { 266 + 14, 25, 37, 48, 59, 71, 82, 94, 105, 117, 128, 139, 150, 267 + }; 268 + 269 + static const u8 thsprprctl[] = { 270 + 11, 19, 29, 40, 50, 61, 72, 82, 93, 103, 114, 125, 135, 146, 150, 271 + }; 272 + 273 + static const u8 thszeroctl[] = { 274 + 18, 24, 29, 35, 40, 46, 51, 57, 62, 68, 73, 79, 84, 90, 275 + 95, 101, 106, 112, 117, 123, 128, 134, 139, 145, 150, 276 + }; 277 + 278 + static const u8 thstrailctl[] = { 279 + 10, 21, 32, 42, 53, 64, 75, 85, 96, 107, 118, 128, 139, 150, 280 + }; 281 + 282 + static const u8 tlpxctl[] = { 283 + 13, 26, 39, 53, 66, 79, 93, 106, 119, 133, 146, 150, 284 + }; 285 + 286 + static const u8 thsexitctl[] = { 287 + 15, 23, 31, 39, 47, 55, 63, 71, 79, 87, 288 + 95, 103, 111, 119, 127, 135, 143, 150, 289 + }; 290 + 291 + /* 292 + * rzv2h_dsi_timings_tables - main timing parameter lookup table 293 + * Maps timing parameter enum to its frequency table, array length and 294 + * base register offset value. 295 + */ 296 + static const struct rzv2h_mipi_dsi_timings rzv2h_dsi_timings_tables[] = { 297 + [TCLKPRPRCTL] = { 298 + .hsfreq = tclkprprctl, 299 + .len = ARRAY_SIZE(tclkprprctl), 300 + .base_value = 0, 301 + }, 302 + [TCLKZEROCTL] = { 303 + .hsfreq = tclkzeroctl, 304 + .len = ARRAY_SIZE(tclkzeroctl), 305 + .base_value = 2, 306 + }, 307 + [TCLKPOSTCTL] = { 308 + .hsfreq = tclkpostctl, 309 + .len = ARRAY_SIZE(tclkpostctl), 310 + .base_value = 6, 311 + }, 312 + [TCLKTRAILCTL] = { 313 + .hsfreq = tclktrailctl, 314 + .len = ARRAY_SIZE(tclktrailctl), 315 + .base_value = 1, 316 + }, 317 + [THSPRPRCTL] = { 318 + .hsfreq = thsprprctl, 319 + .len = ARRAY_SIZE(thsprprctl), 320 + .base_value = 0, 321 + }, 322 + [THSZEROCTL] = { 323 + .hsfreq = thszeroctl, 324 + .len = ARRAY_SIZE(thszeroctl), 325 + .base_value = 0, 326 + }, 327 + [THSTRAILCTL] = { 328 + .hsfreq = thstrailctl, 329 + .len = ARRAY_SIZE(thstrailctl), 330 + .base_value = 3, 331 + }, 332 + [TLPXCTL] = { 333 + .hsfreq = tlpxctl, 334 + .len = ARRAY_SIZE(tlpxctl), 335 + .base_value = 0, 336 + }, 337 + [THSEXITCTL] = { 338 + .hsfreq = thsexitctl, 339 + .len = ARRAY_SIZE(thsexitctl), 340 + .base_value = 1, 341 + }, 342 + }; 343 + 344 + /** 345 + * rzv2h_dphy_find_ulpsexit - Find ULP Exit timing value based on frequency 346 + * The function maps frequency ranges to ULP exit timing values. 347 + * Thresholds in the local hsfreq[] are expressed in Hz already. 348 + * 349 + * @freq: Input frequency in Hz 350 + * 351 + * Return: ULP exit timing value 352 + */ 353 + static u16 rzv2h_dphy_find_ulpsexit(unsigned long freq) 354 + { 355 + /* Frequency thresholds in Hz for ULP exit timing selection */ 356 + static const unsigned long hsfreq[] = { 357 + 1953125UL, 358 + 3906250UL, 359 + 7812500UL, 360 + 15625000UL, 361 + }; 362 + /* Corresponding ULP exit timing values for each frequency range */ 363 + static const u16 ulpsexit[] = {49, 98, 195, 391}; 364 + unsigned int i; 365 + 366 + /* Find the appropriate frequency range */ 367 + for (i = 0; i < ARRAY_SIZE(hsfreq); i++) { 368 + if (freq <= hsfreq[i]) 369 + break; 370 + } 371 + 372 + /* If frequency exceeds all thresholds, use the highest range */ 373 + if (i == ARRAY_SIZE(hsfreq)) 374 + i--; 375 + 376 + return ulpsexit[i]; 377 + } 378 + 379 + /** 380 + * rzv2h_dphy_find_timings_val - Find timing parameter value from lookup tables 381 + * @freq: Input frequency in Hz 382 + * @index: Index to select timing parameter table (see enum rzv2h_dsi_timing_idx) 383 + * 384 + * Selects the timing table for the requested parameter, finds the 385 + * frequency range entry and returns the register value to program: 386 + * 387 + * register_value = timings->base_value + table_index 388 + * 389 + * Note: frequency table entries are stored as small integers (units of 10): 390 + * threshold_in_hz = (unsigned long)table_entry * 10 * MEGA 391 + * 392 + * Return: timing register value to be programmed into hardware 393 + */ 394 + static u16 rzv2h_dphy_find_timings_val(unsigned long freq, u8 index) 395 + { 396 + const struct rzv2h_mipi_dsi_timings *timings; 397 + u16 i; 398 + 399 + /* Get the timing table structure for the requested parameter */ 400 + timings = &rzv2h_dsi_timings_tables[index]; 401 + 402 + /* 403 + * Search through frequency table to find appropriate range 404 + * timings->hsfreq[i] contains frequency values from HW manual 405 + * Convert to Hz by multiplying by 10 * MEGA. 406 + */ 407 + for (i = 0; i < timings->len; i++) { 408 + unsigned long hsfreq = timings->hsfreq[i] * 10 * MEGA; 409 + 410 + if (freq <= hsfreq) 411 + break; 412 + } 413 + 414 + /* If frequency exceeds table range, use the last entry */ 415 + if (i == timings->len) 416 + i--; 417 + 418 + /* 419 + * Calculate final register value: 420 + * - timings->base_value: base value for this timing parameter 421 + * - i: index into frequency table (0-based) 422 + * Combined they give the exact register value to program 423 + */ 424 + return timings->base_value + i; 219 425 }; 220 426 221 427 static void rzg2l_mipi_dsi_phy_write(struct rzg2l_mipi_dsi *dsi, u32 reg, u32 data) ··· 573 317 return 0; 574 318 } 575 319 320 + static unsigned int rzv2h_dphy_mode_clk_check(struct rzg2l_mipi_dsi *dsi, 321 + unsigned long mode_freq) 322 + { 323 + u64 hsfreq_millihz, mode_freq_hz, mode_freq_millihz; 324 + struct rzv2h_pll_div_pars cpg_dsi_parameters; 325 + struct rzv2h_pll_pars dsi_parameters; 326 + bool parameters_found; 327 + unsigned int bpp; 328 + 329 + bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); 330 + mode_freq_hz = mul_u32_u32(mode_freq, KILO); 331 + mode_freq_millihz = mode_freq_hz * MILLI; 332 + parameters_found = 333 + rzv2h_get_pll_divs_pars(dsi->info->cpg_plldsi.limits[0], 334 + &cpg_dsi_parameters, 335 + dsi->info->cpg_plldsi.table, 336 + dsi->info->cpg_plldsi.table_size, 337 + mode_freq_millihz); 338 + if (!parameters_found) 339 + return MODE_CLOCK_RANGE; 340 + 341 + hsfreq_millihz = DIV_ROUND_CLOSEST_ULL(cpg_dsi_parameters.div.freq_millihz * bpp, 342 + dsi->lanes); 343 + parameters_found = rzv2h_get_pll_pars(&rzv2h_plldsi_div_limits, 344 + &dsi_parameters, hsfreq_millihz); 345 + if (!parameters_found) 346 + return MODE_CLOCK_RANGE; 347 + 348 + if (abs(dsi_parameters.error_millihz) >= 500) 349 + return MODE_CLOCK_RANGE; 350 + 351 + memcpy(&dsi->mode_calc.dsi_parameters, &dsi_parameters, sizeof(dsi_parameters)); 352 + dsi->mode_calc.mode_freq_khz = mode_freq; 353 + 354 + return MODE_OK; 355 + } 356 + 357 + static int rzv2h_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_freq, 358 + u64 *hsfreq_millihz) 359 + { 360 + struct rzv2h_pll_pars *dsi_parameters = &dsi->mode_calc.dsi_parameters; 361 + unsigned long status; 362 + 363 + if (dsi->mode_calc.mode_freq_khz != mode_freq) { 364 + status = rzv2h_dphy_mode_clk_check(dsi, mode_freq); 365 + if (status != MODE_OK) { 366 + dev_err(dsi->dev, "No PLL parameters found for mode clk %lu\n", 367 + mode_freq); 368 + return -EINVAL; 369 + } 370 + } 371 + 372 + *hsfreq_millihz = dsi_parameters->freq_millihz; 373 + 374 + return 0; 375 + } 376 + 377 + static int rzv2h_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi, 378 + u64 hsfreq_millihz) 379 + { 380 + struct rzv2h_pll_pars *dsi_parameters = &dsi->mode_calc.dsi_parameters; 381 + unsigned long lpclk_rate = clk_get_rate(dsi->lpclk); 382 + u32 phytclksetr, phythssetr, phytlpxsetr, phycr; 383 + struct rzg2l_mipi_dsi_timings dphy_timings; 384 + u16 ulpsexit; 385 + u64 hsfreq; 386 + 387 + hsfreq = DIV_ROUND_CLOSEST_ULL(hsfreq_millihz, MILLI); 388 + 389 + if (dsi_parameters->freq_millihz != hsfreq_millihz && 390 + !rzv2h_get_pll_pars(&rzv2h_plldsi_div_limits, dsi_parameters, 391 + hsfreq_millihz)) { 392 + dev_err(dsi->dev, "No PLL parameters found for HSFREQ %lluHz\n", hsfreq); 393 + return -EINVAL; 394 + } 395 + 396 + dphy_timings.tclk_trail = 397 + rzv2h_dphy_find_timings_val(hsfreq, TCLKTRAILCTL); 398 + dphy_timings.tclk_post = 399 + rzv2h_dphy_find_timings_val(hsfreq, TCLKPOSTCTL); 400 + dphy_timings.tclk_zero = 401 + rzv2h_dphy_find_timings_val(hsfreq, TCLKZEROCTL); 402 + dphy_timings.tclk_prepare = 403 + rzv2h_dphy_find_timings_val(hsfreq, TCLKPRPRCTL); 404 + dphy_timings.ths_exit = 405 + rzv2h_dphy_find_timings_val(hsfreq, THSEXITCTL); 406 + dphy_timings.ths_trail = 407 + rzv2h_dphy_find_timings_val(hsfreq, THSTRAILCTL); 408 + dphy_timings.ths_zero = 409 + rzv2h_dphy_find_timings_val(hsfreq, THSZEROCTL); 410 + dphy_timings.ths_prepare = 411 + rzv2h_dphy_find_timings_val(hsfreq, THSPRPRCTL); 412 + dphy_timings.tlpx = 413 + rzv2h_dphy_find_timings_val(hsfreq, TLPXCTL); 414 + ulpsexit = rzv2h_dphy_find_ulpsexit(lpclk_rate); 415 + 416 + phytclksetr = FIELD_PREP(PHYTCLKSETR_TCLKTRAILCTL, dphy_timings.tclk_trail) | 417 + FIELD_PREP(PHYTCLKSETR_TCLKPOSTCTL, dphy_timings.tclk_post) | 418 + FIELD_PREP(PHYTCLKSETR_TCLKZEROCTL, dphy_timings.tclk_zero) | 419 + FIELD_PREP(PHYTCLKSETR_TCLKPRPRCTL, dphy_timings.tclk_prepare); 420 + phythssetr = FIELD_PREP(PHYTHSSETR_THSEXITCTL, dphy_timings.ths_exit) | 421 + FIELD_PREP(PHYTHSSETR_THSTRAILCTL, dphy_timings.ths_trail) | 422 + FIELD_PREP(PHYTHSSETR_THSZEROCTL, dphy_timings.ths_zero) | 423 + FIELD_PREP(PHYTHSSETR_THSPRPRCTL, dphy_timings.ths_prepare); 424 + phytlpxsetr = rzg2l_mipi_dsi_phy_read(dsi, PHYTLPXSETR) & ~PHYTLPXSETR_TLPXCTL; 425 + phytlpxsetr |= FIELD_PREP(PHYTLPXSETR_TLPXCTL, dphy_timings.tlpx); 426 + phycr = rzg2l_mipi_dsi_phy_read(dsi, PHYCR) & ~GENMASK(9, 0); 427 + phycr |= FIELD_PREP(PHYCR_ULPSEXIT, ulpsexit); 428 + 429 + /* Setting all D-PHY Timings Registers */ 430 + rzg2l_mipi_dsi_phy_write(dsi, PHYTCLKSETR, phytclksetr); 431 + rzg2l_mipi_dsi_phy_write(dsi, PHYTHSSETR, phythssetr); 432 + rzg2l_mipi_dsi_phy_write(dsi, PHYTLPXSETR, phytlpxsetr); 433 + rzg2l_mipi_dsi_phy_write(dsi, PHYCR, phycr); 434 + 435 + rzg2l_mipi_dsi_phy_write(dsi, PLLCLKSET0R, 436 + FIELD_PREP(PLLCLKSET0R_PLL_S, dsi_parameters->s) | 437 + FIELD_PREP(PLLCLKSET0R_PLL_P, dsi_parameters->p) | 438 + FIELD_PREP(PLLCLKSET0R_PLL_M, dsi_parameters->m)); 439 + rzg2l_mipi_dsi_phy_write(dsi, PLLCLKSET1R, 440 + FIELD_PREP(PLLCLKSET1R_PLL_K, dsi_parameters->k)); 441 + 442 + /* 443 + * From RZ/V2H HW manual (Rev.1.20) section 9.5.3 Operation, 444 + * (C) After write to D-PHY registers we need to wait for more than 1 x tp 445 + * 446 + * tp = 1 / (PLLREFCLK / PLLCLKSET0R.PLL_P) 447 + * PLLREFCLK = 24MHz 448 + * PLLCLKSET0R.PLL_P = {1, 2, 3, 4} 449 + * 450 + * To handle all the cases lets use PLLCLKSET0R.PLL_P = 4 451 + * tp = 1 / (24MHz / 4) = 1 / 6MHz = 166.67ns 452 + */ 453 + ndelay(200); 454 + 455 + rzg2l_mipi_dsi_phy_write(dsi, PLLENR, PLLENR_PLLEN); 456 + /* 457 + * From RZ/V2H HW manual (Rev.1.20) section 9.5.3 Operation, 458 + * (D) After write to PLLENR.PLLEN we need to wait for more than 3000 x tp 459 + * 460 + * 3000 x tp = 3000 x 0.16667 ns = 500.01 microseconds 461 + */ 462 + usleep_range(510, 520); 463 + 464 + return 0; 465 + } 466 + 467 + static void rzv2h_mipi_dsi_dphy_startup_late_init(struct rzg2l_mipi_dsi *dsi) 468 + { 469 + /* 470 + * From RZ/V2H HW manual (Rev.1.20) section 9.5.3 Operation, 471 + * (E) After write to TXSETR we need to wait for more than 200 microseconds 472 + * and then write to PHYRSTR 473 + */ 474 + usleep_range(210, 220); 475 + rzg2l_mipi_dsi_phy_write(dsi, PHYRSTR, PHYRSTR_PHYMRSTN); 476 + } 477 + 478 + static void rzv2h_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi) 479 + { 480 + rzg2l_mipi_dsi_phy_write(dsi, PLLENR, 0); 481 + } 482 + 576 483 static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, 577 484 const struct drm_display_mode *mode) 578 485 { ··· 847 428 break; 848 429 case 18: 849 430 vich1ppsetr = VICH1PPSETR_DT_RGB18; 431 + break; 432 + case 16: 433 + vich1ppsetr = VICH1PPSETR_DT_RGB16; 850 434 break; 851 435 } 852 436 ··· 1401 979 if (IS_ERR(dsi->vclk)) 1402 980 return PTR_ERR(dsi->vclk); 1403 981 982 + dsi->lpclk = devm_clk_get(dsi->dev, "lpclk"); 983 + if (IS_ERR(dsi->lpclk)) 984 + return PTR_ERR(dsi->lpclk); 985 + 1404 986 dsi->rstc = devm_reset_control_get_optional_exclusive(dsi->dev, "rst"); 1405 987 if (IS_ERR(dsi->rstc)) 1406 988 return dev_err_probe(dsi->dev, PTR_ERR(dsi->rstc), ··· 1477 1051 pm_runtime_disable(&pdev->dev); 1478 1052 } 1479 1053 1054 + RZV2H_CPG_PLL_DSI_LIMITS(rzv2h_cpg_pll_dsi_limits); 1055 + 1056 + static const struct rzv2h_pll_limits *rzv2h_plldsi_limits[] = { 1057 + &rzv2h_cpg_pll_dsi_limits, 1058 + }; 1059 + 1060 + static const u8 rzv2h_cpg_div_table[] = { 1061 + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 1062 + }; 1063 + 1064 + static const struct rzg2l_mipi_dsi_hw_info rzv2h_mipi_dsi_info = { 1065 + .dphy_init = rzv2h_mipi_dsi_dphy_init, 1066 + .dphy_startup_late_init = rzv2h_mipi_dsi_dphy_startup_late_init, 1067 + .dphy_exit = rzv2h_mipi_dsi_dphy_exit, 1068 + .dphy_mode_clk_check = rzv2h_dphy_mode_clk_check, 1069 + .dphy_conf_clks = rzv2h_dphy_conf_clks, 1070 + .cpg_plldsi.limits = rzv2h_plldsi_limits, 1071 + .cpg_plldsi.table = rzv2h_cpg_div_table, 1072 + .cpg_plldsi.table_size = ARRAY_SIZE(rzv2h_cpg_div_table), 1073 + .phy_reg_offset = 0x10000, 1074 + .link_reg_offset = 0, 1075 + .min_dclk = 5440, 1076 + .max_dclk = 187500, 1077 + .features = RZ_MIPI_DSI_FEATURE_16BPP, 1078 + }; 1079 + 1480 1080 static const struct rzg2l_mipi_dsi_hw_info rzg2l_mipi_dsi_info = { 1481 1081 .dphy_init = rzg2l_mipi_dsi_dphy_init, 1482 1082 .dphy_exit = rzg2l_mipi_dsi_dphy_exit, ··· 1513 1061 }; 1514 1062 1515 1063 static const struct of_device_id rzg2l_mipi_dsi_of_table[] = { 1064 + { .compatible = "renesas,r9a09g057-mipi-dsi", .data = &rzv2h_mipi_dsi_info, }, 1516 1065 { .compatible = "renesas,rzg2l-mipi-dsi", .data = &rzg2l_mipi_dsi_info, }, 1517 1066 { /* sentinel */ } 1518 1067 };
+34
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h
··· 40 40 #define DSIDPHYTIM3_THS_TRAIL(x) ((x) << 8) 41 41 #define DSIDPHYTIM3_THS_ZERO(x) ((x) << 0) 42 42 43 + /* RZ/V2H DPHY Registers */ 44 + #define PLLENR 0x000 45 + #define PLLENR_PLLEN BIT(0) 46 + 47 + #define PHYRSTR 0x004 48 + #define PHYRSTR_PHYMRSTN BIT(0) 49 + 50 + #define PLLCLKSET0R 0x010 51 + #define PLLCLKSET0R_PLL_S GENMASK(2, 0) 52 + #define PLLCLKSET0R_PLL_P GENMASK(13, 8) 53 + #define PLLCLKSET0R_PLL_M GENMASK(25, 16) 54 + 55 + #define PLLCLKSET1R 0x014 56 + #define PLLCLKSET1R_PLL_K GENMASK(15, 0) 57 + 58 + #define PHYTCLKSETR 0x020 59 + #define PHYTCLKSETR_TCLKTRAILCTL GENMASK(7, 0) 60 + #define PHYTCLKSETR_TCLKPOSTCTL GENMASK(15, 8) 61 + #define PHYTCLKSETR_TCLKZEROCTL GENMASK(23, 16) 62 + #define PHYTCLKSETR_TCLKPRPRCTL GENMASK(31, 24) 63 + 64 + #define PHYTHSSETR 0x024 65 + #define PHYTHSSETR_THSEXITCTL GENMASK(7, 0) 66 + #define PHYTHSSETR_THSTRAILCTL GENMASK(15, 8) 67 + #define PHYTHSSETR_THSZEROCTL GENMASK(23, 16) 68 + #define PHYTHSSETR_THSPRPRCTL GENMASK(31, 24) 69 + 70 + #define PHYTLPXSETR 0x028 71 + #define PHYTLPXSETR_TLPXCTL GENMASK(7, 0) 72 + 73 + #define PHYCR 0x030 74 + #define PHYCR_ULPSEXIT GENMASK(9, 0) 75 + 43 76 /* --------------------------------------------------------*/ 44 77 45 78 /* Link Status Register */ ··· 163 130 164 131 /* Video-Input Channel 1 Pixel Packet Set Register */ 165 132 #define VICH1PPSETR 0x420 133 + #define VICH1PPSETR_DT_RGB16 (0x0e << 16) 166 134 #define VICH1PPSETR_DT_RGB18 (0x1e << 16) 167 135 #define VICH1PPSETR_DT_RGB18_LS (0x2e << 16) 168 136 #define VICH1PPSETR_DT_RGB24 (0x3e << 16)
+16
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
··· 467 467 .use_drm_infoframe = true, 468 468 }; 469 469 470 + static struct rockchip_hdmi_chip_data rk3368_chip_data = { 471 + .lcdsel_grf_reg = -1, 472 + }; 473 + 474 + static const struct dw_hdmi_plat_data rk3368_hdmi_drv_data = { 475 + .mode_valid = dw_hdmi_rockchip_mode_valid, 476 + .mpll_cfg = rockchip_mpll_cfg, 477 + .cur_ctr = rockchip_cur_ctr, 478 + .phy_config = rockchip_phy_config, 479 + .phy_data = &rk3368_chip_data, 480 + .use_drm_infoframe = true, 481 + }; 482 + 470 483 static struct rockchip_hdmi_chip_data rk3399_chip_data = { 471 484 .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, 472 485 .lcdsel_big = FIELD_PREP_WM16_CONST(RK3399_HDMI_LCDC_SEL, 0), ··· 519 506 }, 520 507 { .compatible = "rockchip,rk3328-dw-hdmi", 521 508 .data = &rk3328_hdmi_drv_data 509 + }, 510 + { .compatible = "rockchip,rk3368-dw-hdmi", 511 + .data = &rk3368_hdmi_drv_data 522 512 }, 523 513 { .compatible = "rockchip,rk3399-dw-hdmi", 524 514 .data = &rk3399_hdmi_drv_data
+37 -3
drivers/gpu/drm/scheduler/sched_main.c
··· 344 344 */ 345 345 static void drm_sched_run_job_queue(struct drm_gpu_scheduler *sched) 346 346 { 347 - if (!READ_ONCE(sched->pause_submit)) 347 + if (!drm_sched_is_stopped(sched)) 348 348 queue_work(sched->submit_wq, &sched->work_run_job); 349 349 } 350 350 ··· 354 354 */ 355 355 static void drm_sched_run_free_queue(struct drm_gpu_scheduler *sched) 356 356 { 357 - if (!READ_ONCE(sched->pause_submit)) 357 + if (!drm_sched_is_stopped(sched)) 358 358 queue_work(sched->submit_wq, &sched->work_free_job); 359 359 } 360 360 ··· 729 729 * 730 730 * Drivers can still save and restore their state for recovery operations, but 731 731 * we shouldn't make this a general scheduler feature around the dma_fence 732 - * interface. 732 + * interface. The suggested driver-side replacement is to use 733 + * drm_sched_for_each_pending_job() after stopping the scheduler and implement 734 + * their own recovery operations. 733 735 */ 734 736 void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched) 735 737 { ··· 1569 1567 queue_work(sched->submit_wq, &sched->work_free_job); 1570 1568 } 1571 1569 EXPORT_SYMBOL(drm_sched_wqueue_start); 1570 + 1571 + /** 1572 + * drm_sched_is_stopped() - Checks whether drm_sched is stopped 1573 + * @sched: DRM scheduler 1574 + * 1575 + * Return: true if sched is stopped, false otherwise 1576 + */ 1577 + bool drm_sched_is_stopped(struct drm_gpu_scheduler *sched) 1578 + { 1579 + return READ_ONCE(sched->pause_submit); 1580 + } 1581 + EXPORT_SYMBOL(drm_sched_is_stopped); 1582 + 1583 + /** 1584 + * drm_sched_job_is_signaled() - DRM scheduler job is signaled 1585 + * @job: DRM scheduler job 1586 + * 1587 + * Determine if DRM scheduler job is signaled. DRM scheduler should be stopped 1588 + * to obtain a stable snapshot of state. Both parent fence (hardware fence) and 1589 + * finished fence (software fence) are checked to determine signaling state. 1590 + * 1591 + * Return: true if job is signaled, false otherwise 1592 + */ 1593 + bool drm_sched_job_is_signaled(struct drm_sched_job *job) 1594 + { 1595 + struct drm_sched_fence *s_fence = job->s_fence; 1596 + 1597 + WARN_ON(!drm_sched_is_stopped(job->sched)); 1598 + return (s_fence->parent && dma_fence_is_signaled(s_fence->parent)) || 1599 + dma_fence_is_signaled(&s_fence->finished); 1600 + } 1601 + EXPORT_SYMBOL(drm_sched_job_is_signaled);
+43 -5
drivers/gpu/drm/sitronix/Kconfig
··· 1 - config DRM_ST7571_I2C 2 - tristate "DRM support for Sitronix ST7571 display panels (I2C)" 3 - depends on DRM && I2C && MMU 1 + config DRM_ST7571 2 + tristate "DRM support for Sitronix ST7567/ST7571 display panels" 3 + depends on DRM && MMU 4 4 select DRM_CLIENT_SELECTION 5 5 select DRM_GEM_SHMEM_HELPER 6 6 select DRM_KMS_HELPER 7 - select REGMAP_I2C 8 7 select VIDEOMODE_HELPERS 9 8 help 10 - DRM driver for Sitronix ST7571 panels controlled over I2C. 9 + Sitronix ST7571 is a driver and controller for 4-level gray 10 + scale and monochrome dot matrix LCD panels. 11 + 12 + DRM driver for Sitronix ST7567/ST7571 panels. 13 + This is only the core driver, a driver for the appropriate bus 14 + transport in your chip also must be selected. 15 + 16 + if M is selected the module will be called st7571. 17 + 18 + config DRM_ST7571_I2C 19 + tristate "DRM support for Sitronix ST7567/ST7571 display panels (I2C)" 20 + depends on DRM_ST7571 && I2C 21 + select REGMAP 22 + help 23 + Sitronix ST7571 is a driver and controller for 4-level gray 24 + scale and monochrome dot matrix LCD panels. 25 + 26 + DRM driver for Sitronix ST7565/ST7571 panels connected via I2C bus. 11 27 12 28 if M is selected the module will be called st7571-i2c. 29 + 30 + config DRM_ST7571_SPI 31 + tristate "DRM support for Sitronix ST7567/ST7571 display panels (SPI)" 32 + depends on DRM_ST7571 && SPI 33 + select REGMAP_SPI 34 + help 35 + Sitronix ST7571 is a driver and controller for 4-level gray 36 + scale and monochrome dot matrix LCD panels. 37 + 38 + DRM driver for Sitronix ST7565/ST7571 panels connected via SPI bus. 39 + 40 + if M is selected the module will be called st7571-spi. 13 41 14 42 config DRM_ST7586 15 43 tristate "DRM support for Sitronix ST7586 display panels" ··· 68 40 69 41 If M is selected the module will be called st7735r. 70 42 43 + config DRM_ST7920 44 + tristate "DRM support for Sitronix ST7920 LCD displays" 45 + depends on DRM && SPI && MMU 46 + select DRM_GEM_SHMEM_HELPER 47 + select DRM_KMS_HELPER 48 + select REGMAP_SPI 49 + help 50 + DRM driver for the ST7920 Sitronix LCD controllers. 51 + 52 + If M is selected the module will be called st7920.
+3
drivers/gpu/drm/sitronix/Makefile
··· 1 + obj-$(CONFIG_DRM_ST7571) += st7571.o 1 2 obj-$(CONFIG_DRM_ST7571_I2C) += st7571-i2c.o 3 + obj-$(CONFIG_DRM_ST7571_SPI) += st7571-spi.o 2 4 obj-$(CONFIG_DRM_ST7586) += st7586.o 3 5 obj-$(CONFIG_DRM_ST7735R) += st7735r.o 6 + obj-$(CONFIG_DRM_ST7920) += st7920.o
+39 -964
drivers/gpu/drm/sitronix/st7571-i2c.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 - * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller 3 + * Driver for Sitronix ST7571 connected via I2C bus. 4 4 * 5 5 * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com> 6 6 */ 7 7 8 - #include <linux/bitfield.h> 9 - #include <linux/delay.h> 10 - #include <linux/gpio/consumer.h> 11 8 #include <linux/i2c.h> 12 9 #include <linux/module.h> 13 10 #include <linux/regmap.h> 14 11 15 - #include <drm/clients/drm_client_setup.h> 16 - #include <drm/drm_atomic.h> 17 - #include <drm/drm_atomic_helper.h> 18 - #include <drm/drm_connector.h> 19 - #include <drm/drm_crtc_helper.h> 20 - #include <drm/drm_damage_helper.h> 21 - #include <drm/drm_drv.h> 22 - #include <drm/drm_encoder.h> 23 - #include <drm/drm_fb_helper.h> 24 - #include <drm/drm_fbdev_shmem.h> 25 - #include <drm/drm_fourcc.h> 26 - #include <drm/drm_framebuffer.h> 27 - #include <drm/drm_gem_atomic_helper.h> 28 - #include <drm/drm_gem_framebuffer_helper.h> 29 - #include <drm/drm_gem_shmem_helper.h> 30 - #include <drm/drm_modeset_helper_vtables.h> 31 - #include <drm/drm_module.h> 32 - #include <drm/drm_plane.h> 33 - #include <drm/drm_probe_helper.h> 12 + #include "st7571.h" 34 13 35 - #include <video/display_timing.h> 36 - #include <video/of_display_timing.h> 37 - 38 - #define ST7571_COMMAND_MODE (0x00) 39 - #define ST7571_DATA_MODE (0x40) 40 - 41 - /* Normal mode command set */ 42 - #define ST7571_DISPLAY_OFF (0xae) 43 - #define ST7571_DISPLAY_ON (0xaf) 44 - #define ST7571_OSC_ON (0xab) 45 - #define ST7571_SET_COLUMN_LSB(c) (0x00 | FIELD_PREP(GENMASK(3, 0), (c))) 46 - #define ST7571_SET_COLUMN_MSB(c) (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4)) 47 - #define ST7571_SET_COM0_LSB(x) (FIELD_PREP(GENMASK(6, 0), (x))) 48 - #define ST7571_SET_COM0_MSB (0x44) 49 - #define ST7571_SET_COM_SCAN_DIR(d) (0xc0 | FIELD_PREP(GENMASK(3, 3), (d))) 50 - #define ST7571_SET_CONTRAST_LSB(c) (FIELD_PREP(GENMASK(5, 0), (c))) 51 - #define ST7571_SET_CONTRAST_MSB (0x81) 52 - #define ST7571_SET_DISPLAY_DUTY_LSB(d) (FIELD_PREP(GENMASK(7, 0), (d))) 53 - #define ST7571_SET_DISPLAY_DUTY_MSB (0x48) 54 - #define ST7571_SET_ENTIRE_DISPLAY_ON(p) (0xa4 | FIELD_PREP(GENMASK(0, 0), (p))) 55 - #define ST7571_SET_LCD_BIAS(b) (0x50 | FIELD_PREP(GENMASK(2, 0), (b))) 56 - #define ST7571_SET_MODE_LSB(m) (FIELD_PREP(GENMASK(7, 2), (m))) 57 - #define ST7571_SET_MODE_MSB (0x38) 58 - #define ST7571_SET_PAGE(p) (0xb0 | FIELD_PREP(GENMASK(3, 0), (p))) 59 - #define ST7571_SET_POWER(p) (0x28 | FIELD_PREP(GENMASK(2, 0), (p))) 60 - #define ST7571_SET_REGULATOR_REG(r) (0x20 | FIELD_PREP(GENMASK(2, 0), (r))) 61 - #define ST7571_SET_REVERSE(r) (0xa6 | FIELD_PREP(GENMASK(0, 0), (r))) 62 - #define ST7571_SET_SEG_SCAN_DIR(d) (0xa0 | FIELD_PREP(GENMASK(0, 0), (d))) 63 - #define ST7571_SET_START_LINE_LSB(l) (FIELD_PREP(GENMASK(6, 0), (l))) 64 - #define ST7571_SET_START_LINE_MSB (0x40) 65 - 66 - /* Extension command set 3 */ 67 - #define ST7571_COMMAND_SET_3 (0x7b) 68 - #define ST7571_SET_COLOR_MODE(c) (0x10 | FIELD_PREP(GENMASK(0, 0), (c))) 69 - #define ST7571_COMMAND_SET_NORMAL (0x00) 70 - 71 - /* ST7567 commands */ 72 - #define ST7567_SET_LCD_BIAS(m) (0xa2 | FIELD_PREP(GENMASK(0, 0), (m))) 73 - 74 - #define ST7571_PAGE_HEIGHT 8 75 - 76 - #define DRIVER_NAME "st7571" 77 - #define DRIVER_DESC "ST7571 DRM driver" 78 - #define DRIVER_MAJOR 1 79 - #define DRIVER_MINOR 0 80 - 81 - enum st7571_color_mode { 82 - ST7571_COLOR_MODE_GRAY = 0, 83 - ST7571_COLOR_MODE_BLACKWHITE = 1, 84 - }; 85 - 86 - struct st7571_device; 87 - 88 - struct st7571_panel_constraints { 89 - u32 min_nlines; 90 - u32 max_nlines; 91 - u32 min_ncols; 92 - u32 max_ncols; 93 - bool support_grayscale; 94 - }; 95 - 96 - struct st7571_panel_data { 97 - int (*init)(struct st7571_device *st7571); 98 - int (*parse_dt)(struct st7571_device *st7571); 99 - struct st7571_panel_constraints constraints; 100 - }; 101 - 102 - struct st7571_panel_format { 103 - void (*prepare_buffer)(struct st7571_device *st7571, 104 - const struct iosys_map *vmap, 105 - struct drm_framebuffer *fb, 106 - struct drm_rect *rect, 107 - struct drm_format_conv_state *fmtcnv_state); 108 - int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect); 109 - enum st7571_color_mode mode; 110 - const u8 nformats; 111 - const u32 formats[]; 112 - }; 113 - 114 - struct st7571_device { 115 - struct drm_device dev; 116 - 117 - struct drm_plane primary_plane; 118 - struct drm_crtc crtc; 119 - struct drm_encoder encoder; 120 - struct drm_connector connector; 121 - 122 - struct drm_display_mode mode; 123 - 124 - const struct st7571_panel_format *pformat; 125 - const struct st7571_panel_data *pdata; 14 + struct st7571_i2c_transport { 126 15 struct i2c_client *client; 127 - struct gpio_desc *reset; 128 - struct regmap *regmap; 129 16 130 17 /* 131 18 * Depending on the hardware design, the acknowledge signal may be hard to ··· 36 149 * 37 150 */ 38 151 bool ignore_nak; 39 - 40 - bool grayscale; 41 - bool inverted; 42 - u32 height_mm; 43 - u32 width_mm; 44 - u32 startline; 45 - u32 nlines; 46 - u32 ncols; 47 - u32 bpp; 48 - 49 - /* Intermediate buffer in LCD friendly format */ 50 - u8 *hwbuf; 51 - 52 - /* Row of (transformed) pixels ready to be written to the display */ 53 - u8 *row; 54 152 }; 55 153 56 - static inline struct st7571_device *drm_to_st7571(struct drm_device *dev) 154 + static int st7571_i2c_regmap_write(void *context, const void *data, size_t count) 57 155 { 58 - return container_of(dev, struct st7571_device, dev); 59 - } 60 - 61 - static int st7571_regmap_write(void *context, const void *data, size_t count) 62 - { 63 - struct i2c_client *client = context; 64 - struct st7571_device *st7571 = i2c_get_clientdata(client); 156 + struct st7571_i2c_transport *t = context; 65 157 int ret; 66 158 67 159 struct i2c_msg msg = { 68 - .addr = st7571->client->addr, 69 - .flags = st7571->ignore_nak ? I2C_M_IGNORE_NAK : 0, 160 + .addr = t->client->addr, 161 + .flags = t->ignore_nak ? I2C_M_IGNORE_NAK : 0, 70 162 .len = count, 71 163 .buf = (u8 *)data 72 164 }; 73 165 74 - ret = i2c_transfer(st7571->client->adapter, &msg, 1); 166 + ret = i2c_transfer(t->client->adapter, &msg, 1); 75 167 76 168 /* 77 169 * Unfortunately, there is no way to check if the transfer failed because of ··· 58 192 * 59 193 * However, if the transfer fails and ignore_nak is set, we know it is an error. 60 194 */ 61 - if (ret < 0 && st7571->ignore_nak) 195 + if (ret < 0 && t->ignore_nak) 62 196 return ret; 63 197 64 198 return 0; 65 199 } 66 200 67 201 /* The st7571 driver does not read registers but regmap expects a .read */ 68 - static int st7571_regmap_read(void *context, const void *reg_buf, 69 - size_t reg_size, void *val_buf, size_t val_size) 202 + static int st7571_i2c_regmap_read(void *context, const void *reg_buf, 203 + size_t reg_size, void *val_buf, size_t val_size) 70 204 { 71 205 return -EOPNOTSUPP; 72 206 } 73 207 74 - static int st7571_send_command_list(struct st7571_device *st7571, 75 - const u8 *cmd_list, size_t len) 76 - { 77 - int ret; 78 - 79 - for (int i = 0; i < len; i++) { 80 - ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]); 81 - if (ret < 0) 82 - return ret; 83 - } 84 - 85 - return ret; 86 - } 87 - 88 - static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp) 89 - { 90 - int xrest = x % 8; 91 - u8 result = 0; 92 - u8 row_len = 16 * bpp; 93 - 94 - /* 95 - * Transforms an (x, y) pixel coordinate into a vertical 8-bit 96 - * column from the framebuffer. It calculates the corresponding byte in the 97 - * framebuffer, extracts the bit at the given x position across 8 consecutive 98 - * rows, and packs those bits into a single byte. 99 - * 100 - * Return an 8-bit value representing a vertical column of pixels. 101 - */ 102 - x = x / 8; 103 - y = (y / 8) * 8; 104 - 105 - for (int i = 0; i < 8; i++) { 106 - int row_idx = y + i; 107 - u8 byte = p[row_idx * row_len + x]; 108 - u8 bit = (byte >> xrest) & 1; 109 - 110 - result |= (bit << i); 111 - } 112 - 113 - return result; 114 - } 115 - 116 - static int st7571_set_position(struct st7571_device *st7571, int x, int y) 117 - { 118 - u8 cmd_list[] = { 119 - ST7571_SET_COLUMN_LSB(x), 120 - ST7571_SET_COLUMN_MSB(x), 121 - ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT), 122 - }; 123 - 124 - return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list)); 125 - } 126 - 127 - static int st7571_fb_clear_screen(struct st7571_device *st7571) 128 - { 129 - u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp; 130 - char pixelvalue = 0x00; 131 - 132 - st7571_set_position(st7571, 0, 0); 133 - for (int i = 0; i < npixels; i++) 134 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1); 135 - 136 - return 0; 137 - } 138 - 139 - static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571, 140 - const struct iosys_map *vmap, 141 - struct drm_framebuffer *fb, 142 - struct drm_rect *rect, 143 - struct drm_format_conv_state *fmtcnv_state) 144 - { 145 - unsigned int dst_pitch; 146 - struct iosys_map dst; 147 - u32 size; 148 - 149 - switch (fb->format->format) { 150 - case DRM_FORMAT_XRGB8888: 151 - dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); 152 - iosys_map_set_vaddr(&dst, st7571->hwbuf); 153 - 154 - drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 155 - break; 156 - 157 - case DRM_FORMAT_R1: 158 - size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 159 - memcpy(st7571->hwbuf, vmap->vaddr, size); 160 - break; 161 - } 162 - } 163 - 164 - static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571, 165 - const struct iosys_map *vmap, 166 - struct drm_framebuffer *fb, 167 - struct drm_rect *rect, 168 - struct drm_format_conv_state *fmtcnv_state) 169 - { 170 - u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 171 - unsigned int dst_pitch; 172 - struct iosys_map dst; 173 - 174 - switch (fb->format->format) { 175 - case DRM_FORMAT_XRGB8888: 176 - dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4); 177 - iosys_map_set_vaddr(&dst, st7571->hwbuf); 178 - 179 - drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 180 - break; 181 - 182 - case DRM_FORMAT_R1: 183 - size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 184 - memcpy(st7571->hwbuf, vmap->vaddr, size); 185 - break; 186 - 187 - case DRM_FORMAT_R2: 188 - size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4; 189 - memcpy(st7571->hwbuf, vmap->vaddr, size); 190 - break; 191 - } 192 - } 193 - 194 - static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect) 195 - { 196 - struct st7571_device *st7571 = drm_to_st7571(fb->dev); 197 - char *row = st7571->row; 198 - 199 - /* Align y to display page boundaries */ 200 - rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 201 - rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 202 - 203 - for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 204 - for (int x = rect->x1; x < rect->x2; x++) 205 - row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1); 206 - 207 - st7571_set_position(st7571, rect->x1, y); 208 - 209 - /* TODO: Investige why we can't write multiple bytes at once */ 210 - for (int x = rect->x1; x < rect->x2; x++) 211 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 212 - } 213 - 214 - return 0; 215 - } 216 - 217 - static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect) 218 - { 219 - struct st7571_device *st7571 = drm_to_st7571(fb->dev); 220 - u32 format = fb->format->format; 221 - char *row = st7571->row; 222 - int x1; 223 - int x2; 224 - 225 - /* Align y to display page boundaries */ 226 - rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 227 - rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 228 - 229 - switch (format) { 230 - case DRM_FORMAT_R1: 231 - x1 = rect->x1 * 1; 232 - x2 = rect->x2 * 1; 233 - break; 234 - case DRM_FORMAT_R2: 235 - fallthrough; 236 - case DRM_FORMAT_XRGB8888: 237 - x1 = rect->x1 * 2; 238 - x2 = rect->x2 * 2; 239 - break; 240 - } 241 - 242 - for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 243 - for (int x = x1; x < x2; x++) 244 - row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2); 245 - 246 - st7571_set_position(st7571, rect->x1, y); 247 - 248 - /* TODO: Investige why we can't write multiple bytes at once */ 249 - for (int x = x1; x < x2; x++) { 250 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 251 - 252 - /* 253 - * As the display supports grayscale, all pixels must be written as two bits 254 - * even if the format is monochrome. 255 - * 256 - * The bit values maps to the following grayscale: 257 - * 0 0 = Black 258 - * 0 1 = Dark gray 259 - * 1 0 = Light gray 260 - * 1 1 = White 261 - * 262 - * For monochrome formats, write the same value twice to get 263 - * either a black or white pixel. 264 - */ 265 - if (format == DRM_FORMAT_R1) 266 - regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 267 - } 268 - } 269 - 270 - return 0; 271 - } 272 - 273 - static int st7571_connector_get_modes(struct drm_connector *conn) 274 - { 275 - struct st7571_device *st7571 = drm_to_st7571(conn->dev); 276 - 277 - return drm_connector_helper_get_modes_fixed(conn, &st7571->mode); 278 - } 279 - 280 - static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = { 281 - .get_modes = st7571_connector_get_modes, 208 + static const struct regmap_bus st7571_i2c_regmap_bus = { 209 + .read = st7571_i2c_regmap_read, 210 + .write = st7571_i2c_regmap_write, 282 211 }; 283 212 284 - static const struct st7571_panel_format st7571_monochrome = { 285 - .prepare_buffer = st7571_prepare_buffer_monochrome, 286 - .update_rect = st7571_fb_update_rect_monochrome, 287 - .mode = ST7571_COLOR_MODE_BLACKWHITE, 288 - .formats = { 289 - DRM_FORMAT_XRGB8888, 290 - DRM_FORMAT_R1, 291 - }, 292 - .nformats = 2, 293 - }; 294 - 295 - static const struct st7571_panel_format st7571_grayscale = { 296 - .prepare_buffer = st7571_prepare_buffer_grayscale, 297 - .update_rect = st7571_fb_update_rect_grayscale, 298 - .mode = ST7571_COLOR_MODE_GRAY, 299 - .formats = { 300 - DRM_FORMAT_XRGB8888, 301 - DRM_FORMAT_R1, 302 - DRM_FORMAT_R2, 303 - }, 304 - .nformats = 3, 305 - }; 306 - 307 - static const u64 st7571_primary_plane_fmtmods[] = { 308 - DRM_FORMAT_MOD_LINEAR, 309 - DRM_FORMAT_MOD_INVALID 310 - }; 311 - 312 - static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane, 313 - struct drm_atomic_state *state) 314 - { 315 - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 316 - struct drm_crtc *new_crtc = new_plane_state->crtc; 317 - struct drm_crtc_state *new_crtc_state = NULL; 318 - 319 - if (new_crtc) 320 - new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); 321 - 322 - return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 323 - DRM_PLANE_NO_SCALING, 324 - DRM_PLANE_NO_SCALING, 325 - false, false); 326 - } 327 - 328 - static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane, 329 - struct drm_atomic_state *state) 330 - { 331 - struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 332 - struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 333 - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 334 - struct drm_framebuffer *fb = plane_state->fb; 335 - struct drm_atomic_helper_damage_iter iter; 336 - struct drm_device *dev = plane->dev; 337 - struct drm_rect damage; 338 - struct st7571_device *st7571 = drm_to_st7571(plane->dev); 339 - int ret, idx; 340 - 341 - if (!fb) 342 - return; /* no framebuffer; plane is disabled */ 343 - 344 - ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 345 - if (ret) 346 - return; 347 - 348 - if (!drm_dev_enter(dev, &idx)) 349 - goto out_drm_gem_fb_end_cpu_access; 350 - 351 - drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 352 - drm_atomic_for_each_plane_damage(&iter, &damage) { 353 - st7571->pformat->prepare_buffer(st7571, 354 - &shadow_plane_state->data[0], 355 - fb, &damage, 356 - &shadow_plane_state->fmtcnv_state); 357 - 358 - st7571->pformat->update_rect(fb, &damage); 359 - } 360 - 361 - drm_dev_exit(idx); 362 - 363 - out_drm_gem_fb_end_cpu_access: 364 - drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 365 - } 366 - 367 - static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane, 368 - struct drm_atomic_state *state) 369 - { 370 - struct drm_device *dev = plane->dev; 371 - struct st7571_device *st7571 = drm_to_st7571(plane->dev); 372 - int idx; 373 - 374 - if (!drm_dev_enter(dev, &idx)) 375 - return; 376 - 377 - st7571_fb_clear_screen(st7571); 378 - drm_dev_exit(idx); 379 - } 380 - 381 - static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = { 382 - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 383 - .atomic_check = st7571_primary_plane_helper_atomic_check, 384 - .atomic_update = st7571_primary_plane_helper_atomic_update, 385 - .atomic_disable = st7571_primary_plane_helper_atomic_disable, 386 - }; 387 - 388 - static const struct drm_plane_funcs st7571_primary_plane_funcs = { 389 - .update_plane = drm_atomic_helper_update_plane, 390 - .disable_plane = drm_atomic_helper_disable_plane, 391 - .destroy = drm_plane_cleanup, 392 - DRM_GEM_SHADOW_PLANE_FUNCS, 393 - }; 394 - 395 - /* 396 - * CRTC 397 - */ 398 - 399 - static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc, 400 - const struct drm_display_mode *mode) 401 - { 402 - struct st7571_device *st7571 = drm_to_st7571(crtc->dev); 403 - 404 - return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode); 405 - } 406 - 407 - static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = { 408 - .atomic_check = drm_crtc_helper_atomic_check, 409 - .mode_valid = st7571_crtc_mode_valid, 410 - }; 411 - 412 - static const struct drm_crtc_funcs st7571_crtc_funcs = { 413 - .reset = drm_atomic_helper_crtc_reset, 414 - .destroy = drm_crtc_cleanup, 415 - .set_config = drm_atomic_helper_set_config, 416 - .page_flip = drm_atomic_helper_page_flip, 417 - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 418 - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 419 - }; 420 - 421 - /* 422 - * Encoder 423 - */ 424 - 425 - static void st7571_encoder_atomic_enable(struct drm_encoder *encoder, 426 - struct drm_atomic_state *state) 427 - { 428 - struct drm_device *drm = encoder->dev; 429 - struct st7571_device *st7571 = drm_to_st7571(drm); 430 - u8 command = ST7571_DISPLAY_ON; 431 - int ret; 432 - 433 - ret = st7571->pdata->init(st7571); 434 - if (ret) 435 - return; 436 - 437 - st7571_send_command_list(st7571, &command, 1); 438 - } 439 - 440 - static void st7571_encoder_atomic_disable(struct drm_encoder *encoder, 441 - struct drm_atomic_state *state) 442 - { 443 - struct drm_device *drm = encoder->dev; 444 - struct st7571_device *st7571 = drm_to_st7571(drm); 445 - u8 command = ST7571_DISPLAY_OFF; 446 - 447 - st7571_send_command_list(st7571, &command, 1); 448 - } 449 - 450 - static const struct drm_encoder_funcs st7571_encoder_funcs = { 451 - .destroy = drm_encoder_cleanup, 452 - 453 - }; 454 - 455 - static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = { 456 - .atomic_enable = st7571_encoder_atomic_enable, 457 - .atomic_disable = st7571_encoder_atomic_disable, 458 - }; 459 - 460 - /* 461 - * Connector 462 - */ 463 - 464 - static const struct drm_connector_funcs st7571_connector_funcs = { 465 - .reset = drm_atomic_helper_connector_reset, 466 - .fill_modes = drm_helper_probe_single_connector_modes, 467 - .destroy = drm_connector_cleanup, 468 - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 469 - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 470 - }; 471 - 472 - static const struct drm_mode_config_funcs st7571_mode_config_funcs = { 473 - .fb_create = drm_gem_fb_create_with_dirty, 474 - .atomic_check = drm_atomic_helper_check, 475 - .atomic_commit = drm_atomic_helper_commit, 476 - }; 477 - 478 - static struct drm_display_mode st7571_mode(struct st7571_device *st7571) 479 - { 480 - struct drm_display_mode mode = { 481 - DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines, 482 - st7571->width_mm, st7571->height_mm), 483 - }; 484 - 485 - return mode; 486 - } 487 - 488 - static int st7571_mode_config_init(struct st7571_device *st7571) 489 - { 490 - struct drm_device *dev = &st7571->dev; 491 - const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 492 - int ret; 493 - 494 - ret = drmm_mode_config_init(dev); 495 - if (ret) 496 - return ret; 497 - 498 - dev->mode_config.min_width = constraints->min_ncols; 499 - dev->mode_config.min_height = constraints->min_nlines; 500 - dev->mode_config.max_width = constraints->max_ncols; 501 - dev->mode_config.max_height = constraints->max_nlines; 502 - dev->mode_config.preferred_depth = 24; 503 - dev->mode_config.funcs = &st7571_mode_config_funcs; 504 - 505 - return 0; 506 - } 507 - 508 - static int st7571_plane_init(struct st7571_device *st7571, 509 - const struct st7571_panel_format *pformat) 510 - { 511 - struct drm_plane *primary_plane = &st7571->primary_plane; 512 - struct drm_device *dev = &st7571->dev; 513 - int ret; 514 - 515 - ret = drm_universal_plane_init(dev, primary_plane, 0, 516 - &st7571_primary_plane_funcs, 517 - pformat->formats, 518 - pformat->nformats, 519 - st7571_primary_plane_fmtmods, 520 - DRM_PLANE_TYPE_PRIMARY, NULL); 521 - if (ret) 522 - return ret; 523 - 524 - drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs); 525 - drm_plane_enable_fb_damage_clips(primary_plane); 526 - 527 - return 0; 528 - } 529 - 530 - static int st7571_crtc_init(struct st7571_device *st7571) 531 - { 532 - struct drm_plane *primary_plane = &st7571->primary_plane; 533 - struct drm_crtc *crtc = &st7571->crtc; 534 - struct drm_device *dev = &st7571->dev; 535 - int ret; 536 - 537 - ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, 538 - &st7571_crtc_funcs, NULL); 539 - if (ret) 540 - return ret; 541 - 542 - drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs); 543 - 544 - return 0; 545 - } 546 - 547 - static int st7571_encoder_init(struct st7571_device *st7571) 548 - { 549 - struct drm_encoder *encoder = &st7571->encoder; 550 - struct drm_crtc *crtc = &st7571->crtc; 551 - struct drm_device *dev = &st7571->dev; 552 - int ret; 553 - 554 - ret = drm_encoder_init(dev, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); 555 - if (ret) 556 - return ret; 557 - 558 - drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs); 559 - 560 - encoder->possible_crtcs = drm_crtc_mask(crtc); 561 - 562 - return 0; 563 - } 564 - 565 - static int st7571_connector_init(struct st7571_device *st7571) 566 - { 567 - struct drm_connector *connector = &st7571->connector; 568 - struct drm_encoder *encoder = &st7571->encoder; 569 - struct drm_device *dev = &st7571->dev; 570 - int ret; 571 - 572 - ret = drm_connector_init(dev, connector, &st7571_connector_funcs, 573 - DRM_MODE_CONNECTOR_Unknown); 574 - if (ret) 575 - return ret; 576 - 577 - drm_connector_helper_add(connector, &st7571_connector_helper_funcs); 578 - 579 - return drm_connector_attach_encoder(connector, encoder); 580 - } 581 - 582 - DEFINE_DRM_GEM_FOPS(st7571_fops); 583 - 584 - static const struct drm_driver st7571_driver = { 585 - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 586 - 587 - .name = DRIVER_NAME, 588 - .desc = DRIVER_DESC, 589 - .major = DRIVER_MAJOR, 590 - .minor = DRIVER_MINOR, 591 - 592 - .fops = &st7571_fops, 593 - DRM_GEM_SHMEM_DRIVER_OPS, 594 - DRM_FBDEV_SHMEM_DRIVER_OPS, 595 - }; 596 - 597 - static const struct regmap_bus st7571_regmap_bus = { 598 - .read = st7571_regmap_read, 599 - .write = st7571_regmap_write, 600 - }; 601 - 602 - static const struct regmap_config st7571_regmap_config = { 213 + static const struct regmap_config st7571_i2c_regmap_config = { 603 214 .reg_bits = 8, 604 215 .val_bits = 8, 605 216 .use_single_write = true, 606 217 }; 607 218 608 - static int st7571_validate_parameters(struct st7571_device *st7571) 609 - { 610 - struct device *dev = st7571->dev.dev; 611 - const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 612 - 613 - if (st7571->width_mm == 0) { 614 - dev_err(dev, "Invalid panel width\n"); 615 - return -EINVAL; 616 - } 617 - 618 - if (st7571->height_mm == 0) { 619 - dev_err(dev, "Invalid panel height\n"); 620 - return -EINVAL; 621 - } 622 - 623 - if (st7571->nlines < constraints->min_nlines || 624 - st7571->nlines > constraints->max_nlines) { 625 - dev_err(dev, "Invalid timing configuration.\n"); 626 - return -EINVAL; 627 - } 628 - 629 - if (st7571->startline + st7571->nlines > constraints->max_nlines) { 630 - dev_err(dev, "Invalid timing configuration.\n"); 631 - return -EINVAL; 632 - } 633 - 634 - if (st7571->ncols < constraints->min_ncols || 635 - st7571->ncols > constraints->max_ncols) { 636 - dev_err(dev, "Invalid timing configuration.\n"); 637 - return -EINVAL; 638 - } 639 - 640 - if (st7571->grayscale && !constraints->support_grayscale) { 641 - dev_err(dev, "Grayscale not supported\n"); 642 - return -EINVAL; 643 - } 644 - 645 - return 0; 646 - } 647 - 648 - static int st7567_parse_dt(struct st7571_device *st7567) 649 - { 650 - struct device *dev = &st7567->client->dev; 651 - struct device_node *np = dev->of_node; 652 - struct display_timing dt; 653 - int ret; 654 - 655 - ret = of_get_display_timing(np, "panel-timing", &dt); 656 - if (ret) { 657 - dev_err(dev, "Failed to get display timing from DT\n"); 658 - return ret; 659 - } 660 - 661 - of_property_read_u32(np, "width-mm", &st7567->width_mm); 662 - of_property_read_u32(np, "height-mm", &st7567->height_mm); 663 - st7567->inverted = of_property_read_bool(np, "sitronix,inverted"); 664 - 665 - st7567->pformat = &st7571_monochrome; 666 - st7567->bpp = 1; 667 - 668 - st7567->startline = dt.vfront_porch.typ; 669 - st7567->nlines = dt.vactive.typ; 670 - st7567->ncols = dt.hactive.typ; 671 - 672 - return 0; 673 - } 674 - 675 - static int st7571_parse_dt(struct st7571_device *st7571) 676 - { 677 - struct device *dev = &st7571->client->dev; 678 - struct device_node *np = dev->of_node; 679 - struct display_timing dt; 680 - int ret; 681 - 682 - ret = of_get_display_timing(np, "panel-timing", &dt); 683 - if (ret) { 684 - dev_err(dev, "Failed to get display timing from DT\n"); 685 - return ret; 686 - } 687 - 688 - of_property_read_u32(np, "width-mm", &st7571->width_mm); 689 - of_property_read_u32(np, "height-mm", &st7571->height_mm); 690 - st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale"); 691 - st7571->inverted = of_property_read_bool(np, "sitronix,inverted"); 692 - 693 - if (st7571->grayscale) { 694 - st7571->pformat = &st7571_grayscale; 695 - st7571->bpp = 2; 696 - } else { 697 - st7571->pformat = &st7571_monochrome; 698 - st7571->bpp = 1; 699 - } 700 - 701 - st7571->startline = dt.vfront_porch.typ; 702 - st7571->nlines = dt.vactive.typ; 703 - st7571->ncols = dt.hactive.typ; 704 - 705 - st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 706 - if (IS_ERR(st7571->reset)) 707 - return dev_err_probe(dev, PTR_ERR(st7571->reset), 708 - "Failed to get reset gpio\n"); 709 - 710 - 711 - return 0; 712 - } 713 - 714 - static void st7571_reset(struct st7571_device *st7571) 715 - { 716 - gpiod_set_value_cansleep(st7571->reset, 1); 717 - fsleep(20); 718 - gpiod_set_value_cansleep(st7571->reset, 0); 719 - } 720 - 721 - static int st7567_lcd_init(struct st7571_device *st7567) 722 - { 723 - /* 724 - * Most of the initialization sequence is taken directly from the 725 - * referential initial code in the ST7567 datasheet. 726 - */ 727 - u8 commands[] = { 728 - ST7571_DISPLAY_OFF, 729 - 730 - ST7567_SET_LCD_BIAS(1), 731 - 732 - ST7571_SET_SEG_SCAN_DIR(0), 733 - ST7571_SET_COM_SCAN_DIR(1), 734 - 735 - ST7571_SET_REGULATOR_REG(4), 736 - ST7571_SET_CONTRAST_MSB, 737 - ST7571_SET_CONTRAST_LSB(0x20), 738 - 739 - ST7571_SET_START_LINE_MSB, 740 - ST7571_SET_START_LINE_LSB(st7567->startline), 741 - 742 - ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 743 - ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 744 - ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 745 - 746 - ST7571_SET_REVERSE(st7567->inverted ? 1 : 0), 747 - ST7571_SET_ENTIRE_DISPLAY_ON(0), 748 - }; 749 - 750 - return st7571_send_command_list(st7567, commands, ARRAY_SIZE(commands)); 751 - } 752 - 753 - static int st7571_lcd_init(struct st7571_device *st7571) 754 - { 755 - /* 756 - * Most of the initialization sequence is taken directly from the 757 - * referential initial code in the ST7571 datasheet. 758 - */ 759 - u8 commands[] = { 760 - ST7571_DISPLAY_OFF, 761 - 762 - ST7571_SET_MODE_MSB, 763 - ST7571_SET_MODE_LSB(0x2e), 764 - 765 - ST7571_SET_SEG_SCAN_DIR(0), 766 - ST7571_SET_COM_SCAN_DIR(1), 767 - 768 - ST7571_SET_COM0_MSB, 769 - ST7571_SET_COM0_LSB(0x00), 770 - 771 - ST7571_SET_START_LINE_MSB, 772 - ST7571_SET_START_LINE_LSB(st7571->startline), 773 - 774 - ST7571_OSC_ON, 775 - ST7571_SET_REGULATOR_REG(5), 776 - ST7571_SET_CONTRAST_MSB, 777 - ST7571_SET_CONTRAST_LSB(0x33), 778 - ST7571_SET_LCD_BIAS(0x04), 779 - ST7571_SET_DISPLAY_DUTY_MSB, 780 - ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines), 781 - 782 - ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 783 - ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 784 - ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 785 - 786 - ST7571_COMMAND_SET_3, 787 - ST7571_SET_COLOR_MODE(st7571->pformat->mode), 788 - ST7571_COMMAND_SET_NORMAL, 789 - 790 - ST7571_SET_REVERSE(st7571->inverted ? 1 : 0), 791 - ST7571_SET_ENTIRE_DISPLAY_ON(0), 792 - }; 793 - 794 - /* Perform a reset before initializing the controller */ 795 - st7571_reset(st7571); 796 - 797 - return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands)); 798 - } 799 - 800 - static int st7571_probe(struct i2c_client *client) 219 + static int st7571_i2c_probe(struct i2c_client *client) 801 220 { 802 221 struct st7571_device *st7571; 803 - struct drm_device *dev; 804 - int ret; 222 + struct st7571_i2c_transport *t; 223 + struct regmap *regmap; 805 224 806 - st7571 = devm_drm_dev_alloc(&client->dev, &st7571_driver, 807 - struct st7571_device, dev); 808 - if (IS_ERR(st7571)) 809 - return PTR_ERR(st7571); 225 + t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL); 226 + if (!t) 227 + return -ENOMEM; 810 228 811 - dev = &st7571->dev; 812 - st7571->client = client; 813 - i2c_set_clientdata(client, st7571); 814 - st7571->pdata = device_get_match_data(&client->dev); 815 - 816 - ret = st7571->pdata->parse_dt(st7571); 817 - if (ret) 818 - return ret; 819 - 820 - ret = st7571_validate_parameters(st7571); 821 - if (ret) 822 - return ret; 823 - 824 - st7571->mode = st7571_mode(st7571); 229 + t->client = client; 825 230 826 231 /* 827 232 * The hardware design could make it hard to detect a NAK on the I2C bus. ··· 101 964 * cruft in the logs. 102 965 */ 103 966 if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING)) 104 - st7571->ignore_nak = true; 967 + t->ignore_nak = true; 105 968 106 - st7571->regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus, 107 - client, &st7571_regmap_config); 108 - if (IS_ERR(st7571->regmap)) { 109 - return dev_err_probe(&client->dev, PTR_ERR(st7571->regmap), 969 + regmap = devm_regmap_init(&client->dev, &st7571_i2c_regmap_bus, 970 + t, &st7571_i2c_regmap_config); 971 + if (IS_ERR(regmap)) { 972 + return dev_err_probe(&client->dev, PTR_ERR(regmap), 110 973 "Failed to initialize regmap\n"); 111 974 } 112 975 113 - st7571->hwbuf = devm_kzalloc(&client->dev, 114 - (st7571->nlines * st7571->ncols * st7571->bpp) / 8, 115 - GFP_KERNEL); 116 - if (!st7571->hwbuf) 117 - return -ENOMEM; 976 + st7571 = st7571_probe(&client->dev, regmap); 977 + if (IS_ERR(st7571)) 978 + return dev_err_probe(&client->dev, PTR_ERR(st7571), 979 + "Failed to initialize regmap\n"); 118 980 119 - st7571->row = devm_kzalloc(&client->dev, 120 - (st7571->ncols * st7571->bpp), 121 - GFP_KERNEL); 122 - if (!st7571->row) 123 - return -ENOMEM; 124 - 125 - ret = st7571_mode_config_init(st7571); 126 - if (ret) 127 - return dev_err_probe(&client->dev, ret, 128 - "Failed to initialize mode config\n"); 129 - 130 - ret = st7571_plane_init(st7571, st7571->pformat); 131 - if (ret) 132 - return dev_err_probe(&client->dev, ret, 133 - "Failed to initialize primary plane\n"); 134 - 135 - ret = st7571_crtc_init(st7571); 136 - if (ret < 0) 137 - return dev_err_probe(&client->dev, ret, 138 - "Failed to initialize CRTC\n"); 139 - 140 - ret = st7571_encoder_init(st7571); 141 - if (ret < 0) 142 - return dev_err_probe(&client->dev, ret, 143 - "Failed to initialize encoder\n"); 144 - 145 - ret = st7571_connector_init(st7571); 146 - if (ret < 0) 147 - return dev_err_probe(&client->dev, ret, 148 - "Failed to initialize connector\n"); 149 - 150 - drm_mode_config_reset(dev); 151 - 152 - ret = drm_dev_register(dev, 0); 153 - if (ret) 154 - return dev_err_probe(&client->dev, ret, 155 - "Failed to register DRM device\n"); 156 - 157 - drm_client_setup(dev, NULL); 981 + i2c_set_clientdata(client, st7571); 158 982 return 0; 159 983 } 160 984 161 - static void st7571_remove(struct i2c_client *client) 985 + static void st7571_i2c_remove(struct i2c_client *client) 162 986 { 163 987 struct st7571_device *st7571 = i2c_get_clientdata(client); 164 988 165 - drm_dev_unplug(&st7571->dev); 989 + st7571_remove(st7571); 166 990 } 167 - 168 - static const struct st7571_panel_data st7567_config = { 169 - .init = st7567_lcd_init, 170 - .parse_dt = st7567_parse_dt, 171 - .constraints = { 172 - .min_nlines = 1, 173 - .max_nlines = 64, 174 - .min_ncols = 128, 175 - .max_ncols = 128, 176 - .support_grayscale = false, 177 - }, 178 - }; 179 - 180 - static const struct st7571_panel_data st7571_config = { 181 - .init = st7571_lcd_init, 182 - .parse_dt = st7571_parse_dt, 183 - .constraints = { 184 - .min_nlines = 1, 185 - .max_nlines = 128, 186 - .min_ncols = 128, 187 - .max_ncols = 128, 188 - .support_grayscale = true, 189 - }, 190 - }; 191 991 192 992 static const struct of_device_id st7571_of_match[] = { 193 993 { .compatible = "sitronix,st7567", .data = &st7567_config }, ··· 142 1068 143 1069 static struct i2c_driver st7571_i2c_driver = { 144 1070 .driver = { 145 - .name = "st7571", 1071 + .name = "st7571-i2c", 146 1072 .of_match_table = st7571_of_match, 147 1073 }, 148 - .probe = st7571_probe, 149 - .remove = st7571_remove, 1074 + .probe = st7571_i2c_probe, 1075 + .remove = st7571_i2c_remove, 150 1076 .id_table = st7571_id, 151 1077 }; 152 1078 153 1079 module_i2c_driver(st7571_i2c_driver); 154 1080 155 1081 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); 156 - MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller"); 1082 + MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (I2C)"); 157 1083 MODULE_LICENSE("GPL"); 1084 + MODULE_IMPORT_NS("DRM_ST7571");
+76
drivers/gpu/drm/sitronix/st7571-spi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Driver for Sitronix ST7571 connected via SPI bus. 4 + * 5 + * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com> 6 + */ 7 + 8 + #include <linux/spi/spi.h> 9 + #include <linux/module.h> 10 + #include <linux/regmap.h> 11 + 12 + #include "st7571.h" 13 + 14 + static const struct regmap_config st7571_spi_regmap_config = { 15 + .reg_bits = 8, 16 + .val_bits = 8, 17 + .can_multi_write = true, 18 + }; 19 + 20 + static int st7571_spi_probe(struct spi_device *spi) 21 + { 22 + struct st7571_device *st7571; 23 + struct regmap *regmap; 24 + 25 + regmap = devm_regmap_init_spi(spi, &st7571_spi_regmap_config); 26 + if (IS_ERR(regmap)) { 27 + return dev_err_probe(&spi->dev, PTR_ERR(regmap), 28 + "Failed to initialize regmap\n"); 29 + } 30 + 31 + st7571 = st7571_probe(&spi->dev, regmap); 32 + if (IS_ERR(st7571)) 33 + return dev_err_probe(&spi->dev, PTR_ERR(st7571), 34 + "Failed to initialize regmap\n"); 35 + 36 + spi_set_drvdata(spi, st7571); 37 + return 0; 38 + } 39 + 40 + static void st7571_spi_remove(struct spi_device *spi) 41 + { 42 + struct st7571_device *st7571 = spi_get_drvdata(spi); 43 + 44 + st7571_remove(st7571); 45 + } 46 + 47 + static const struct of_device_id st7571_of_match[] = { 48 + { .compatible = "sitronix,st7567", .data = &st7567_config }, 49 + { .compatible = "sitronix,st7571", .data = &st7571_config }, 50 + {}, 51 + }; 52 + MODULE_DEVICE_TABLE(of, st7571_of_match); 53 + 54 + static const struct spi_device_id st7571_spi_id[] = { 55 + { "st7567", 0 }, 56 + { "st7571", 0 }, 57 + { } 58 + }; 59 + MODULE_DEVICE_TABLE(spi, st7571_spi_id); 60 + 61 + static struct spi_driver st7571_spi_driver = { 62 + .driver = { 63 + .name = "st7571-spi", 64 + .of_match_table = st7571_of_match, 65 + }, 66 + .probe = st7571_spi_probe, 67 + .remove = st7571_spi_remove, 68 + .id_table = st7571_spi_id, 69 + }; 70 + 71 + module_spi_driver(st7571_spi_driver); 72 + 73 + MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); 74 + MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (SPI)"); 75 + MODULE_LICENSE("GPL"); 76 + MODULE_IMPORT_NS("DRM_ST7571");
+918
drivers/gpu/drm/sitronix/st7571.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller 4 + * 5 + * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/delay.h> 10 + #include <linux/gpio/consumer.h> 11 + #include <linux/i2c.h> 12 + #include <linux/module.h> 13 + #include <linux/regmap.h> 14 + 15 + #include <drm/clients/drm_client_setup.h> 16 + #include <drm/drm_atomic.h> 17 + #include <drm/drm_atomic_helper.h> 18 + #include <drm/drm_connector.h> 19 + #include <drm/drm_crtc_helper.h> 20 + #include <drm/drm_damage_helper.h> 21 + #include <drm/drm_drv.h> 22 + #include <drm/drm_encoder.h> 23 + #include <drm/drm_fb_helper.h> 24 + #include <drm/drm_fbdev_shmem.h> 25 + #include <drm/drm_fourcc.h> 26 + #include <drm/drm_framebuffer.h> 27 + #include <drm/drm_gem_atomic_helper.h> 28 + #include <drm/drm_gem_framebuffer_helper.h> 29 + #include <drm/drm_gem_shmem_helper.h> 30 + #include <drm/drm_modeset_helper_vtables.h> 31 + #include <drm/drm_module.h> 32 + #include <drm/drm_plane.h> 33 + #include <drm/drm_probe_helper.h> 34 + 35 + #include <video/display_timing.h> 36 + #include <video/of_display_timing.h> 37 + 38 + #include "st7571.h" 39 + 40 + #define ST7571_COMMAND_MODE (0x00) 41 + #define ST7571_DATA_MODE (0x40) 42 + 43 + /* Normal mode command set */ 44 + #define ST7571_DISPLAY_OFF (0xae) 45 + #define ST7571_DISPLAY_ON (0xaf) 46 + #define ST7571_OSC_ON (0xab) 47 + #define ST7571_SET_COLUMN_LSB(c) (0x00 | FIELD_PREP(GENMASK(3, 0), (c))) 48 + #define ST7571_SET_COLUMN_MSB(c) (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4)) 49 + #define ST7571_SET_COM0_LSB(x) (FIELD_PREP(GENMASK(6, 0), (x))) 50 + #define ST7571_SET_COM0_MSB (0x44) 51 + #define ST7571_SET_COM_SCAN_DIR(d) (0xc0 | FIELD_PREP(GENMASK(3, 3), (d))) 52 + #define ST7571_SET_CONTRAST_LSB(c) (FIELD_PREP(GENMASK(5, 0), (c))) 53 + #define ST7571_SET_CONTRAST_MSB (0x81) 54 + #define ST7571_SET_DISPLAY_DUTY_LSB(d) (FIELD_PREP(GENMASK(7, 0), (d))) 55 + #define ST7571_SET_DISPLAY_DUTY_MSB (0x48) 56 + #define ST7571_SET_ENTIRE_DISPLAY_ON(p) (0xa4 | FIELD_PREP(GENMASK(0, 0), (p))) 57 + #define ST7571_SET_LCD_BIAS(b) (0x50 | FIELD_PREP(GENMASK(2, 0), (b))) 58 + #define ST7571_SET_MODE_LSB(m) (FIELD_PREP(GENMASK(7, 2), (m))) 59 + #define ST7571_SET_MODE_MSB (0x38) 60 + #define ST7571_SET_PAGE(p) (0xb0 | FIELD_PREP(GENMASK(3, 0), (p))) 61 + #define ST7571_SET_POWER(p) (0x28 | FIELD_PREP(GENMASK(2, 0), (p))) 62 + #define ST7571_SET_REGULATOR_REG(r) (0x20 | FIELD_PREP(GENMASK(2, 0), (r))) 63 + #define ST7571_SET_REVERSE(r) (0xa6 | FIELD_PREP(GENMASK(0, 0), (r))) 64 + #define ST7571_SET_SEG_SCAN_DIR(d) (0xa0 | FIELD_PREP(GENMASK(0, 0), (d))) 65 + #define ST7571_SET_START_LINE_LSB(l) (FIELD_PREP(GENMASK(6, 0), (l))) 66 + #define ST7571_SET_START_LINE_MSB (0x40) 67 + 68 + /* Extension command set 3 */ 69 + #define ST7571_COMMAND_SET_3 (0x7b) 70 + #define ST7571_SET_COLOR_MODE(c) (0x10 | FIELD_PREP(GENMASK(0, 0), (c))) 71 + #define ST7571_COMMAND_SET_NORMAL (0x00) 72 + 73 + /* ST7567 commands */ 74 + #define ST7567_SET_LCD_BIAS(m) (0xa2 | FIELD_PREP(GENMASK(0, 0), (m))) 75 + 76 + #define ST7571_PAGE_HEIGHT 8 77 + 78 + #define DRIVER_NAME "st7571" 79 + #define DRIVER_DESC "ST7571 DRM driver" 80 + #define DRIVER_MAJOR 1 81 + #define DRIVER_MINOR 0 82 + 83 + static inline struct st7571_device *drm_to_st7571(struct drm_device *drm) 84 + { 85 + return container_of(drm, struct st7571_device, drm); 86 + } 87 + 88 + static int st7571_send_command_list(struct st7571_device *st7571, 89 + const u8 *cmd_list, size_t len) 90 + { 91 + int ret; 92 + 93 + for (int i = 0; i < len; i++) { 94 + ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]); 95 + if (ret < 0) 96 + return ret; 97 + } 98 + 99 + return ret; 100 + } 101 + 102 + static inline u8 st7571_transform_xy(const char *p, int x, int y, u8 bpp) 103 + { 104 + int xrest = x % 8; 105 + u8 result = 0; 106 + u8 row_len = 16 * bpp; 107 + 108 + /* 109 + * Transforms an (x, y) pixel coordinate into a vertical 8-bit 110 + * column from the framebuffer. It calculates the corresponding byte in the 111 + * framebuffer, extracts the bit at the given x position across 8 consecutive 112 + * rows, and packs those bits into a single byte. 113 + * 114 + * Return an 8-bit value representing a vertical column of pixels. 115 + */ 116 + x = x / 8; 117 + y = (y / 8) * 8; 118 + 119 + for (int i = 0; i < 8; i++) { 120 + int row_idx = y + i; 121 + u8 byte = p[row_idx * row_len + x]; 122 + u8 bit = (byte >> xrest) & 1; 123 + 124 + result |= (bit << i); 125 + } 126 + 127 + return result; 128 + } 129 + 130 + static int st7571_set_position(struct st7571_device *st7571, int x, int y) 131 + { 132 + u8 cmd_list[] = { 133 + ST7571_SET_COLUMN_LSB(x), 134 + ST7571_SET_COLUMN_MSB(x), 135 + ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT), 136 + }; 137 + 138 + return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list)); 139 + } 140 + 141 + static int st7571_fb_clear_screen(struct st7571_device *st7571) 142 + { 143 + u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp; 144 + char pixelvalue = 0x00; 145 + 146 + st7571_set_position(st7571, 0, 0); 147 + for (int i = 0; i < npixels; i++) 148 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1); 149 + 150 + return 0; 151 + } 152 + 153 + static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571, 154 + const struct iosys_map *vmap, 155 + struct drm_framebuffer *fb, 156 + struct drm_rect *rect, 157 + struct drm_format_conv_state *fmtcnv_state) 158 + { 159 + unsigned int dst_pitch; 160 + struct iosys_map dst; 161 + u32 size; 162 + 163 + switch (fb->format->format) { 164 + case DRM_FORMAT_XRGB8888: 165 + dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); 166 + iosys_map_set_vaddr(&dst, st7571->hwbuf); 167 + 168 + drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 169 + break; 170 + 171 + case DRM_FORMAT_R1: 172 + size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 173 + memcpy(st7571->hwbuf, vmap->vaddr, size); 174 + break; 175 + } 176 + } 177 + 178 + static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571, 179 + const struct iosys_map *vmap, 180 + struct drm_framebuffer *fb, 181 + struct drm_rect *rect, 182 + struct drm_format_conv_state *fmtcnv_state) 183 + { 184 + u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 185 + unsigned int dst_pitch; 186 + struct iosys_map dst; 187 + 188 + switch (fb->format->format) { 189 + case DRM_FORMAT_XRGB8888: 190 + dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 4); 191 + iosys_map_set_vaddr(&dst, st7571->hwbuf); 192 + 193 + drm_fb_xrgb8888_to_gray2(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 194 + break; 195 + 196 + case DRM_FORMAT_R1: 197 + size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8; 198 + memcpy(st7571->hwbuf, vmap->vaddr, size); 199 + break; 200 + 201 + case DRM_FORMAT_R2: 202 + size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4; 203 + memcpy(st7571->hwbuf, vmap->vaddr, size); 204 + break; 205 + } 206 + } 207 + 208 + static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect) 209 + { 210 + struct st7571_device *st7571 = drm_to_st7571(fb->dev); 211 + char *row = st7571->row; 212 + 213 + /* Align y to display page boundaries */ 214 + rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 215 + rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 216 + 217 + for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 218 + for (int x = rect->x1; x < rect->x2; x++) 219 + row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 1); 220 + 221 + st7571_set_position(st7571, rect->x1, y); 222 + 223 + /* TODO: Investige why we can't write multiple bytes at once */ 224 + for (int x = rect->x1; x < rect->x2; x++) 225 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 226 + } 227 + 228 + return 0; 229 + } 230 + 231 + static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect) 232 + { 233 + struct st7571_device *st7571 = drm_to_st7571(fb->dev); 234 + u32 format = fb->format->format; 235 + char *row = st7571->row; 236 + int x1; 237 + int x2; 238 + 239 + /* Align y to display page boundaries */ 240 + rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT); 241 + rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines); 242 + 243 + switch (format) { 244 + case DRM_FORMAT_R1: 245 + x1 = rect->x1 * 1; 246 + x2 = rect->x2 * 1; 247 + break; 248 + case DRM_FORMAT_R2: 249 + fallthrough; 250 + case DRM_FORMAT_XRGB8888: 251 + x1 = rect->x1 * 2; 252 + x2 = rect->x2 * 2; 253 + break; 254 + } 255 + 256 + for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) { 257 + for (int x = x1; x < x2; x++) 258 + row[x] = st7571_transform_xy(st7571->hwbuf, x, y, 2); 259 + 260 + st7571_set_position(st7571, rect->x1, y); 261 + 262 + /* TODO: Investige why we can't write multiple bytes at once */ 263 + for (int x = x1; x < x2; x++) { 264 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 265 + 266 + /* 267 + * As the display supports grayscale, all pixels must be written as two bits 268 + * even if the format is monochrome. 269 + * 270 + * The bit values maps to the following grayscale: 271 + * 0 0 = Black 272 + * 0 1 = Dark gray 273 + * 1 0 = Light gray 274 + * 1 1 = White 275 + * 276 + * For monochrome formats, write the same value twice to get 277 + * either a black or white pixel. 278 + */ 279 + if (format == DRM_FORMAT_R1) 280 + regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1); 281 + } 282 + } 283 + 284 + return 0; 285 + } 286 + 287 + static int st7571_connector_get_modes(struct drm_connector *conn) 288 + { 289 + struct st7571_device *st7571 = drm_to_st7571(conn->dev); 290 + 291 + return drm_connector_helper_get_modes_fixed(conn, &st7571->mode); 292 + } 293 + 294 + static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = { 295 + .get_modes = st7571_connector_get_modes, 296 + }; 297 + 298 + static const struct st7571_panel_format st7571_monochrome = { 299 + .prepare_buffer = st7571_prepare_buffer_monochrome, 300 + .update_rect = st7571_fb_update_rect_monochrome, 301 + .mode = ST7571_COLOR_MODE_BLACKWHITE, 302 + .formats = { 303 + DRM_FORMAT_XRGB8888, 304 + DRM_FORMAT_R1, 305 + }, 306 + .nformats = 2, 307 + }; 308 + 309 + static const struct st7571_panel_format st7571_grayscale = { 310 + .prepare_buffer = st7571_prepare_buffer_grayscale, 311 + .update_rect = st7571_fb_update_rect_grayscale, 312 + .mode = ST7571_COLOR_MODE_GRAY, 313 + .formats = { 314 + DRM_FORMAT_XRGB8888, 315 + DRM_FORMAT_R1, 316 + DRM_FORMAT_R2, 317 + }, 318 + .nformats = 3, 319 + }; 320 + 321 + static const u64 st7571_primary_plane_fmtmods[] = { 322 + DRM_FORMAT_MOD_LINEAR, 323 + DRM_FORMAT_MOD_INVALID 324 + }; 325 + 326 + static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane, 327 + struct drm_atomic_state *state) 328 + { 329 + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); 330 + struct drm_crtc *new_crtc = new_plane_state->crtc; 331 + struct drm_crtc_state *new_crtc_state = NULL; 332 + 333 + if (new_crtc) 334 + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); 335 + 336 + return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, 337 + DRM_PLANE_NO_SCALING, 338 + DRM_PLANE_NO_SCALING, 339 + false, false); 340 + } 341 + 342 + static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane, 343 + struct drm_atomic_state *state) 344 + { 345 + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 346 + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 347 + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 348 + struct drm_framebuffer *fb = plane_state->fb; 349 + struct drm_atomic_helper_damage_iter iter; 350 + struct drm_device *drm = plane->dev; 351 + struct drm_rect damage; 352 + struct st7571_device *st7571 = drm_to_st7571(plane->dev); 353 + int ret, idx; 354 + 355 + if (!fb) 356 + return; /* no framebuffer; plane is disabled */ 357 + 358 + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 359 + if (ret) 360 + return; 361 + 362 + if (!drm_dev_enter(drm, &idx)) 363 + goto out_drm_gem_fb_end_cpu_access; 364 + 365 + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 366 + drm_atomic_for_each_plane_damage(&iter, &damage) { 367 + st7571->pformat->prepare_buffer(st7571, 368 + &shadow_plane_state->data[0], 369 + fb, &damage, 370 + &shadow_plane_state->fmtcnv_state); 371 + 372 + st7571->pformat->update_rect(fb, &damage); 373 + } 374 + 375 + drm_dev_exit(idx); 376 + 377 + out_drm_gem_fb_end_cpu_access: 378 + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 379 + } 380 + 381 + static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane, 382 + struct drm_atomic_state *state) 383 + { 384 + struct drm_device *drm = plane->dev; 385 + struct st7571_device *st7571 = drm_to_st7571(plane->dev); 386 + int idx; 387 + 388 + if (!drm_dev_enter(drm, &idx)) 389 + return; 390 + 391 + st7571_fb_clear_screen(st7571); 392 + drm_dev_exit(idx); 393 + } 394 + 395 + static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = { 396 + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 397 + .atomic_check = st7571_primary_plane_helper_atomic_check, 398 + .atomic_update = st7571_primary_plane_helper_atomic_update, 399 + .atomic_disable = st7571_primary_plane_helper_atomic_disable, 400 + }; 401 + 402 + static const struct drm_plane_funcs st7571_primary_plane_funcs = { 403 + .update_plane = drm_atomic_helper_update_plane, 404 + .disable_plane = drm_atomic_helper_disable_plane, 405 + .destroy = drm_plane_cleanup, 406 + DRM_GEM_SHADOW_PLANE_FUNCS, 407 + }; 408 + 409 + /* 410 + * CRTC 411 + */ 412 + 413 + static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc, 414 + const struct drm_display_mode *mode) 415 + { 416 + struct st7571_device *st7571 = drm_to_st7571(crtc->dev); 417 + 418 + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode); 419 + } 420 + 421 + static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = { 422 + .atomic_check = drm_crtc_helper_atomic_check, 423 + .mode_valid = st7571_crtc_mode_valid, 424 + }; 425 + 426 + static const struct drm_crtc_funcs st7571_crtc_funcs = { 427 + .reset = drm_atomic_helper_crtc_reset, 428 + .destroy = drm_crtc_cleanup, 429 + .set_config = drm_atomic_helper_set_config, 430 + .page_flip = drm_atomic_helper_page_flip, 431 + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 432 + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 433 + }; 434 + 435 + /* 436 + * Encoder 437 + */ 438 + 439 + static void st7571_encoder_atomic_enable(struct drm_encoder *encoder, 440 + struct drm_atomic_state *state) 441 + { 442 + struct drm_device *drm = encoder->dev; 443 + struct st7571_device *st7571 = drm_to_st7571(drm); 444 + u8 command = ST7571_DISPLAY_ON; 445 + int ret; 446 + 447 + ret = st7571->pdata->init(st7571); 448 + if (ret) 449 + return; 450 + 451 + st7571_send_command_list(st7571, &command, 1); 452 + } 453 + 454 + static void st7571_encoder_atomic_disable(struct drm_encoder *encoder, 455 + struct drm_atomic_state *state) 456 + { 457 + struct drm_device *drm = encoder->dev; 458 + struct st7571_device *st7571 = drm_to_st7571(drm); 459 + u8 command = ST7571_DISPLAY_OFF; 460 + 461 + st7571_send_command_list(st7571, &command, 1); 462 + } 463 + 464 + static const struct drm_encoder_funcs st7571_encoder_funcs = { 465 + .destroy = drm_encoder_cleanup, 466 + 467 + }; 468 + 469 + static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = { 470 + .atomic_enable = st7571_encoder_atomic_enable, 471 + .atomic_disable = st7571_encoder_atomic_disable, 472 + }; 473 + 474 + /* 475 + * Connector 476 + */ 477 + 478 + static const struct drm_connector_funcs st7571_connector_funcs = { 479 + .reset = drm_atomic_helper_connector_reset, 480 + .fill_modes = drm_helper_probe_single_connector_modes, 481 + .destroy = drm_connector_cleanup, 482 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 483 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 484 + }; 485 + 486 + static const struct drm_mode_config_funcs st7571_mode_config_funcs = { 487 + .fb_create = drm_gem_fb_create_with_dirty, 488 + .atomic_check = drm_atomic_helper_check, 489 + .atomic_commit = drm_atomic_helper_commit, 490 + }; 491 + 492 + static struct drm_display_mode st7571_mode(struct st7571_device *st7571) 493 + { 494 + struct drm_display_mode mode = { 495 + DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines, 496 + st7571->width_mm, st7571->height_mm), 497 + }; 498 + 499 + return mode; 500 + } 501 + 502 + static int st7571_mode_config_init(struct st7571_device *st7571) 503 + { 504 + struct drm_device *drm = &st7571->drm; 505 + const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 506 + int ret; 507 + 508 + ret = drmm_mode_config_init(drm); 509 + if (ret) 510 + return ret; 511 + 512 + drm->mode_config.min_width = constraints->min_ncols; 513 + drm->mode_config.min_height = constraints->min_nlines; 514 + drm->mode_config.max_width = constraints->max_ncols; 515 + drm->mode_config.max_height = constraints->max_nlines; 516 + drm->mode_config.preferred_depth = 24; 517 + drm->mode_config.funcs = &st7571_mode_config_funcs; 518 + 519 + return 0; 520 + } 521 + 522 + static int st7571_plane_init(struct st7571_device *st7571, 523 + const struct st7571_panel_format *pformat) 524 + { 525 + struct drm_plane *primary_plane = &st7571->primary_plane; 526 + struct drm_device *drm = &st7571->drm; 527 + int ret; 528 + 529 + ret = drm_universal_plane_init(drm, primary_plane, 0, 530 + &st7571_primary_plane_funcs, 531 + pformat->formats, 532 + pformat->nformats, 533 + st7571_primary_plane_fmtmods, 534 + DRM_PLANE_TYPE_PRIMARY, NULL); 535 + if (ret) 536 + return ret; 537 + 538 + drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs); 539 + drm_plane_enable_fb_damage_clips(primary_plane); 540 + 541 + return 0; 542 + } 543 + 544 + static int st7571_crtc_init(struct st7571_device *st7571) 545 + { 546 + struct drm_plane *primary_plane = &st7571->primary_plane; 547 + struct drm_crtc *crtc = &st7571->crtc; 548 + struct drm_device *drm = &st7571->drm; 549 + int ret; 550 + 551 + ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, 552 + &st7571_crtc_funcs, NULL); 553 + if (ret) 554 + return ret; 555 + 556 + drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs); 557 + 558 + return 0; 559 + } 560 + 561 + static int st7571_encoder_init(struct st7571_device *st7571) 562 + { 563 + struct drm_encoder *encoder = &st7571->encoder; 564 + struct drm_crtc *crtc = &st7571->crtc; 565 + struct drm_device *drm = &st7571->drm; 566 + int ret; 567 + 568 + ret = drm_encoder_init(drm, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); 569 + if (ret) 570 + return ret; 571 + 572 + drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs); 573 + 574 + encoder->possible_crtcs = drm_crtc_mask(crtc); 575 + 576 + return 0; 577 + } 578 + 579 + static int st7571_connector_init(struct st7571_device *st7571) 580 + { 581 + struct drm_connector *connector = &st7571->connector; 582 + struct drm_encoder *encoder = &st7571->encoder; 583 + struct drm_device *drm = &st7571->drm; 584 + int ret; 585 + 586 + ret = drm_connector_init(drm, connector, &st7571_connector_funcs, 587 + DRM_MODE_CONNECTOR_Unknown); 588 + if (ret) 589 + return ret; 590 + 591 + drm_connector_helper_add(connector, &st7571_connector_helper_funcs); 592 + 593 + return drm_connector_attach_encoder(connector, encoder); 594 + } 595 + 596 + DEFINE_DRM_GEM_FOPS(st7571_fops); 597 + 598 + static const struct drm_driver st7571_driver = { 599 + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 600 + 601 + .name = DRIVER_NAME, 602 + .desc = DRIVER_DESC, 603 + .major = DRIVER_MAJOR, 604 + .minor = DRIVER_MINOR, 605 + 606 + .fops = &st7571_fops, 607 + DRM_GEM_SHMEM_DRIVER_OPS, 608 + DRM_FBDEV_SHMEM_DRIVER_OPS, 609 + }; 610 + 611 + static int st7571_validate_parameters(struct st7571_device *st7571) 612 + { 613 + struct device *dev = st7571->dev; 614 + const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints; 615 + 616 + if (st7571->width_mm == 0) { 617 + dev_err(dev, "Invalid panel width\n"); 618 + return -EINVAL; 619 + } 620 + 621 + if (st7571->height_mm == 0) { 622 + dev_err(dev, "Invalid panel height\n"); 623 + return -EINVAL; 624 + } 625 + 626 + if (st7571->nlines < constraints->min_nlines || 627 + st7571->nlines > constraints->max_nlines) { 628 + dev_err(dev, "Invalid timing configuration.\n"); 629 + return -EINVAL; 630 + } 631 + 632 + if (st7571->startline + st7571->nlines > constraints->max_nlines) { 633 + dev_err(dev, "Invalid timing configuration.\n"); 634 + return -EINVAL; 635 + } 636 + 637 + if (st7571->ncols < constraints->min_ncols || 638 + st7571->ncols > constraints->max_ncols) { 639 + dev_err(dev, "Invalid timing configuration.\n"); 640 + return -EINVAL; 641 + } 642 + 643 + if (st7571->grayscale && !constraints->support_grayscale) { 644 + dev_err(dev, "Grayscale not supported\n"); 645 + return -EINVAL; 646 + } 647 + 648 + return 0; 649 + } 650 + 651 + static int st7567_parse_dt(struct st7571_device *st7567) 652 + { 653 + struct device *dev = st7567->dev; 654 + struct device_node *np = dev->of_node; 655 + struct display_timing dt; 656 + int ret; 657 + 658 + ret = of_get_display_timing(np, "panel-timing", &dt); 659 + if (ret) { 660 + dev_err(dev, "Failed to get display timing from DT\n"); 661 + return ret; 662 + } 663 + 664 + of_property_read_u32(np, "width-mm", &st7567->width_mm); 665 + of_property_read_u32(np, "height-mm", &st7567->height_mm); 666 + st7567->inverted = of_property_read_bool(np, "sitronix,inverted"); 667 + 668 + st7567->pformat = &st7571_monochrome; 669 + st7567->bpp = 1; 670 + 671 + st7567->startline = dt.vfront_porch.typ; 672 + st7567->nlines = dt.vactive.typ; 673 + st7567->ncols = dt.hactive.typ; 674 + 675 + return 0; 676 + } 677 + 678 + static int st7571_parse_dt(struct st7571_device *st7571) 679 + { 680 + struct device *dev = st7571->dev; 681 + struct device_node *np = dev->of_node; 682 + struct display_timing dt; 683 + int ret; 684 + 685 + ret = of_get_display_timing(np, "panel-timing", &dt); 686 + if (ret) { 687 + dev_err(dev, "Failed to get display timing from DT\n"); 688 + return ret; 689 + } 690 + 691 + of_property_read_u32(np, "width-mm", &st7571->width_mm); 692 + of_property_read_u32(np, "height-mm", &st7571->height_mm); 693 + st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale"); 694 + st7571->inverted = of_property_read_bool(np, "sitronix,inverted"); 695 + 696 + if (st7571->grayscale) { 697 + st7571->pformat = &st7571_grayscale; 698 + st7571->bpp = 2; 699 + } else { 700 + st7571->pformat = &st7571_monochrome; 701 + st7571->bpp = 1; 702 + } 703 + 704 + st7571->startline = dt.vfront_porch.typ; 705 + st7571->nlines = dt.vactive.typ; 706 + st7571->ncols = dt.hactive.typ; 707 + 708 + st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 709 + if (IS_ERR(st7571->reset)) 710 + return dev_err_probe(dev, PTR_ERR(st7571->reset), 711 + "Failed to get reset gpio\n"); 712 + 713 + return 0; 714 + } 715 + 716 + static void st7571_reset(struct st7571_device *st7571) 717 + { 718 + gpiod_set_value_cansleep(st7571->reset, 1); 719 + fsleep(20); 720 + gpiod_set_value_cansleep(st7571->reset, 0); 721 + } 722 + 723 + static int st7567_lcd_init(struct st7571_device *st7567) 724 + { 725 + /* 726 + * Most of the initialization sequence is taken directly from the 727 + * referential initial code in the ST7567 datasheet. 728 + */ 729 + u8 commands[] = { 730 + ST7571_DISPLAY_OFF, 731 + 732 + ST7567_SET_LCD_BIAS(1), 733 + 734 + ST7571_SET_SEG_SCAN_DIR(0), 735 + ST7571_SET_COM_SCAN_DIR(1), 736 + 737 + ST7571_SET_REGULATOR_REG(4), 738 + ST7571_SET_CONTRAST_MSB, 739 + ST7571_SET_CONTRAST_LSB(0x20), 740 + 741 + ST7571_SET_START_LINE_MSB, 742 + ST7571_SET_START_LINE_LSB(st7567->startline), 743 + 744 + ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 745 + ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 746 + ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 747 + 748 + ST7571_SET_REVERSE(st7567->inverted ? 1 : 0), 749 + ST7571_SET_ENTIRE_DISPLAY_ON(0), 750 + }; 751 + 752 + return st7571_send_command_list(st7567, commands, ARRAY_SIZE(commands)); 753 + } 754 + 755 + static int st7571_lcd_init(struct st7571_device *st7571) 756 + { 757 + /* 758 + * Most of the initialization sequence is taken directly from the 759 + * referential initial code in the ST7571 datasheet. 760 + */ 761 + u8 commands[] = { 762 + ST7571_DISPLAY_OFF, 763 + 764 + ST7571_SET_MODE_MSB, 765 + ST7571_SET_MODE_LSB(0x2e), 766 + 767 + ST7571_SET_SEG_SCAN_DIR(0), 768 + ST7571_SET_COM_SCAN_DIR(1), 769 + 770 + ST7571_SET_COM0_MSB, 771 + ST7571_SET_COM0_LSB(0x00), 772 + 773 + ST7571_SET_START_LINE_MSB, 774 + ST7571_SET_START_LINE_LSB(st7571->startline), 775 + 776 + ST7571_OSC_ON, 777 + ST7571_SET_REGULATOR_REG(5), 778 + ST7571_SET_CONTRAST_MSB, 779 + ST7571_SET_CONTRAST_LSB(0x33), 780 + ST7571_SET_LCD_BIAS(0x04), 781 + ST7571_SET_DISPLAY_DUTY_MSB, 782 + ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines), 783 + 784 + ST7571_SET_POWER(0x4), /* Power Control, VC: ON, VR: OFF, VF: OFF */ 785 + ST7571_SET_POWER(0x6), /* Power Control, VC: ON, VR: ON, VF: OFF */ 786 + ST7571_SET_POWER(0x7), /* Power Control, VC: ON, VR: ON, VF: ON */ 787 + 788 + ST7571_COMMAND_SET_3, 789 + ST7571_SET_COLOR_MODE(st7571->pformat->mode), 790 + ST7571_COMMAND_SET_NORMAL, 791 + 792 + ST7571_SET_REVERSE(st7571->inverted ? 1 : 0), 793 + ST7571_SET_ENTIRE_DISPLAY_ON(0), 794 + }; 795 + 796 + /* Perform a reset before initializing the controller */ 797 + st7571_reset(st7571); 798 + 799 + return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands)); 800 + } 801 + 802 + struct st7571_device *st7571_probe(struct device *dev, 803 + struct regmap *regmap) 804 + { 805 + struct st7571_device *st7571; 806 + struct drm_device *drm; 807 + int ret; 808 + 809 + st7571 = devm_drm_dev_alloc(dev, &st7571_driver, 810 + struct st7571_device, drm); 811 + if (IS_ERR(st7571)) 812 + return st7571; 813 + 814 + drm = &st7571->drm; 815 + st7571->dev = dev; 816 + st7571->pdata = device_get_match_data(st7571->dev); 817 + 818 + ret = st7571->pdata->parse_dt(st7571); 819 + if (ret) 820 + return ERR_PTR(ret); 821 + 822 + ret = st7571_validate_parameters(st7571); 823 + if (ret) 824 + return ERR_PTR(ret); 825 + 826 + st7571->mode = st7571_mode(st7571); 827 + st7571->regmap = regmap; 828 + 829 + st7571->hwbuf = devm_kzalloc(st7571->dev, 830 + (st7571->nlines * st7571->ncols * st7571->bpp) / 8, 831 + GFP_KERNEL); 832 + if (!st7571->hwbuf) 833 + return ERR_PTR(-ENOMEM); 834 + 835 + st7571->row = devm_kzalloc(st7571->dev, 836 + (st7571->ncols * st7571->bpp), 837 + GFP_KERNEL); 838 + if (!st7571->row) 839 + return ERR_PTR(-ENOMEM); 840 + 841 + ret = st7571_mode_config_init(st7571); 842 + if (ret) { 843 + dev_err(st7571->dev, "Failed to initialize mode config\n"); 844 + return ERR_PTR(ret); 845 + } 846 + 847 + ret = st7571_plane_init(st7571, st7571->pformat); 848 + if (ret) { 849 + dev_err(st7571->dev, "Failed to initialize primary plane\n"); 850 + return ERR_PTR(ret); 851 + } 852 + 853 + ret = st7571_crtc_init(st7571); 854 + if (ret < 0) { 855 + dev_err(st7571->dev, "Failed to initialize CRTC\n"); 856 + return ERR_PTR(ret); 857 + } 858 + 859 + ret = st7571_encoder_init(st7571); 860 + if (ret < 0) { 861 + dev_err(st7571->dev, "Failed to initialize encoder\n"); 862 + return ERR_PTR(ret); 863 + } 864 + 865 + ret = st7571_connector_init(st7571); 866 + if (ret < 0) { 867 + dev_err(st7571->dev, "Failed to initialize connector\n"); 868 + return ERR_PTR(ret); 869 + } 870 + 871 + drm_mode_config_reset(drm); 872 + 873 + ret = drm_dev_register(drm, 0); 874 + if (ret) { 875 + dev_err(st7571->dev, "Failed to register DRM device\n"); 876 + return ERR_PTR(ret); 877 + } 878 + 879 + drm_client_setup(drm, NULL); 880 + return st7571; 881 + } 882 + EXPORT_SYMBOL_GPL(st7571_probe); 883 + 884 + void st7571_remove(struct st7571_device *st7571) 885 + { 886 + drm_dev_unplug(&st7571->drm); 887 + } 888 + EXPORT_SYMBOL_GPL(st7571_remove); 889 + 890 + const struct st7571_panel_data st7567_config = { 891 + .init = st7567_lcd_init, 892 + .parse_dt = st7567_parse_dt, 893 + .constraints = { 894 + .min_nlines = 1, 895 + .max_nlines = 64, 896 + .min_ncols = 128, 897 + .max_ncols = 128, 898 + .support_grayscale = false, 899 + }, 900 + }; 901 + EXPORT_SYMBOL_NS_GPL(st7567_config, "DRM_ST7571"); 902 + 903 + const struct st7571_panel_data st7571_config = { 904 + .init = st7571_lcd_init, 905 + .parse_dt = st7571_parse_dt, 906 + .constraints = { 907 + .min_nlines = 1, 908 + .max_nlines = 128, 909 + .min_ncols = 128, 910 + .max_ncols = 128, 911 + .support_grayscale = true, 912 + }, 913 + }; 914 + EXPORT_SYMBOL_NS_GPL(st7571_config, "DRM_ST7571"); 915 + 916 + MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); 917 + MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller"); 918 + MODULE_LICENSE("GPL");
+91
drivers/gpu/drm/sitronix/st7571.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * Header file for: 4 + * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller 5 + * 6 + * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com> 7 + */ 8 + 9 + #ifndef __ST7571_H__ 10 + #define __ST7571_H__ 11 + 12 + #include <drm/drm_connector.h> 13 + #include <drm/drm_crtc.h> 14 + #include <drm/drm_drv.h> 15 + #include <drm/drm_encoder.h> 16 + #include <drm/drm_format_helper.h> 17 + 18 + #include <linux/regmap.h> 19 + 20 + enum st7571_color_mode { 21 + ST7571_COLOR_MODE_GRAY = 0, 22 + ST7571_COLOR_MODE_BLACKWHITE = 1, 23 + }; 24 + 25 + struct st7571_device; 26 + 27 + struct st7571_panel_constraints { 28 + u32 min_nlines; 29 + u32 max_nlines; 30 + u32 min_ncols; 31 + u32 max_ncols; 32 + bool support_grayscale; 33 + }; 34 + 35 + struct st7571_panel_data { 36 + int (*init)(struct st7571_device *st7571); 37 + int (*parse_dt)(struct st7571_device *st7571); 38 + struct st7571_panel_constraints constraints; 39 + }; 40 + 41 + struct st7571_panel_format { 42 + void (*prepare_buffer)(struct st7571_device *st7571, 43 + const struct iosys_map *vmap, 44 + struct drm_framebuffer *fb, 45 + struct drm_rect *rect, 46 + struct drm_format_conv_state *fmtcnv_state); 47 + int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect); 48 + enum st7571_color_mode mode; 49 + const u8 nformats; 50 + const u32 formats[]; 51 + }; 52 + 53 + struct st7571_device { 54 + struct drm_device drm; 55 + struct device *dev; 56 + 57 + struct drm_plane primary_plane; 58 + struct drm_crtc crtc; 59 + struct drm_encoder encoder; 60 + struct drm_connector connector; 61 + 62 + struct drm_display_mode mode; 63 + 64 + const struct st7571_panel_format *pformat; 65 + const struct st7571_panel_data *pdata; 66 + struct gpio_desc *reset; 67 + struct regmap *regmap; 68 + 69 + bool grayscale; 70 + bool inverted; 71 + u32 height_mm; 72 + u32 width_mm; 73 + u32 startline; 74 + u32 nlines; 75 + u32 ncols; 76 + u32 bpp; 77 + 78 + /* Intermediate buffer in LCD friendly format */ 79 + u8 *hwbuf; 80 + 81 + /* Row of (transformed) pixels ready to be written to the display */ 82 + u8 *row; 83 + }; 84 + 85 + extern const struct st7571_panel_data st7567_config; 86 + extern const struct st7571_panel_data st7571_config; 87 + 88 + struct st7571_device *st7571_probe(struct device *dev, struct regmap *regmap); 89 + void st7571_remove(struct st7571_device *st7571); 90 + 91 + #endif /* __ST7571_H__ */
+867
drivers/gpu/drm/sitronix/st7920.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * DRM driver for Sitronix ST7920 LCD displays 4 + * 5 + * Copyright 2025 Iker Pedrosa <ikerpedrosam@gmail.com> 6 + * 7 + */ 8 + 9 + #include <linux/bitrev.h> 10 + #include <linux/delay.h> 11 + #include <linux/gpio/consumer.h> 12 + #include <linux/module.h> 13 + #include <linux/regmap.h> 14 + #include <linux/spi/spi.h> 15 + 16 + #include <drm/clients/drm_client_setup.h> 17 + #include <drm/drm_atomic.h> 18 + #include <drm/drm_atomic_helper.h> 19 + #include <drm/drm_crtc_helper.h> 20 + #include <drm/drm_damage_helper.h> 21 + #include <drm/drm_drv.h> 22 + #include <drm/drm_fbdev_shmem.h> 23 + #include <drm/drm_framebuffer.h> 24 + #include <drm/drm_gem_atomic_helper.h> 25 + #include <drm/drm_gem_framebuffer_helper.h> 26 + #include <drm/drm_gem_shmem_helper.h> 27 + #include <drm/drm_plane.h> 28 + #include <drm/drm_print.h> 29 + #include <drm/drm_probe_helper.h> 30 + 31 + #define DRIVER_NAME "sitronix_st7920" 32 + #define DRIVER_DESC "DRM driver for Sitronix ST7920 LCD displays" 33 + #define DRIVER_MAJOR 1 34 + #define DRIVER_MINOR 0 35 + 36 + /* Display organization */ 37 + #define ST7920_PITCH 16 38 + #define ST7920_SCANLINES 64 39 + #define BYTES_IN_DISPLAY (ST7920_PITCH * ST7920_SCANLINES) 40 + #define BYTES_IN_SEGMENT 2 41 + #define PIXELS_PER_SEGMENT (BYTES_IN_SEGMENT * 8) 42 + #define ST7920_DEFAULT_WIDTH 128 43 + #define ST7920_DEFAULT_HEIGHT 64 44 + 45 + /* Sync sequence */ 46 + #define SYNC_BITS 0xF8 47 + #define RW_HIGH 0x04 48 + #define RS_HIGH 0x02 49 + 50 + /* Commands */ 51 + #define SET_DISPLAY_ON 0x0C 52 + #define SET_DISPLAY_OFF 0x08 53 + #define SET_DISPLAY_CLEAR 0x01 54 + #define SET_BASIC_INSTRUCTION_SET 0x30 55 + #define SET_EXT_INSTRUCTION_SET 0x34 56 + #define SET_GRAPHICS_DISPLAY 0x36 57 + #define SET_GDRAM_ADDRESS 0x80 58 + #define SET_GDRAM_DATA 0xFF /* Driver internal command */ 59 + 60 + /* Masks */ 61 + #define HIGH_DATA_MASK 0xF0 62 + #define LOW_DATA_MASK 0x0F 63 + #define TOP_VERTICAL_ADDRESS 0x80 64 + #define BOTTOM_VERTICAL_ADDRESS 0x60 65 + #define TOP_HORIZONTAL_ADDRESS 0x00 66 + #define BOTTOM_HORIZONTAL_ADDRESS 0x80 67 + 68 + #define CMD_SIZE 35 69 + 70 + struct spi7920_error { 71 + int errno; 72 + }; 73 + 74 + struct st7920_device { 75 + struct drm_device drm; 76 + struct drm_display_mode mode; 77 + struct drm_plane primary_plane; 78 + struct drm_crtc crtc; 79 + struct drm_encoder encoder; 80 + struct drm_connector connector; 81 + struct spi_device *spi; 82 + 83 + struct regmap *regmap; 84 + 85 + struct gpio_desc *reset_gpio; 86 + 87 + u32 height; 88 + u32 width; 89 + }; 90 + 91 + struct st7920_plane_state { 92 + struct drm_shadow_plane_state base; 93 + /* Intermediate buffer to convert pixels from XRGB8888 to HW format */ 94 + u8 *buffer; 95 + }; 96 + 97 + struct st7920_crtc_state { 98 + struct drm_crtc_state base; 99 + /* Buffer to store pixels in HW format and written to the panel */ 100 + u8 *data_array; 101 + }; 102 + 103 + static inline struct st7920_plane_state *to_st7920_plane_state(struct drm_plane_state *state) 104 + { 105 + return container_of(state, struct st7920_plane_state, base.base); 106 + } 107 + 108 + static inline struct st7920_crtc_state *to_st7920_crtc_state(struct drm_crtc_state *state) 109 + { 110 + return container_of(state, struct st7920_crtc_state, base); 111 + } 112 + 113 + static inline struct st7920_device *drm_to_st7920(struct drm_device *drm) 114 + { 115 + return container_of(drm, struct st7920_device, drm); 116 + } 117 + 118 + static int st7920_store_gdram_address(const void *data, u8 *reg) 119 + { 120 + const u8 y_addr = *(const u8 *)data; 121 + bool bottom_screen = (y_addr >= 32); 122 + int i = 0; 123 + 124 + reg[i++] = SYNC_BITS; 125 + /* Set vertical address */ 126 + if (!bottom_screen) 127 + reg[i++] = TOP_VERTICAL_ADDRESS + (*(uint8_t *)data & HIGH_DATA_MASK); 128 + else 129 + reg[i++] = BOTTOM_VERTICAL_ADDRESS + (*(uint8_t *)data & HIGH_DATA_MASK); 130 + 131 + reg[i++] = *(uint8_t *)data << 4; 132 + /* Set horizontal address */ 133 + reg[i++] = SET_GDRAM_ADDRESS; 134 + if (!bottom_screen) 135 + reg[i++] = TOP_HORIZONTAL_ADDRESS; 136 + else 137 + reg[i++] = BOTTOM_HORIZONTAL_ADDRESS; 138 + 139 + return i; 140 + } 141 + 142 + static int st7920_store_gdram_data(const void *data, u8 *reg) 143 + { 144 + const u8 *line_data = data; 145 + int i = 0, j = 0; 146 + 147 + reg[i++] = SYNC_BITS | RS_HIGH; 148 + 149 + for (j = 0; j < 16; j++) { 150 + reg[i++] = line_data[j] & 0xF0; 151 + reg[i++] = (line_data[j] << 4) & 0xF0; 152 + } 153 + 154 + return i; 155 + } 156 + 157 + static int st7920_store_others(int cmd, const void *data, u8 *reg) 158 + { 159 + int i = 0; 160 + 161 + reg[i++] = SYNC_BITS; 162 + reg[i++] = cmd & HIGH_DATA_MASK; 163 + reg[i++] = (cmd & LOW_DATA_MASK) << 4; 164 + 165 + return i; 166 + } 167 + 168 + static void st7920_spi_write(struct spi_device *spi, int cmd, const void *data, 169 + int delay_us, struct spi7920_error *err) 170 + { 171 + u8 reg[CMD_SIZE] = {0}; 172 + int size = 0; 173 + int ret; 174 + 175 + if (err->errno) 176 + return; 177 + 178 + /* 179 + * First the sync bits are sent: 11111WS0. 180 + * Where W is the read/write (RW) bit and S is the register/data (RS) bit. 181 + * Then, every 8 bits instruction/data will be separated into 2 groups. 182 + * Higher 4 bits (DB7~DB4) will be placed in the first section followed by 183 + * 4 '0's. And lower 4 bits (DB3~DB0) will be placed in the second section 184 + * followed by 4 '0's. 185 + */ 186 + if (cmd == SET_GDRAM_ADDRESS) 187 + size = st7920_store_gdram_address(data, reg); 188 + else if (cmd == SET_GDRAM_DATA) 189 + size = st7920_store_gdram_data(data, reg); 190 + else 191 + size = st7920_store_others(cmd, data, reg); 192 + 193 + ret = spi_write(spi, reg, size); 194 + if (ret) { 195 + err->errno = ret; 196 + return; 197 + } 198 + 199 + if (delay_us) 200 + udelay(delay_us); 201 + } 202 + 203 + static const struct regmap_config st7920_spi_regmap_config = { 204 + .reg_bits = 8, 205 + .val_bits = 8, 206 + }; 207 + 208 + static const struct of_device_id st7920_of_match[] = { 209 + /* st7920 family */ 210 + { 211 + .compatible = "sitronix,st7920", 212 + }, 213 + { /* sentinel */ } 214 + }; 215 + MODULE_DEVICE_TABLE(of, st7920_of_match); 216 + 217 + /* 218 + * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even 219 + * if the device was registered via OF. This means that the module will not be 220 + * auto loaded, unless it contains an alias that matches the MODALIAS reported. 221 + * 222 + * To workaround this issue, add a SPI device ID table. Even when this should 223 + * not be needed for this driver to match the registered SPI devices. 224 + */ 225 + static const struct spi_device_id st7920_spi_id[] = { 226 + /* st7920 family */ 227 + { "st7920", 0 }, 228 + { /* sentinel */ } 229 + }; 230 + MODULE_DEVICE_TABLE(spi, st7920_spi_id); 231 + 232 + static void st7920_power_on(struct st7920_device *st7920, 233 + struct spi7920_error *err) 234 + { 235 + st7920_spi_write(st7920->spi, SET_DISPLAY_ON, NULL, 72, err); 236 + } 237 + 238 + static void st7920_power_off(struct st7920_device *st7920, 239 + struct spi7920_error *err) 240 + { 241 + st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, err); 242 + st7920_spi_write(st7920->spi, SET_DISPLAY_OFF, NULL, 72, err); 243 + } 244 + 245 + static void st7920_hw_reset(struct st7920_device *st7920) 246 + { 247 + if (!st7920->reset_gpio) 248 + return; 249 + 250 + gpiod_set_value_cansleep(st7920->reset_gpio, 1); 251 + usleep_range(15, 20); 252 + gpiod_set_value_cansleep(st7920->reset_gpio, 0); 253 + msleep(40); 254 + } 255 + 256 + static int st7920_init(struct st7920_device *st7920) 257 + { 258 + struct spi7920_error err = {0}; 259 + 260 + st7920_spi_write(st7920->spi, SET_BASIC_INSTRUCTION_SET, NULL, 72, &err); 261 + st7920_power_on(st7920, &err); 262 + st7920_spi_write(st7920->spi, SET_GRAPHICS_DISPLAY, NULL, 72, &err); 263 + st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, &err); 264 + 265 + return err.errno; 266 + } 267 + 268 + static int st7920_update_rect(struct st7920_device *st7920, 269 + struct drm_rect *rect, u8 *buf, 270 + u8 *data_array) 271 + { 272 + struct spi7920_error err = {0}; 273 + u32 array_idx = 0; 274 + int i, j; 275 + 276 + /* 277 + * The screen is divided in 64(Y)x8(X) segments and each segment is 278 + * further divided in 2 bytes (D15~D0). 279 + * 280 + * Segment 0x0 is in the top-right corner, while segment 63x15 is in the 281 + * bottom-left. They would be displayed in the screen in the following way: 282 + * 0x0 0x1 0x2 ... 0x15 283 + * 1x0 1x1 1x2 ... 1x15 284 + * ... 285 + * 63x0 63x1 63x2 ... 63x15 286 + * 287 + * The data in each byte is big endian. 288 + */ 289 + 290 + for (i = 0; i < ST7920_SCANLINES; i++) { 291 + u8 *line_start = buf + (i * ST7920_PITCH); 292 + u8 line_buffer[ST7920_PITCH]; 293 + 294 + for (j = 0; j < ST7920_PITCH; j++) { 295 + line_buffer[j] = bitrev8(line_start[j]); 296 + data_array[array_idx++] = line_buffer[j]; 297 + } 298 + 299 + st7920_spi_write(st7920->spi, SET_GDRAM_ADDRESS, &i, 72, &err); 300 + st7920_spi_write(st7920->spi, SET_GDRAM_DATA, line_buffer, 72, &err); 301 + } 302 + 303 + return err.errno; 304 + } 305 + 306 + static void st7920_clear_screen(struct st7920_device *st7920, u8 *data_array) 307 + { 308 + struct spi7920_error err = {0}; 309 + 310 + memset(data_array, 0, BYTES_IN_DISPLAY); 311 + 312 + st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, &err); 313 + } 314 + 315 + static int st7920_fb_blit_rect(struct drm_framebuffer *fb, 316 + const struct iosys_map *vmap, 317 + struct drm_rect *rect, 318 + u8 *buf, u8 *data_array, 319 + struct drm_format_conv_state *fmtcnv_state) 320 + { 321 + struct st7920_device *st7920 = drm_to_st7920(fb->dev); 322 + struct iosys_map dst; 323 + unsigned int dst_pitch; 324 + int ret; 325 + 326 + /* Align y to display page boundaries */ 327 + rect->y1 = round_down(rect->y1, PIXELS_PER_SEGMENT); 328 + rect->y2 = min_t(unsigned int, round_up(rect->y2, PIXELS_PER_SEGMENT), st7920->height); 329 + 330 + dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); 331 + 332 + iosys_map_set_vaddr(&dst, buf); 333 + drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); 334 + 335 + ret = st7920_update_rect(st7920, rect, buf, data_array); 336 + 337 + return ret; 338 + } 339 + 340 + static int st7920_primary_plane_atomic_check(struct drm_plane *plane, 341 + struct drm_atomic_state *state) 342 + { 343 + struct drm_device *drm = plane->dev; 344 + struct st7920_device *st7920 = drm_to_st7920(drm); 345 + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 346 + struct st7920_plane_state *st7920_state = to_st7920_plane_state(plane_state); 347 + struct drm_shadow_plane_state *shadow_plane_state = &st7920_state->base; 348 + struct drm_crtc *crtc = plane_state->crtc; 349 + struct drm_crtc_state *crtc_state = NULL; 350 + const struct drm_format_info *fi; 351 + unsigned int pitch; 352 + int ret; 353 + 354 + if (crtc) 355 + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 356 + 357 + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, 358 + DRM_PLANE_NO_SCALING, 359 + DRM_PLANE_NO_SCALING, 360 + false, false); 361 + if (ret) 362 + return ret; 363 + else if (!plane_state->visible) 364 + return 0; 365 + 366 + fi = drm_format_info(DRM_FORMAT_R1); 367 + if (!fi) 368 + return -EINVAL; 369 + 370 + pitch = drm_format_info_min_pitch(fi, 0, st7920->width); 371 + 372 + if (plane_state->fb->format != fi) { 373 + void *buf; 374 + 375 + /* format conversion necessary; reserve buffer */ 376 + buf = drm_format_conv_state_reserve(&shadow_plane_state->fmtcnv_state, 377 + pitch, GFP_KERNEL); 378 + if (!buf) 379 + return -ENOMEM; 380 + } 381 + 382 + st7920_state->buffer = kcalloc(pitch, st7920->height, GFP_KERNEL); 383 + if (!st7920_state->buffer) 384 + return -ENOMEM; 385 + 386 + return 0; 387 + } 388 + 389 + static void st7920_primary_plane_atomic_update(struct drm_plane *plane, 390 + struct drm_atomic_state *state) 391 + { 392 + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 393 + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 394 + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 395 + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); 396 + struct st7920_crtc_state *st7920_crtc_state = to_st7920_crtc_state(crtc_state); 397 + struct st7920_plane_state *st7920_plane_state = to_st7920_plane_state(plane_state); 398 + struct drm_framebuffer *fb = plane_state->fb; 399 + struct drm_atomic_helper_damage_iter iter; 400 + struct drm_device *drm = plane->dev; 401 + struct drm_rect dst_clip; 402 + struct drm_rect damage; 403 + int idx; 404 + int ret; 405 + 406 + if (!drm_dev_enter(drm, &idx)) 407 + return; 408 + 409 + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) { 410 + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 411 + drm_atomic_for_each_plane_damage(&iter, &damage) { 412 + dst_clip = plane_state->dst; 413 + 414 + if (!drm_rect_intersect(&dst_clip, &damage)) 415 + continue; 416 + 417 + ret = st7920_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip, 418 + st7920_plane_state->buffer, 419 + st7920_crtc_state->data_array, 420 + &shadow_plane_state->fmtcnv_state); 421 + if (ret) 422 + drm_err_once(plane->dev, "Failed to write to device: %d.\n", ret); 423 + } 424 + 425 + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 426 + } 427 + 428 + drm_dev_exit(idx); 429 + } 430 + 431 + static void st7920_primary_plane_atomic_disable(struct drm_plane *plane, 432 + struct drm_atomic_state *state) 433 + { 434 + struct drm_device *drm = plane->dev; 435 + struct st7920_device *st7920 = drm_to_st7920(drm); 436 + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 437 + struct drm_crtc_state *crtc_state; 438 + struct st7920_crtc_state *st7920_crtc_state; 439 + int idx; 440 + 441 + if (!plane_state->crtc) 442 + return; 443 + 444 + crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); 445 + st7920_crtc_state = to_st7920_crtc_state(crtc_state); 446 + 447 + if (!drm_dev_enter(drm, &idx)) 448 + return; 449 + 450 + st7920_clear_screen(st7920, st7920_crtc_state->data_array); 451 + 452 + drm_dev_exit(idx); 453 + } 454 + 455 + /* Called during init to allocate the plane's atomic state. */ 456 + static void st7920_primary_plane_reset(struct drm_plane *plane) 457 + { 458 + struct st7920_plane_state *st7920_state; 459 + 460 + drm_WARN_ON_ONCE(plane->dev, plane->state); 461 + 462 + st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL); 463 + if (!st7920_state) 464 + return; 465 + 466 + __drm_gem_reset_shadow_plane(plane, &st7920_state->base); 467 + } 468 + 469 + static struct drm_plane_state *st7920_primary_plane_duplicate_state(struct drm_plane *plane) 470 + { 471 + struct drm_shadow_plane_state *new_shadow_plane_state; 472 + struct st7920_plane_state *st7920_state; 473 + 474 + if (drm_WARN_ON_ONCE(plane->dev, !plane->state)) 475 + return NULL; 476 + 477 + st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL); 478 + if (!st7920_state) 479 + return NULL; 480 + 481 + new_shadow_plane_state = &st7920_state->base; 482 + 483 + __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); 484 + 485 + return &new_shadow_plane_state->base; 486 + } 487 + 488 + static void st7920_primary_plane_destroy_state(struct drm_plane *plane, 489 + struct drm_plane_state *state) 490 + { 491 + struct st7920_plane_state *st7920_state = to_st7920_plane_state(state); 492 + 493 + kfree(st7920_state->buffer); 494 + 495 + __drm_gem_destroy_shadow_plane_state(&st7920_state->base); 496 + 497 + kfree(st7920_state); 498 + } 499 + 500 + static const struct drm_plane_helper_funcs st7920_primary_plane_helper_funcs = { 501 + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 502 + .atomic_check = st7920_primary_plane_atomic_check, 503 + .atomic_update = st7920_primary_plane_atomic_update, 504 + .atomic_disable = st7920_primary_plane_atomic_disable, 505 + }; 506 + 507 + static const struct drm_plane_funcs st7920_primary_plane_funcs = { 508 + .update_plane = drm_atomic_helper_update_plane, 509 + .disable_plane = drm_atomic_helper_disable_plane, 510 + .reset = st7920_primary_plane_reset, 511 + .atomic_duplicate_state = st7920_primary_plane_duplicate_state, 512 + .atomic_destroy_state = st7920_primary_plane_destroy_state, 513 + .destroy = drm_plane_cleanup, 514 + }; 515 + 516 + static enum drm_mode_status st7920_crtc_mode_valid(struct drm_crtc *crtc, 517 + const struct drm_display_mode *mode) 518 + { 519 + struct st7920_device *st7920 = drm_to_st7920(crtc->dev); 520 + 521 + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7920->mode); 522 + } 523 + 524 + static int st7920_crtc_atomic_check(struct drm_crtc *crtc, 525 + struct drm_atomic_state *state) 526 + { 527 + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 528 + struct st7920_crtc_state *st7920_state = to_st7920_crtc_state(crtc_state); 529 + int ret; 530 + 531 + ret = drm_crtc_helper_atomic_check(crtc, state); 532 + if (ret) 533 + return ret; 534 + 535 + st7920_state->data_array = kmalloc(BYTES_IN_DISPLAY, GFP_KERNEL); 536 + if (!st7920_state->data_array) 537 + return -ENOMEM; 538 + 539 + return 0; 540 + } 541 + 542 + static void st7920_crtc_atomic_enable(struct drm_crtc *crtc, 543 + struct drm_atomic_state *state) 544 + { 545 + struct drm_device *drm = crtc->dev; 546 + struct st7920_device *st7920 = drm_to_st7920(drm); 547 + int idx; 548 + int ret; 549 + 550 + if (!drm_dev_enter(drm, &idx)) 551 + return; 552 + 553 + st7920_hw_reset(st7920); 554 + 555 + ret = st7920_init(st7920); 556 + if (ret) 557 + drm_err(drm, "Failed to init hardware: %d\n", ret); 558 + 559 + drm_dev_exit(idx); 560 + } 561 + 562 + static void st7920_crtc_atomic_disable(struct drm_crtc *crtc, 563 + struct drm_atomic_state *state) 564 + { 565 + struct spi7920_error err = {0}; 566 + struct drm_device *drm = crtc->dev; 567 + struct st7920_device *st7920 = drm_to_st7920(drm); 568 + int idx; 569 + 570 + drm_dev_enter(drm, &idx); 571 + 572 + st7920_power_off(st7920, &err); 573 + 574 + drm_dev_exit(idx); 575 + } 576 + 577 + /* Called during init to allocate the CRTC's atomic state. */ 578 + static void st7920_crtc_reset(struct drm_crtc *crtc) 579 + { 580 + struct st7920_crtc_state *st7920_state; 581 + 582 + drm_WARN_ON_ONCE(crtc->dev, crtc->state); 583 + 584 + st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL); 585 + if (!st7920_state) 586 + return; 587 + 588 + __drm_atomic_helper_crtc_reset(crtc, &st7920_state->base); 589 + } 590 + 591 + static struct drm_crtc_state *st7920_crtc_duplicate_state(struct drm_crtc *crtc) 592 + { 593 + struct st7920_crtc_state *st7920_state; 594 + 595 + if (drm_WARN_ON_ONCE(crtc->dev, !crtc->state)) 596 + return NULL; 597 + 598 + st7920_state = kzalloc(sizeof(*st7920_state), GFP_KERNEL); 599 + if (!st7920_state) 600 + return NULL; 601 + 602 + __drm_atomic_helper_crtc_duplicate_state(crtc, &st7920_state->base); 603 + 604 + return &st7920_state->base; 605 + } 606 + 607 + static void st7920_crtc_destroy_state(struct drm_crtc *crtc, 608 + struct drm_crtc_state *state) 609 + { 610 + struct st7920_crtc_state *st7920_state = to_st7920_crtc_state(state); 611 + 612 + kfree(st7920_state->data_array); 613 + 614 + __drm_atomic_helper_crtc_destroy_state(state); 615 + 616 + kfree(st7920_state); 617 + } 618 + 619 + /* 620 + * The CRTC is always enabled. Screen updates are performed by 621 + * the primary plane's atomic_update function. Disabling clears 622 + * the screen in the primary plane's atomic_disable function. 623 + */ 624 + static const struct drm_crtc_helper_funcs st7920_crtc_helper_funcs = { 625 + .mode_valid = st7920_crtc_mode_valid, 626 + .atomic_check = st7920_crtc_atomic_check, 627 + .atomic_enable = st7920_crtc_atomic_enable, 628 + .atomic_disable = st7920_crtc_atomic_disable, 629 + }; 630 + 631 + static const struct drm_crtc_funcs st7920_crtc_funcs = { 632 + .reset = st7920_crtc_reset, 633 + .destroy = drm_crtc_cleanup, 634 + .set_config = drm_atomic_helper_set_config, 635 + .page_flip = drm_atomic_helper_page_flip, 636 + .atomic_duplicate_state = st7920_crtc_duplicate_state, 637 + .atomic_destroy_state = st7920_crtc_destroy_state, 638 + }; 639 + 640 + static const struct drm_encoder_funcs st7920_encoder_funcs = { 641 + .destroy = drm_encoder_cleanup, 642 + }; 643 + 644 + static int st7920_connector_get_modes(struct drm_connector *connector) 645 + { 646 + struct st7920_device *st7920 = drm_to_st7920(connector->dev); 647 + 648 + return drm_connector_helper_get_modes_fixed(connector, &st7920->mode); 649 + } 650 + 651 + static const struct drm_connector_helper_funcs st7920_connector_helper_funcs = { 652 + .get_modes = st7920_connector_get_modes, 653 + }; 654 + 655 + static const struct drm_connector_funcs st7920_connector_funcs = { 656 + .reset = drm_atomic_helper_connector_reset, 657 + .fill_modes = drm_helper_probe_single_connector_modes, 658 + .destroy = drm_connector_cleanup, 659 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 660 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 661 + }; 662 + 663 + static const struct drm_mode_config_funcs st7920_mode_config_funcs = { 664 + .fb_create = drm_gem_fb_create_with_dirty, 665 + .atomic_check = drm_atomic_helper_check, 666 + .atomic_commit = drm_atomic_helper_commit, 667 + }; 668 + 669 + static const uint32_t st7920_formats[] = { 670 + DRM_FORMAT_XRGB8888, 671 + }; 672 + 673 + DEFINE_DRM_GEM_FOPS(st7920_fops); 674 + 675 + static const struct drm_driver st7920_drm_driver = { 676 + DRM_GEM_SHMEM_DRIVER_OPS, 677 + DRM_FBDEV_SHMEM_DRIVER_OPS, 678 + .name = DRIVER_NAME, 679 + .desc = DRIVER_DESC, 680 + .major = DRIVER_MAJOR, 681 + .minor = DRIVER_MINOR, 682 + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, 683 + .fops = &st7920_fops, 684 + }; 685 + 686 + static int st7920_init_modeset(struct st7920_device *st7920) 687 + { 688 + struct drm_display_mode *mode = &st7920->mode; 689 + struct drm_device *drm = &st7920->drm; 690 + unsigned long max_width, max_height; 691 + struct drm_plane *primary_plane; 692 + struct drm_crtc *crtc; 693 + struct drm_encoder *encoder; 694 + struct drm_connector *connector; 695 + int ret; 696 + 697 + /* 698 + * Modesetting 699 + */ 700 + 701 + ret = drmm_mode_config_init(drm); 702 + if (ret) { 703 + drm_err(drm, "DRM mode config init failed: %d\n", ret); 704 + return ret; 705 + } 706 + 707 + mode->type = DRM_MODE_TYPE_DRIVER; 708 + mode->clock = 30; 709 + mode->hdisplay = st7920->width; 710 + mode->htotal = st7920->width; 711 + mode->hsync_start = st7920->width; 712 + mode->hsync_end = st7920->width; 713 + mode->vdisplay = st7920->height; 714 + mode->vtotal = st7920->height; 715 + mode->vsync_start = st7920->height; 716 + mode->vsync_end = st7920->height; 717 + mode->width_mm = 27; 718 + mode->height_mm = 27; 719 + 720 + max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH); 721 + max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT); 722 + 723 + drm->mode_config.min_width = mode->hdisplay; 724 + drm->mode_config.max_width = max_width; 725 + drm->mode_config.min_height = mode->vdisplay; 726 + drm->mode_config.max_height = max_height; 727 + drm->mode_config.preferred_depth = 24; 728 + drm->mode_config.funcs = &st7920_mode_config_funcs; 729 + 730 + /* Primary plane */ 731 + 732 + primary_plane = &st7920->primary_plane; 733 + ret = drm_universal_plane_init(drm, primary_plane, 0, &st7920_primary_plane_funcs, 734 + st7920_formats, ARRAY_SIZE(st7920_formats), 735 + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); 736 + if (ret) { 737 + drm_err(drm, "DRM primary plane init failed: %d\n", ret); 738 + return ret; 739 + } 740 + 741 + drm_plane_helper_add(primary_plane, &st7920_primary_plane_helper_funcs); 742 + 743 + drm_plane_enable_fb_damage_clips(primary_plane); 744 + 745 + /* CRTC */ 746 + 747 + crtc = &st7920->crtc; 748 + ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, 749 + &st7920_crtc_funcs, NULL); 750 + if (ret) { 751 + drm_err(drm, "DRM crtc init failed: %d\n", ret); 752 + return ret; 753 + } 754 + 755 + drm_crtc_helper_add(crtc, &st7920_crtc_helper_funcs); 756 + 757 + /* Encoder */ 758 + 759 + encoder = &st7920->encoder; 760 + ret = drm_encoder_init(drm, encoder, &st7920_encoder_funcs, 761 + DRM_MODE_ENCODER_NONE, NULL); 762 + if (ret) { 763 + drm_err(drm, "DRM encoder init failed: %d\n", ret); 764 + return ret; 765 + } 766 + 767 + encoder->possible_crtcs = drm_crtc_mask(crtc); 768 + 769 + /* Connector */ 770 + 771 + connector = &st7920->connector; 772 + ret = drm_connector_init(drm, connector, &st7920_connector_funcs, 773 + DRM_MODE_CONNECTOR_Unknown); 774 + if (ret) { 775 + drm_err(drm, "DRM connector init failed: %d\n", ret); 776 + return ret; 777 + } 778 + 779 + drm_connector_helper_add(connector, &st7920_connector_helper_funcs); 780 + 781 + ret = drm_connector_attach_encoder(connector, encoder); 782 + if (ret) { 783 + drm_err(drm, "DRM attach connector to encoder failed: %d\n", ret); 784 + return ret; 785 + } 786 + 787 + drm_mode_config_reset(drm); 788 + 789 + return 0; 790 + } 791 + 792 + static int st7920_probe(struct spi_device *spi) 793 + { 794 + struct st7920_device *st7920; 795 + struct regmap *regmap; 796 + struct device *dev = &spi->dev; 797 + struct drm_device *drm; 798 + int ret; 799 + 800 + regmap = devm_regmap_init_spi(spi, &st7920_spi_regmap_config); 801 + if (IS_ERR(regmap)) 802 + return PTR_ERR(regmap); 803 + 804 + st7920 = devm_drm_dev_alloc(dev, &st7920_drm_driver, 805 + struct st7920_device, drm); 806 + if (IS_ERR(st7920)) 807 + return PTR_ERR(st7920); 808 + 809 + drm = &st7920->drm; 810 + 811 + st7920->drm.dev = dev; 812 + st7920->regmap = regmap; 813 + st7920->spi = spi; 814 + st7920->width = ST7920_DEFAULT_WIDTH; 815 + st7920->height = ST7920_DEFAULT_HEIGHT; 816 + 817 + st7920->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 818 + if (IS_ERR(st7920->reset_gpio)) { 819 + ret = PTR_ERR(st7920->reset_gpio); 820 + return dev_err_probe(dev, ret, "Unable to retrieve reset GPIO\n"); 821 + } 822 + 823 + spi_set_drvdata(spi, st7920); 824 + 825 + ret = st7920_init_modeset(st7920); 826 + if (ret) 827 + return ret; 828 + 829 + ret = drm_dev_register(drm, 0); 830 + if (ret) 831 + return dev_err_probe(dev, ret, "DRM device register failed\n"); 832 + 833 + drm_client_setup(drm, NULL); 834 + 835 + return 0; 836 + } 837 + 838 + static void st7920_remove(struct spi_device *spi) 839 + { 840 + struct st7920_device *st7920 = spi_get_drvdata(spi); 841 + 842 + drm_dev_unplug(&st7920->drm); 843 + drm_atomic_helper_shutdown(&st7920->drm); 844 + } 845 + 846 + static void st7920_shutdown(struct spi_device *spi) 847 + { 848 + struct st7920_device *st7920 = spi_get_drvdata(spi); 849 + 850 + drm_atomic_helper_shutdown(&st7920->drm); 851 + } 852 + 853 + static struct spi_driver st7920_spi_driver = { 854 + .driver = { 855 + .name = DRIVER_NAME, 856 + .of_match_table = st7920_of_match, 857 + }, 858 + .id_table = st7920_spi_id, 859 + .probe = st7920_probe, 860 + .remove = st7920_remove, 861 + .shutdown = st7920_shutdown, 862 + }; 863 + module_spi_driver(st7920_spi_driver); 864 + 865 + MODULE_DESCRIPTION(DRIVER_DESC); 866 + MODULE_AUTHOR("Iker Pedrosa <ipedrosam@gmail.com>"); 867 + MODULE_LICENSE("GPL");
+3 -4
include/drm/drm_file.h
··· 33 33 #include <linux/types.h> 34 34 #include <linux/completion.h> 35 35 #include <linux/idr.h> 36 + #include <linux/xarray.h> 36 37 37 38 #include <uapi/drm/drm.h> 38 39 ··· 317 316 /** @table_lock: Protects @object_idr. */ 318 317 spinlock_t table_lock; 319 318 320 - /** @syncobj_idr: Mapping of sync object handles to object pointers. */ 321 - struct idr syncobj_idr; 322 - /** @syncobj_table_lock: Protects @syncobj_idr. */ 323 - spinlock_t syncobj_table_lock; 319 + /** @syncobj_xa: Mapping of sync object handles to object pointers. */ 320 + struct xarray syncobj_xa; 324 321 325 322 /** @filp: Pointer to the core file structure. */ 326 323 struct file *filp;
+2 -2
include/drm/drm_gem.h
··· 508 508 /** 509 509 * drm_gem_get_huge_mnt - Get the huge tmpfs mountpoint used by a DRM device 510 510 * @dev: DRM device 511 - 511 + * 512 512 * This function gets the huge tmpfs mountpoint used by DRM device @dev. A huge 513 513 * tmpfs mountpoint is used instead of `shm_mnt` after a successful call to 514 514 * drm_gem_huge_mnt_create() when CONFIG_TRANSPARENT_HUGEPAGE is enabled. 515 - 515 + * 516 516 * Returns: 517 517 * The huge tmpfs mountpoint in use, NULL otherwise. 518 518 */
+52
include/drm/gpu_scheduler.h
··· 645 645 void drm_sched_start(struct drm_gpu_scheduler *sched, int errno); 646 646 void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched); 647 647 void drm_sched_fault(struct drm_gpu_scheduler *sched); 648 + bool drm_sched_is_stopped(struct drm_gpu_scheduler *sched); 648 649 649 650 struct drm_gpu_scheduler * 650 651 drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, ··· 675 674 struct dma_fence *fence); 676 675 void drm_sched_job_cleanup(struct drm_sched_job *job); 677 676 void drm_sched_increase_karma(struct drm_sched_job *bad); 677 + bool drm_sched_job_is_signaled(struct drm_sched_job *job); 678 678 679 679 static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, 680 680 int threshold) ··· 699 697 void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, 700 698 struct drm_gpu_scheduler **sched_list, 701 699 unsigned int num_sched_list); 700 + 701 + /** 702 + * struct drm_sched_pending_job_iter - DRM scheduler pending job iterator state 703 + * @sched: DRM scheduler associated with pending job iterator 704 + */ 705 + struct drm_sched_pending_job_iter { 706 + struct drm_gpu_scheduler *sched; 707 + }; 708 + 709 + /* Drivers should never call this directly */ 710 + static inline struct drm_sched_pending_job_iter 711 + __drm_sched_pending_job_iter_begin(struct drm_gpu_scheduler *sched) 712 + { 713 + struct drm_sched_pending_job_iter iter = { 714 + .sched = sched, 715 + }; 716 + 717 + WARN_ON(!drm_sched_is_stopped(sched)); 718 + return iter; 719 + } 720 + 721 + /* Drivers should never call this directly */ 722 + static inline void 723 + __drm_sched_pending_job_iter_end(const struct drm_sched_pending_job_iter iter) 724 + { 725 + WARN_ON(!drm_sched_is_stopped(iter.sched)); 726 + } 727 + 728 + DEFINE_CLASS(drm_sched_pending_job_iter, struct drm_sched_pending_job_iter, 729 + __drm_sched_pending_job_iter_end(_T), 730 + __drm_sched_pending_job_iter_begin(__sched), 731 + struct drm_gpu_scheduler *__sched); 732 + static inline void * 733 + class_drm_sched_pending_job_iter_lock_ptr(class_drm_sched_pending_job_iter_t *_T) 734 + { return _T; } 735 + #define class_drm_sched_pending_job_iter_is_conditional false 736 + 737 + /** 738 + * drm_sched_for_each_pending_job() - Iterator for each pending job in scheduler 739 + * @__job: Current pending job being iterated over 740 + * @__sched: DRM scheduler to iterate over pending jobs 741 + * @__entity: DRM scheduler entity to filter jobs, NULL indicates no filter 742 + * 743 + * Iterator for each pending job in scheduler, filtering on an entity, and 744 + * enforcing scheduler is fully stopped 745 + */ 746 + #define drm_sched_for_each_pending_job(__job, __sched, __entity) \ 747 + scoped_guard(drm_sched_pending_job_iter, (__sched)) \ 748 + list_for_each_entry((__job), &(__sched)->pending_list, list) \ 749 + for_each_if(!(__entity) || (__job)->entity == (__entity)) 702 750 703 751 #endif
+157
include/trace/events/dma_buf.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #undef TRACE_SYSTEM 3 + #define TRACE_SYSTEM dma_buf 4 + 5 + #if !defined(_TRACE_DMA_BUF_H) || defined(TRACE_HEADER_MULTI_READ) 6 + #define _TRACE_DMA_BUF_H 7 + 8 + #include <linux/dma-buf.h> 9 + #include <linux/tracepoint.h> 10 + 11 + DECLARE_EVENT_CLASS(dma_buf, 12 + 13 + TP_PROTO(struct dma_buf *dmabuf), 14 + 15 + TP_ARGS(dmabuf), 16 + 17 + TP_STRUCT__entry( 18 + __string(exp_name, dmabuf->exp_name) 19 + __field(size_t, size) 20 + __field(ino_t, ino) 21 + ), 22 + 23 + TP_fast_assign( 24 + __assign_str(exp_name); 25 + __entry->size = dmabuf->size; 26 + __entry->ino = dmabuf->file->f_inode->i_ino; 27 + ), 28 + 29 + TP_printk("exp_name=%s size=%zu ino=%lu", 30 + __get_str(exp_name), 31 + __entry->size, 32 + __entry->ino) 33 + ); 34 + 35 + DECLARE_EVENT_CLASS(dma_buf_attach_dev, 36 + 37 + TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach, 38 + bool is_dynamic, struct device *dev), 39 + 40 + TP_ARGS(dmabuf, attach, is_dynamic, dev), 41 + 42 + TP_STRUCT__entry( 43 + __string(dev_name, dev_name(dev)) 44 + __string(exp_name, dmabuf->exp_name) 45 + __field(size_t, size) 46 + __field(ino_t, ino) 47 + __field(struct dma_buf_attachment *, attach) 48 + __field(bool, is_dynamic) 49 + ), 50 + 51 + TP_fast_assign( 52 + __assign_str(dev_name); 53 + __assign_str(exp_name); 54 + __entry->size = dmabuf->size; 55 + __entry->ino = dmabuf->file->f_inode->i_ino; 56 + __entry->is_dynamic = is_dynamic; 57 + __entry->attach = attach; 58 + ), 59 + 60 + TP_printk("exp_name=%s size=%zu ino=%lu attachment:%p is_dynamic=%d dev_name=%s", 61 + __get_str(exp_name), 62 + __entry->size, 63 + __entry->ino, 64 + __entry->attach, 65 + __entry->is_dynamic, 66 + __get_str(dev_name)) 67 + ); 68 + 69 + DECLARE_EVENT_CLASS(dma_buf_fd, 70 + 71 + TP_PROTO(struct dma_buf *dmabuf, int fd), 72 + 73 + TP_ARGS(dmabuf, fd), 74 + 75 + TP_STRUCT__entry( 76 + __string(exp_name, dmabuf->exp_name) 77 + __field(size_t, size) 78 + __field(ino_t, ino) 79 + __field(int, fd) 80 + ), 81 + 82 + TP_fast_assign( 83 + __assign_str(exp_name); 84 + __entry->size = dmabuf->size; 85 + __entry->ino = dmabuf->file->f_inode->i_ino; 86 + __entry->fd = fd; 87 + ), 88 + 89 + TP_printk("exp_name=%s size=%zu ino=%lu fd=%d", 90 + __get_str(exp_name), 91 + __entry->size, 92 + __entry->ino, 93 + __entry->fd) 94 + ); 95 + 96 + DEFINE_EVENT(dma_buf, dma_buf_export, 97 + 98 + TP_PROTO(struct dma_buf *dmabuf), 99 + 100 + TP_ARGS(dmabuf) 101 + ); 102 + 103 + DEFINE_EVENT(dma_buf, dma_buf_mmap_internal, 104 + 105 + TP_PROTO(struct dma_buf *dmabuf), 106 + 107 + TP_ARGS(dmabuf) 108 + ); 109 + 110 + DEFINE_EVENT(dma_buf, dma_buf_mmap, 111 + 112 + TP_PROTO(struct dma_buf *dmabuf), 113 + 114 + TP_ARGS(dmabuf) 115 + ); 116 + 117 + DEFINE_EVENT(dma_buf, dma_buf_put, 118 + 119 + TP_PROTO(struct dma_buf *dmabuf), 120 + 121 + TP_ARGS(dmabuf) 122 + ); 123 + 124 + DEFINE_EVENT(dma_buf_attach_dev, dma_buf_dynamic_attach, 125 + 126 + TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach, 127 + bool is_dynamic, struct device *dev), 128 + 129 + TP_ARGS(dmabuf, attach, is_dynamic, dev) 130 + ); 131 + 132 + DEFINE_EVENT(dma_buf_attach_dev, dma_buf_detach, 133 + 134 + TP_PROTO(struct dma_buf *dmabuf, struct dma_buf_attachment *attach, 135 + bool is_dynamic, struct device *dev), 136 + 137 + TP_ARGS(dmabuf, attach, is_dynamic, dev) 138 + ); 139 + 140 + DEFINE_EVENT(dma_buf_fd, dma_buf_fd, 141 + 142 + TP_PROTO(struct dma_buf *dmabuf, int fd), 143 + 144 + TP_ARGS(dmabuf, fd) 145 + ); 146 + 147 + DEFINE_EVENT(dma_buf_fd, dma_buf_get, 148 + 149 + TP_PROTO(struct dma_buf *dmabuf, int fd), 150 + 151 + TP_ARGS(dmabuf, fd) 152 + ); 153 + 154 + #endif /* _TRACE_DMA_BUF_H */ 155 + 156 + /* This part must be outside protection */ 157 + #include <trace/define_trace.h>
+8
include/uapi/drm/amdxdna_accel.h
··· 19 19 #define AMDXDNA_INVALID_BO_HANDLE 0 20 20 #define AMDXDNA_INVALID_FENCE_HANDLE 0 21 21 22 + /* 23 + * Define hardware context priority 24 + */ 25 + #define AMDXDNA_QOS_REALTIME_PRIORITY 0x100 26 + #define AMDXDNA_QOS_HIGH_PRIORITY 0x180 27 + #define AMDXDNA_QOS_NORMAL_PRIORITY 0x200 28 + #define AMDXDNA_QOS_LOW_PRIORITY 0x280 29 + 22 30 enum amdxdna_device_type { 23 31 AMDXDNA_DEV_TYPE_UNKNOWN = -1, 24 32 AMDXDNA_DEV_TYPE_KMQ,
+1 -1
include/uapi/drm/panthor_drm.h
··· 350 350 __u32 as_present; 351 351 352 352 /** 353 - * @select_coherency: Coherency selected for this device. 353 + * @selected_coherency: Coherency selected for this device. 354 354 * 355 355 * One of drm_panthor_gpu_coherency. 356 356 */