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

Configure Feed

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

Merge tag 'drm-misc-next-2026-03-20' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next

drm-misc-next for v7.1:

UAPI Changes:

math:
- provide __KERNEL_DIV_ROUND_CLOSEST() in UAPI

mode:
- provide DRM_ARGB_GET*() macros for reading color components

Cross-subsystem Changes:

math:
- implement DIV_ROUND_CLOSEST() with __KERNEL_DIV_ROUND_CLOSEST()

Core Changes:

atomic:
- fix handling of colorop state in atomic updates
- provide CRTC background color

ttm:
- improve tests and doumentation

Driver Changes:

amdxdna:
- allow forcing DMA through IOMMU IOVA
- improve debugging

bridge:
- Support Lontium LT8713SX DP MST bridge plus DT bindings

imx:
- support planes behind the primary plane
- fix bus-format selection

ivpu:
- perform engine reset on TDR error

panel:
- novatek-nt36672a: Use mipi_dsi_*_multi() functions
- panel-edp: Support BOE NV153WUM-N42, CMN N153JCA-ELK, CSW MNF307QS3-2

renesas:
- rz-du: clean up

rockchip:
- support CRTC background color

sun4i:
- fix leak in init code
- clean up

tildc
- clean up

v3d:
- improve handling of struct v3d_stats
- improve error handling
- clean up

vkms:
- support CRTC background color

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

From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patch.msgid.link/20260320082604.GA17867@linux.fritz.box

+1872 -775
+113
Documentation/devicetree/bindings/display/bridge/lontium,lt8713sx.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/bridge/lontium,lt8713sx.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Lontium LT8713SX Type-C/DP1.4 to Type-C/DP1.4/HDMI2.0/DP++ bridge-hub 8 + 9 + maintainers: 10 + - Vishnu Saini <vishnu.saini@oss.qualcomm.com> 11 + 12 + description: 13 + The Lontium LT8713SX is a Type-C/DP1.4 to Type-C/DP1.4/HDMI2.0 converter 14 + that integrates one DP input and up to three configurable output interfaces 15 + (DP1.4 / HDMI2.0 / DP++), with SST/MST functionality and audio support. 16 + 17 + properties: 18 + compatible: 19 + enum: 20 + - lontium,lt8713sx 21 + 22 + reg: 23 + maxItems: 1 24 + 25 + vcc-supply: 26 + description: Regulator for 3.3V vcc. 27 + 28 + vdd-supply: 29 + description: Regulator for 1.1V vdd. 30 + 31 + reset-gpios: 32 + description: GPIO connected to active low RESET pin. 33 + 34 + ports: 35 + $ref: /schemas/graph.yaml#/properties/ports 36 + 37 + properties: 38 + port@0: 39 + $ref: /schemas/graph.yaml#/properties/port 40 + description: 41 + DP port for DP input from soc to bridge chip 42 + 43 + port@1: 44 + $ref: /schemas/graph.yaml#/properties/port 45 + description: 46 + DP port for DP output from bridge 47 + 48 + port@2: 49 + $ref: /schemas/graph.yaml#/properties/port 50 + description: 51 + Additional DP port for DP output from bridge 52 + 53 + port@3: 54 + $ref: /schemas/graph.yaml#/properties/port 55 + description: 56 + Additional DP port for DP output from bridge 57 + 58 + required: 59 + - port@0 60 + 61 + required: 62 + - compatible 63 + - reg 64 + - ports 65 + 66 + additionalProperties: false 67 + 68 + examples: 69 + - | 70 + #include <dt-bindings/gpio/gpio.h> 71 + 72 + i2c { 73 + #address-cells = <1>; 74 + #size-cells = <0>; 75 + bridge@4f { 76 + compatible = "lontium,lt8713sx"; 77 + reg = <0x4f>; 78 + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; 79 + 80 + ports { 81 + #address-cells = <1>; 82 + #size-cells = <0>; 83 + 84 + port@0 { 85 + reg = <0>; 86 + lt8713sx_dp_in: endpoint { 87 + remote-endpoint = <&mdss_dp0_out>; 88 + }; 89 + }; 90 + 91 + port@1 { 92 + reg = <1>; 93 + lt8713sx_dp0_out: endpoint { 94 + remote-endpoint = <&dp0_connector_in>; 95 + }; 96 + }; 97 + 98 + port@2 { 99 + reg = <2>; 100 + lt8713sx_dp1_out: endpoint { 101 + remote-endpoint = <&dp1_connector_in>; 102 + }; 103 + }; 104 + 105 + port@3 { 106 + reg = <3>; 107 + lt8713sx_dp2_out: endpoint { 108 + remote-endpoint = <&dp2_connector_in>; 109 + }; 110 + }; 111 + }; 112 + }; 113 + };
+1
drivers/accel/amdxdna/Makefile
··· 11 11 aie2_solver.o \ 12 12 amdxdna_ctx.o \ 13 13 amdxdna_gem.o \ 14 + amdxdna_iommu.o \ 14 15 amdxdna_mailbox.o \ 15 16 amdxdna_mailbox_helper.o \ 16 17 amdxdna_pci_drv.o \
+81 -10
drivers/accel/amdxdna/aie2_ctx.c
··· 29 29 30 30 #define HWCTX_MAX_TIMEOUT 60000 /* milliseconds */ 31 31 32 + struct aie2_ctx_health { 33 + struct amdxdna_ctx_health header; 34 + u32 txn_op_idx; 35 + u32 ctx_pc; 36 + u32 fatal_error_type; 37 + u32 fatal_error_exception_type; 38 + u32 fatal_error_exception_pc; 39 + u32 fatal_error_app_module; 40 + }; 41 + 32 42 static void aie2_job_release(struct kref *ref) 33 43 { 34 44 struct amdxdna_sched_job *job; ··· 49 39 wake_up(&job->hwctx->priv->job_free_wq); 50 40 if (job->out_fence) 51 41 dma_fence_put(job->out_fence); 42 + kfree(job->aie2_job_health); 52 43 kfree(job); 53 44 } 54 45 ··· 79 68 } 80 69 81 70 ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, 82 - heap->mem.userptr, heap->mem.size); 71 + amdxdna_obj_dma_addr(hwctx->client, heap), 72 + heap->mem.size); 83 73 if (ret) { 84 74 XDNA_ERR(xdna, "Map host buf failed, ret %d", ret); 85 75 goto out; ··· 187 175 aie2_job_put(job); 188 176 } 189 177 178 + static void aie2_set_cmd_timeout(struct amdxdna_sched_job *job) 179 + { 180 + struct aie2_ctx_health *aie2_health __free(kfree) = NULL; 181 + struct amdxdna_dev *xdna = job->hwctx->client->xdna; 182 + struct amdxdna_gem_obj *cmd_abo = job->cmd_bo; 183 + struct app_health_report *report = job->aie2_job_health; 184 + u32 fail_cmd_idx = 0; 185 + 186 + if (!report) 187 + goto set_timeout; 188 + 189 + XDNA_ERR(xdna, "Firmware timeout state capture:"); 190 + XDNA_ERR(xdna, "\tVersion: %d.%d", report->major, report->minor); 191 + XDNA_ERR(xdna, "\tReport size: 0x%x", report->size); 192 + XDNA_ERR(xdna, "\tContext ID: %d", report->context_id); 193 + XDNA_ERR(xdna, "\tDPU PC: 0x%x", report->dpu_pc); 194 + XDNA_ERR(xdna, "\tTXN OP ID: 0x%x", report->txn_op_id); 195 + XDNA_ERR(xdna, "\tContext PC: 0x%x", report->ctx_pc); 196 + XDNA_ERR(xdna, "\tFatal error type: 0x%x", report->fatal_info.fatal_type); 197 + XDNA_ERR(xdna, "\tFatal error exception type: 0x%x", report->fatal_info.exception_type); 198 + XDNA_ERR(xdna, "\tFatal error exception PC: 0x%x", report->fatal_info.exception_pc); 199 + XDNA_ERR(xdna, "\tFatal error app module: 0x%x", report->fatal_info.app_module); 200 + XDNA_ERR(xdna, "\tFatal error task ID: %d", report->fatal_info.task_index); 201 + XDNA_ERR(xdna, "\tTimed out sub command ID: %d", report->run_list_id); 202 + 203 + fail_cmd_idx = report->run_list_id; 204 + aie2_health = kzalloc_obj(*aie2_health); 205 + if (!aie2_health) 206 + goto set_timeout; 207 + 208 + aie2_health->header.version = AMDXDNA_CMD_CTX_HEALTH_V1; 209 + aie2_health->header.npu_gen = AMDXDNA_CMD_CTX_HEALTH_AIE2; 210 + aie2_health->txn_op_idx = report->txn_op_id; 211 + aie2_health->ctx_pc = report->ctx_pc; 212 + aie2_health->fatal_error_type = report->fatal_info.fatal_type; 213 + aie2_health->fatal_error_exception_type = report->fatal_info.exception_type; 214 + aie2_health->fatal_error_exception_pc = report->fatal_info.exception_pc; 215 + aie2_health->fatal_error_app_module = report->fatal_info.app_module; 216 + 217 + set_timeout: 218 + amdxdna_cmd_set_error(cmd_abo, job, fail_cmd_idx, ERT_CMD_STATE_TIMEOUT, 219 + aie2_health, sizeof(*aie2_health)); 220 + } 221 + 190 222 static int 191 223 aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size) 192 224 { ··· 242 186 cmd_abo = job->cmd_bo; 243 187 244 188 if (unlikely(job->job_timeout)) { 245 - amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_TIMEOUT); 189 + aie2_set_cmd_timeout(job); 246 190 ret = -EINVAL; 247 191 goto out; 248 192 } 249 193 250 194 if (unlikely(!data) || unlikely(size != sizeof(u32))) { 251 - amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_ABORT); 195 + amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_ABORT, NULL, 0); 252 196 ret = -EINVAL; 253 197 goto out; 254 198 } ··· 258 202 if (status == AIE2_STATUS_SUCCESS) 259 203 amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_COMPLETED); 260 204 else 261 - amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_ERROR); 205 + amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_ERROR, NULL, 0); 262 206 263 207 out: 264 208 aie2_sched_notify(job); ··· 292 236 struct amdxdna_sched_job *job = handle; 293 237 struct amdxdna_gem_obj *cmd_abo; 294 238 struct amdxdna_dev *xdna; 239 + u32 fail_cmd_idx = 0; 295 240 u32 fail_cmd_status; 296 - u32 fail_cmd_idx; 297 241 u32 cmd_status; 298 242 int ret = 0; 299 243 300 244 cmd_abo = job->cmd_bo; 301 245 302 246 if (unlikely(job->job_timeout)) { 303 - amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_TIMEOUT); 247 + aie2_set_cmd_timeout(job); 304 248 ret = -EINVAL; 305 249 goto out; 306 250 } 307 251 308 252 if (unlikely(!data) || unlikely(size != sizeof(u32) * 3)) { 309 - amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_ABORT); 253 + amdxdna_cmd_set_error(cmd_abo, job, 0, ERT_CMD_STATE_ABORT, NULL, 0); 310 254 ret = -EINVAL; 311 255 goto out; 312 256 } ··· 326 270 fail_cmd_idx, fail_cmd_status); 327 271 328 272 if (fail_cmd_status == AIE2_STATUS_SUCCESS) { 329 - amdxdna_cmd_set_error(cmd_abo, job, fail_cmd_idx, ERT_CMD_STATE_ABORT); 273 + amdxdna_cmd_set_error(cmd_abo, job, fail_cmd_idx, ERT_CMD_STATE_ABORT, NULL, 0); 330 274 ret = -EINVAL; 331 275 } else { 332 - amdxdna_cmd_set_error(cmd_abo, job, fail_cmd_idx, ERT_CMD_STATE_ERROR); 276 + amdxdna_cmd_set_error(cmd_abo, job, fail_cmd_idx, ERT_CMD_STATE_ERROR, NULL, 0); 333 277 } 334 278 335 279 out: ··· 418 362 { 419 363 struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job); 420 364 struct amdxdna_hwctx *hwctx = job->hwctx; 365 + struct app_health_report *report; 421 366 struct amdxdna_dev *xdna; 367 + int ret; 422 368 423 369 xdna = hwctx->client->xdna; 424 370 trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq); 425 371 job->job_timeout = true; 372 + 426 373 mutex_lock(&xdna->dev_lock); 374 + report = kzalloc_obj(*report); 375 + if (!report) 376 + goto reset_hwctx; 377 + 378 + ret = aie2_query_app_health(xdna->dev_handle, hwctx->fw_ctx_id, report); 379 + if (ret) 380 + kfree(report); 381 + else 382 + job->aie2_job_health = report; 383 + 384 + reset_hwctx: 427 385 aie2_hwctx_stop(xdna, hwctx, sched_job); 428 386 429 387 aie2_hwctx_restart(xdna, hwctx); ··· 707 637 } 708 638 709 639 ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, 710 - heap->mem.userptr, heap->mem.size); 640 + amdxdna_obj_dma_addr(hwctx->client, heap), 641 + heap->mem.size); 711 642 if (ret) { 712 643 XDNA_ERR(xdna, "Map host buffer failed, ret %d", ret); 713 644 goto release_resource;
+2 -3
drivers/accel/amdxdna/aie2_error.c
··· 355 355 return -ENOMEM; 356 356 357 357 events->buf = aie2_alloc_msg_buffer(ndev, &total_size, &events->addr); 358 - 359 - if (!events->buf) { 360 - ret = -ENOMEM; 358 + if (IS_ERR(events->buf)) { 359 + ret = PTR_ERR(events->buf); 361 360 goto free_events; 362 361 } 363 362 events->size = total_size;
+93 -12
drivers/accel/amdxdna/aie2_message.c
··· 61 61 *size = max(*size, SZ_8K); 62 62 order = get_order(*size); 63 63 if (order > MAX_PAGE_ORDER) 64 - return NULL; 64 + return ERR_PTR(-EINVAL); 65 65 *size = PAGE_SIZE << order; 66 66 67 + if (amdxdna_iova_on(xdna)) 68 + return amdxdna_iommu_alloc(xdna, *size, dma_addr); 69 + 67 70 return dma_alloc_noncoherent(xdna->ddev.dev, *size, dma_addr, 68 - DMA_FROM_DEVICE, GFP_KERNEL); 71 + DMA_FROM_DEVICE, GFP_KERNEL); 72 + } 73 + 74 + void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size, 75 + void *cpu_addr, dma_addr_t dma_addr) 76 + { 77 + struct amdxdna_dev *xdna = ndev->xdna; 78 + 79 + if (amdxdna_iova_on(xdna)) { 80 + amdxdna_iommu_free(xdna, size, cpu_addr, dma_addr); 81 + return; 82 + } 83 + 84 + dma_free_noncoherent(xdna->ddev.dev, size, cpu_addr, dma_addr, DMA_FROM_DEVICE); 69 85 } 70 86 71 87 int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev) ··· 272 256 req.num_col = hwctx->num_col; 273 257 req.num_unused_col = hwctx->num_unused_col; 274 258 req.num_cq_pairs_requested = 1; 275 - req.pasid = hwctx->client->pasid; 259 + req.pasid = amdxdna_pasid_on(hwctx->client) ? hwctx->client->pasid : 0; 276 260 req.context_priority = aie2_get_context_priority(ndev, hwctx); 277 261 278 262 ret = aie2_send_mgmt_msg_wait(ndev, &msg); ··· 396 380 int ret; 397 381 398 382 buff_addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr); 399 - if (!buff_addr) 400 - return -ENOMEM; 383 + if (IS_ERR(buff_addr)) 384 + return PTR_ERR(buff_addr); 401 385 402 386 /* Go through each hardware context and mark the AIE columns that are active */ 403 387 list_for_each_entry(client, &xdna->client_list, node) ··· 452 436 return -EINVAL; 453 437 454 438 addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr); 455 - if (!addr) 456 - return -ENOMEM; 439 + if (IS_ERR(addr)) 440 + return PTR_ERR(addr); 457 441 458 442 req.buf_addr = dma_addr; 459 443 req.buf_size = buf_sz; ··· 1002 986 struct amdxdna_cmd_chain *payload; 1003 987 struct xdna_mailbox_msg msg; 1004 988 union exec_chain_req req; 1005 - u32 payload_len; 989 + u32 payload_len, ccnt; 1006 990 u32 offset = 0; 1007 991 size_t size; 1008 992 int ret; ··· 1011 995 1012 996 op = amdxdna_cmd_get_op(cmd_abo); 1013 997 payload = amdxdna_cmd_get_payload(cmd_abo, &payload_len); 1014 - if (op != ERT_CMD_CHAIN || !payload || 1015 - payload_len < struct_size(payload, data, payload->command_count)) 998 + if (op != ERT_CMD_CHAIN) { 999 + XDNA_DBG(xdna, "Invalid op code %d", op); 1016 1000 return -EINVAL; 1001 + } 1002 + 1003 + if (!payload) { 1004 + XDNA_DBG(xdna, "Failed to get command payload"); 1005 + return -EINVAL; 1006 + } 1007 + 1008 + ccnt = payload->command_count; 1009 + if (payload_len < struct_size(payload, data, ccnt)) { 1010 + XDNA_DBG(xdna, "Invalid command count %d", ccnt); 1011 + return -EINVAL; 1012 + } 1017 1013 1018 1014 op = ERT_INVALID_CMD; 1019 - for (i = 0; i < payload->command_count; i++) { 1015 + for (i = 0; i < ccnt; i++) { 1020 1016 u32 boh = (u32)(payload->data[i]); 1021 1017 struct amdxdna_gem_obj *abo; 1022 1018 ··· 1047 1019 1048 1020 offset += size; 1049 1021 } 1022 + 1023 + XDNA_DBG(xdna, "Total %d commands:", ccnt); 1024 + print_hex_dump_debug("cmdbufs: ", DUMP_PREFIX_OFFSET, 16, 4, 1025 + cmdbuf_abo->mem.kva, offset, false); 1026 + 1050 1027 msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op); 1051 1028 if (msg.opcode == MSG_OP_MAX_OPCODE) 1052 1029 return -EOPNOTSUPP; 1053 1030 1054 1031 /* The offset is the accumulated total size of the cmd buffer */ 1055 1032 EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr, 1056 - offset, payload->command_count); 1033 + offset, ccnt); 1057 1034 drm_clflush_virt_range(cmdbuf_abo->mem.kva, offset); 1058 1035 1059 1036 msg.handle = job; 1060 1037 msg.notify_cb = notify_cb; 1061 1038 msg.send_data = (u8 *)&req; 1062 1039 msg.send_size = sizeof(req); 1040 + print_hex_dump_debug("cmdlist msg: ", DUMP_PREFIX_OFFSET, 16, 4, 1041 + &req, msg.send_size, false); 1063 1042 ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); 1064 1043 if (ret) { 1065 1044 XDNA_ERR(xdna, "Send message failed"); ··· 1095 1060 if (ret) 1096 1061 return ret; 1097 1062 1063 + print_hex_dump_debug("cmdbuf: ", DUMP_PREFIX_OFFSET, 16, 4, 1064 + cmdbuf_abo->mem.kva, size, false); 1065 + 1098 1066 msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op); 1099 1067 if (msg.opcode == MSG_OP_MAX_OPCODE) 1100 1068 return -EOPNOTSUPP; ··· 1110 1072 msg.notify_cb = notify_cb; 1111 1073 msg.send_data = (u8 *)&req; 1112 1074 msg.send_size = sizeof(req); 1075 + print_hex_dump_debug("cmdlist msg: ", DUMP_PREFIX_OFFSET, 16, 4, 1076 + &req, msg.send_size, false); 1113 1077 ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); 1114 1078 if (ret) { 1115 1079 XDNA_ERR(hwctx->client->xdna, "Send message failed"); ··· 1184 1144 msg.opcode = MSG_OP_CONFIG_DEBUG_BO; 1185 1145 1186 1146 return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); 1147 + } 1148 + 1149 + int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id, 1150 + struct app_health_report *report) 1151 + { 1152 + DECLARE_AIE2_MSG(get_app_health, MSG_OP_GET_APP_HEALTH); 1153 + struct amdxdna_dev *xdna = ndev->xdna; 1154 + struct app_health_report *buf; 1155 + dma_addr_t dma_addr; 1156 + u32 buf_size; 1157 + int ret; 1158 + 1159 + if (!AIE2_FEATURE_ON(ndev, AIE2_APP_HEALTH)) { 1160 + XDNA_DBG(xdna, "App health feature not supported"); 1161 + return -EOPNOTSUPP; 1162 + } 1163 + 1164 + buf_size = sizeof(*report); 1165 + buf = aie2_alloc_msg_buffer(ndev, &buf_size, &dma_addr); 1166 + if (IS_ERR(buf)) { 1167 + XDNA_ERR(xdna, "Failed to allocate buffer for app health"); 1168 + return PTR_ERR(buf); 1169 + } 1170 + 1171 + req.buf_addr = dma_addr; 1172 + req.context_id = context_id; 1173 + req.buf_size = buf_size; 1174 + 1175 + drm_clflush_virt_range(buf, sizeof(*report)); 1176 + ret = aie2_send_mgmt_msg_wait(ndev, &msg); 1177 + if (ret) { 1178 + XDNA_ERR(xdna, "Get app health failed, ret %d status 0x%x", ret, resp.status); 1179 + goto free_buf; 1180 + } 1181 + 1182 + /* Copy the report to caller's buffer */ 1183 + memcpy(report, buf, sizeof(*report)); 1184 + 1185 + free_buf: 1186 + aie2_free_msg_buffer(ndev, buf_size, buf, dma_addr); 1187 + return ret; 1187 1188 }
+52
drivers/accel/amdxdna/aie2_msg_priv.h
··· 31 31 MSG_OP_SET_RUNTIME_CONFIG = 0x10A, 32 32 MSG_OP_GET_RUNTIME_CONFIG = 0x10B, 33 33 MSG_OP_REGISTER_ASYNC_EVENT_MSG = 0x10C, 34 + MSG_OP_GET_APP_HEALTH = 0x114, 34 35 MSG_OP_MAX_DRV_OPCODE, 35 36 MSG_OP_GET_PROTOCOL_VERSION = 0x301, 36 37 MSG_OP_MAX_OPCODE ··· 451 450 452 451 struct config_debug_bo_resp { 453 452 enum aie2_msg_status status; 453 + } __packed; 454 + 455 + struct fatal_error_info { 456 + __u32 fatal_type; /* Fatal error type */ 457 + __u32 exception_type; /* Only valid if fatal_type is a specific value */ 458 + __u32 exception_argument; /* Argument based on exception type */ 459 + __u32 exception_pc; /* Program Counter at the time of the exception */ 460 + __u32 app_module; /* Error module name */ 461 + __u32 task_index; /* Index of the task in which the error occurred */ 462 + __u32 reserved[128]; 463 + }; 464 + 465 + struct app_health_report { 466 + __u16 major; 467 + __u16 minor; 468 + __u32 size; 469 + __u32 context_id; 470 + /* 471 + * Program Counter (PC) of the last initiated DPU opcode, as reported by the ERT 472 + * application. Before execution begins or after successful completion, the value is set 473 + * to UINT_MAX. If execution halts prematurely due to an error, this field retains the 474 + * opcode's PC value. 475 + * Note: To optimize performance, the ERT may simplify certain aspects of reporting. 476 + * Proper interpretation requires familiarity with the implementation details. 477 + */ 478 + __u32 dpu_pc; 479 + /* 480 + * Index of the last initiated TXN opcode. 481 + * Before execution starts or after successful completion, the value is set to UINT_MAX. 482 + * If execution halts prematurely due to an error, this field retains the opcode's ID. 483 + * Note: To optimize performance, the ERT may simplify certain aspects of reporting. 484 + * Proper interpretation requires familiarity with the implementation details. 485 + */ 486 + __u32 txn_op_id; 487 + /* The PC of the context at the time of the report */ 488 + __u32 ctx_pc; 489 + struct fatal_error_info fatal_info; 490 + /* Index of the most recently executed run list entry. */ 491 + __u32 run_list_id; 492 + }; 493 + 494 + struct get_app_health_req { 495 + __u32 context_id; 496 + __u32 buf_size; 497 + __u64 buf_addr; 498 + } __packed; 499 + 500 + struct get_app_health_resp { 501 + enum aie2_msg_status status; 502 + __u32 required_buffer_size; 503 + __u32 reserved[7]; 454 504 } __packed; 455 505 #endif /* _AIE2_MSG_PRIV_H_ */
+14
drivers/accel/amdxdna/aie2_pci.c
··· 846 846 struct amdxdna_drm_hwctx_entry *tmp __free(kfree) = NULL; 847 847 struct amdxdna_drm_get_array *array_args = arg; 848 848 struct amdxdna_drm_hwctx_entry __user *buf; 849 + struct app_health_report report; 850 + struct amdxdna_dev_hdl *ndev; 849 851 u32 size; 852 + int ret; 850 853 851 854 if (!array_args->num_element) 852 855 return -EINVAL; ··· 872 869 tmp->latency = hwctx->qos.latency; 873 870 tmp->frame_exec_time = hwctx->qos.frame_exec_time; 874 871 tmp->state = AMDXDNA_HWCTX_STATE_ACTIVE; 872 + ndev = hwctx->client->xdna->dev_handle; 873 + ret = aie2_query_app_health(ndev, hwctx->fw_ctx_id, &report); 874 + if (!ret) { 875 + /* Fill in app health report fields */ 876 + tmp->txn_op_idx = report.txn_op_id; 877 + tmp->ctx_pc = report.ctx_pc; 878 + tmp->fatal_error_type = report.fatal_info.fatal_type; 879 + tmp->fatal_error_exception_type = report.fatal_info.exception_type; 880 + tmp->fatal_error_exception_pc = report.fatal_info.exception_pc; 881 + tmp->fatal_error_app_module = report.fatal_info.app_module; 882 + } 875 883 876 884 buf = u64_to_user_ptr(array_args->buffer); 877 885 size = min(sizeof(*tmp), array_args->element_size);
+7 -3
drivers/accel/amdxdna/aie2_pci.h
··· 10 10 #include <linux/limits.h> 11 11 #include <linux/semaphore.h> 12 12 13 + #include "aie2_msg_priv.h" 13 14 #include "amdxdna_mailbox.h" 14 15 15 16 #define AIE2_INTERVAL 20000 /* us */ ··· 262 261 AIE2_NPU_COMMAND, 263 262 AIE2_PREEMPT, 264 263 AIE2_TEMPORAL_ONLY, 264 + AIE2_APP_HEALTH, 265 265 AIE2_FEATURE_MAX 266 266 }; 267 267 ··· 273 271 u32 min_minor; 274 272 }; 275 273 274 + #define AIE2_ALL_FEATURES GENMASK_ULL(AIE2_FEATURE_MAX - 1, AIE2_NPU_COMMAND) 276 275 #define AIE2_FEATURE_ON(ndev, feature) test_bit(feature, &(ndev)->feature_mask) 277 276 278 277 struct amdxdna_dev_priv { ··· 344 341 int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata); 345 342 int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev, 346 343 struct amdxdna_fw_ver *fw_ver); 344 + int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id, 345 + struct app_health_report *report); 347 346 int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx); 348 347 int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx); 349 348 int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size); ··· 371 366 int (*notify_cb)(void *, void __iomem *, size_t)); 372 367 void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size, 373 368 dma_addr_t *dma_addr); 374 - #define aie2_free_msg_buffer(ndev, size, buff_addr, dma_addr) \ 375 - dma_free_noncoherent((ndev)->xdna->ddev.dev, size, buff_addr, \ 376 - dma_addr, DMA_FROM_DEVICE) 369 + void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size, 370 + void *cpu_addr, dma_addr_t dma_addr); 377 371 378 372 /* aie2_hwctx.c */ 379 373 int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
+5 -1
drivers/accel/amdxdna/amdxdna_ctx.c
··· 137 137 138 138 int amdxdna_cmd_set_error(struct amdxdna_gem_obj *abo, 139 139 struct amdxdna_sched_job *job, u32 cmd_idx, 140 - enum ert_cmd_state error_state) 140 + enum ert_cmd_state error_state, 141 + void *err_data, size_t size) 141 142 { 142 143 struct amdxdna_client *client = job->hwctx->client; 143 144 struct amdxdna_cmd *cmd = abo->mem.kva; ··· 157 156 } 158 157 159 158 memset(cmd->data, 0xff, abo->mem.size - sizeof(*cmd)); 159 + if (err_data) 160 + memcpy(cmd->data, err_data, min(size, abo->mem.size - sizeof(*cmd))); 161 + 160 162 if (cc) 161 163 amdxdna_gem_put_obj(abo); 162 164
+17 -1
drivers/accel/amdxdna/amdxdna_ctx.h
··· 72 72 u32 prop_args[]; /* properties and regular kernel arguments */ 73 73 }; 74 74 75 + #define AMDXDNA_CMD_CTX_HEALTH_V1 1 76 + #define AMDXDNA_CMD_CTX_HEALTH_AIE2 0 77 + struct amdxdna_ctx_health { 78 + u32 version; 79 + u32 npu_gen; 80 + }; 81 + 75 82 /* Exec buffer command header format */ 76 83 #define AMDXDNA_CMD_STATE GENMASK(3, 0) 77 84 #define AMDXDNA_CMD_EXTRA_CU_MASK GENMASK(11, 10) ··· 129 122 u32 result; 130 123 }; 131 124 125 + struct app_health_report; 126 + union amdxdna_job_priv { 127 + struct app_health_report *aie2_health; 128 + }; 129 + 132 130 struct amdxdna_sched_job { 133 131 struct drm_sched_job base; 134 132 struct kref refcnt; ··· 148 136 u64 seq; 149 137 struct amdxdna_drv_cmd *drv_cmd; 150 138 struct amdxdna_gem_obj *cmd_bo; 139 + union amdxdna_job_priv priv; 151 140 size_t bo_cnt; 152 141 struct drm_gem_object *bos[] __counted_by(bo_cnt); 153 142 }; 143 + 144 + #define aie2_job_health priv.aie2_health 154 145 155 146 static inline u32 156 147 amdxdna_cmd_get_op(struct amdxdna_gem_obj *abo) ··· 184 169 u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo); 185 170 int amdxdna_cmd_set_error(struct amdxdna_gem_obj *abo, 186 171 struct amdxdna_sched_job *job, u32 cmd_idx, 187 - enum ert_cmd_state error_state); 172 + enum ert_cmd_state error_state, 173 + void *err_data, size_t size); 188 174 189 175 void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job); 190 176 void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
+44 -1
drivers/accel/amdxdna/amdxdna_gem.c
··· 474 474 drm_gem_shmem_free(&abo->base); 475 475 } 476 476 477 + static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *filp) 478 + { 479 + struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); 480 + struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); 481 + int ret; 482 + 483 + guard(mutex)(&abo->lock); 484 + if (abo->ref) { 485 + abo->ref++; 486 + return 0; 487 + } 488 + 489 + if (amdxdna_iova_on(xdna)) { 490 + ret = amdxdna_iommu_map_bo(xdna, abo); 491 + if (ret) 492 + return ret; 493 + } 494 + abo->ref++; 495 + 496 + return 0; 497 + } 498 + 499 + static void amdxdna_gem_obj_close(struct drm_gem_object *gobj, struct drm_file *filp) 500 + { 501 + struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); 502 + struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); 503 + 504 + guard(mutex)(&abo->lock); 505 + abo->ref--; 506 + if (abo->ref) 507 + return; 508 + 509 + if (amdxdna_iova_on(xdna)) 510 + amdxdna_iommu_unmap_bo(xdna, abo); 511 + } 512 + 477 513 static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = { 478 514 .free = amdxdna_gem_dev_obj_free, 479 515 }; 480 516 481 517 static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { 482 518 .free = amdxdna_gem_obj_free, 519 + .open = amdxdna_gem_obj_open, 520 + .close = amdxdna_gem_obj_close, 483 521 .print_info = drm_gem_shmem_object_print_info, 484 522 .pin = drm_gem_shmem_object_pin, 485 523 .unpin = drm_gem_shmem_object_unpin, ··· 544 506 545 507 abo->mem.userptr = AMDXDNA_INVALID_ADDR; 546 508 abo->mem.dev_addr = AMDXDNA_INVALID_ADDR; 509 + abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; 547 510 abo->mem.size = size; 548 511 INIT_LIST_HEAD(&abo->mem.umap_list); 549 512 ··· 660 621 abo = to_xdna_obj(gobj); 661 622 abo->attach = attach; 662 623 abo->dma_buf = dma_buf; 624 + abo->type = AMDXDNA_BO_SHMEM; 663 625 664 626 return gobj; 665 627 ··· 945 905 946 906 abo = to_xdna_obj(gobj); 947 907 args->vaddr = abo->mem.userptr; 948 - args->xdna_addr = abo->mem.dev_addr; 908 + if (abo->mem.dev_addr != AMDXDNA_INVALID_ADDR) 909 + args->xdna_addr = abo->mem.dev_addr; 910 + else 911 + args->xdna_addr = abo->mem.dma_addr; 949 912 950 913 if (abo->type != AMDXDNA_BO_DEV) 951 914 args->map_offset = drm_vma_node_offset_addr(&gobj->vma_node);
+10
drivers/accel/amdxdna/amdxdna_gem.h
··· 6 6 #ifndef _AMDXDNA_GEM_H_ 7 7 #define _AMDXDNA_GEM_H_ 8 8 9 + #include <drm/drm_gem_shmem_helper.h> 9 10 #include <linux/hmm.h> 11 + #include <linux/iova.h> 10 12 #include "amdxdna_pci_drv.h" 11 13 12 14 struct amdxdna_umap { ··· 27 25 u64 userptr; 28 26 void *kva; 29 27 u64 dev_addr; 28 + u64 dma_addr; 30 29 size_t size; 31 30 struct page **pages; 32 31 u32 nr_pages; ··· 42 39 bool pinned; 43 40 struct mutex lock; /* Protects: pinned */ 44 41 struct amdxdna_mem mem; 42 + u32 ref; 45 43 46 44 /* Below members is uninitialized when needed */ 47 45 struct drm_mm mm; /* For AMDXDNA_BO_DEV_HEAP */ ··· 70 66 static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo) 71 67 { 72 68 return abo->mem.dev_addr - abo->client->dev_heap->mem.dev_addr; 69 + } 70 + 71 + static inline u64 amdxdna_obj_dma_addr(struct amdxdna_client *client, 72 + struct amdxdna_gem_obj *abo) 73 + { 74 + return amdxdna_pasid_on(client) ? abo->mem.userptr : abo->mem.dma_addr; 73 75 } 74 76 75 77 void amdxdna_umap_put(struct amdxdna_umap *mapp);
+184
drivers/accel/amdxdna/amdxdna_iommu.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025, Advanced Micro Devices, Inc. 4 + */ 5 + 6 + #include <drm/amdxdna_accel.h> 7 + #include <linux/iommu.h> 8 + #include <linux/iova.h> 9 + 10 + #include "amdxdna_gem.h" 11 + #include "amdxdna_pci_drv.h" 12 + 13 + static bool force_iova; 14 + module_param(force_iova, bool, 0600); 15 + MODULE_PARM_DESC(force_iova, "Force use IOVA (Default false)"); 16 + 17 + static struct iova *amdxdna_iommu_alloc_iova(struct amdxdna_dev *xdna, 18 + size_t size, 19 + dma_addr_t *dma_addr, 20 + bool size_aligned) 21 + { 22 + unsigned long shift, end; 23 + struct iova *iova; 24 + 25 + end = xdna->domain->geometry.aperture_end; 26 + shift = iova_shift(&xdna->iovad); 27 + size = iova_align(&xdna->iovad, size); 28 + 29 + iova = alloc_iova(&xdna->iovad, size >> shift, end >> shift, size_aligned); 30 + if (!iova) 31 + return ERR_PTR(-ENOMEM); 32 + 33 + *dma_addr = iova_dma_addr(&xdna->iovad, iova); 34 + 35 + return iova; 36 + } 37 + 38 + int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) 39 + { 40 + struct sg_table *sgt; 41 + dma_addr_t dma_addr; 42 + struct iova *iova; 43 + size_t size; 44 + 45 + if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM) 46 + return 0; 47 + 48 + sgt = drm_gem_shmem_get_pages_sgt(&abo->base); 49 + if (IS_ERR(sgt)) { 50 + XDNA_ERR(xdna, "Get sgt failed, ret %ld", PTR_ERR(sgt)); 51 + return PTR_ERR(sgt); 52 + } 53 + 54 + if (!sgt->orig_nents || !sg_page(sgt->sgl)) { 55 + XDNA_ERR(xdna, "sgl is zero length or not page backed"); 56 + return -EOPNOTSUPP; 57 + } 58 + 59 + iova = amdxdna_iommu_alloc_iova(xdna, abo->mem.size, &dma_addr, 60 + (abo->type == AMDXDNA_BO_DEV_HEAP)); 61 + if (IS_ERR(iova)) { 62 + XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova)); 63 + return PTR_ERR(iova); 64 + } 65 + 66 + size = iommu_map_sgtable(xdna->domain, dma_addr, sgt, 67 + IOMMU_READ | IOMMU_WRITE); 68 + if (size < abo->mem.size) { 69 + __free_iova(&xdna->iovad, iova); 70 + return -ENXIO; 71 + } 72 + 73 + abo->mem.dma_addr = dma_addr; 74 + 75 + return 0; 76 + } 77 + 78 + void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo) 79 + { 80 + size_t size; 81 + 82 + if (abo->mem.dma_addr == AMDXDNA_INVALID_ADDR) 83 + return; 84 + 85 + size = iova_align(&xdna->iovad, abo->mem.size); 86 + iommu_unmap(xdna->domain, abo->mem.dma_addr, size); 87 + free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, abo->mem.dma_addr)); 88 + abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; 89 + } 90 + 91 + void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma_addr) 92 + { 93 + struct iova *iova; 94 + void *cpu_addr; 95 + int ret; 96 + 97 + iova = amdxdna_iommu_alloc_iova(xdna, size, dma_addr, true); 98 + if (IS_ERR(iova)) { 99 + XDNA_ERR(xdna, "Alloc iova failed, ret %ld", PTR_ERR(iova)); 100 + return iova; 101 + } 102 + 103 + cpu_addr = (void *)__get_free_pages(GFP_KERNEL, get_order(size)); 104 + if (!cpu_addr) { 105 + ret = -ENOMEM; 106 + goto free_iova; 107 + } 108 + 109 + ret = iommu_map(xdna->domain, *dma_addr, virt_to_phys(cpu_addr), 110 + iova_align(&xdna->iovad, size), 111 + IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); 112 + if (ret) 113 + goto free_iova; 114 + 115 + return cpu_addr; 116 + 117 + free_iova: 118 + __free_iova(&xdna->iovad, iova); 119 + return ERR_PTR(ret); 120 + } 121 + 122 + void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size, 123 + void *cpu_addr, dma_addr_t dma_addr) 124 + { 125 + iommu_unmap(xdna->domain, dma_addr, iova_align(&xdna->iovad, size)); 126 + free_iova(&xdna->iovad, iova_pfn(&xdna->iovad, dma_addr)); 127 + free_pages((unsigned long)cpu_addr, get_order(size)); 128 + } 129 + 130 + int amdxdna_iommu_init(struct amdxdna_dev *xdna) 131 + { 132 + unsigned long order; 133 + int ret; 134 + 135 + xdna->group = iommu_group_get(xdna->ddev.dev); 136 + if (!xdna->group || !force_iova) 137 + return 0; 138 + 139 + XDNA_WARN(xdna, "Enabled force_iova mode."); 140 + xdna->domain = iommu_paging_domain_alloc_flags(xdna->ddev.dev, 141 + IOMMU_HWPT_ALLOC_PASID); 142 + if (IS_ERR(xdna->domain)) { 143 + XDNA_ERR(xdna, "Failed to alloc iommu domain"); 144 + ret = PTR_ERR(xdna->domain); 145 + goto put_group; 146 + } 147 + 148 + ret = iova_cache_get(); 149 + if (ret) 150 + goto free_domain; 151 + 152 + order = __ffs(xdna->domain->pgsize_bitmap); 153 + init_iova_domain(&xdna->iovad, 1UL << order, 0); 154 + 155 + ret = iommu_attach_group(xdna->domain, xdna->group); 156 + if (ret) 157 + goto put_iova; 158 + 159 + return 0; 160 + 161 + put_iova: 162 + put_iova_domain(&xdna->iovad); 163 + iova_cache_put(); 164 + free_domain: 165 + iommu_domain_free(xdna->domain); 166 + put_group: 167 + iommu_group_put(xdna->group); 168 + xdna->domain = NULL; 169 + 170 + return ret; 171 + } 172 + 173 + void amdxdna_iommu_fini(struct amdxdna_dev *xdna) 174 + { 175 + if (xdna->domain) { 176 + iommu_detach_group(xdna->domain, xdna->group); 177 + put_iova_domain(&xdna->iovad); 178 + iova_cache_put(); 179 + iommu_domain_free(xdna->domain); 180 + } 181 + 182 + if (xdna->group) 183 + iommu_group_put(xdna->group); 184 + }
+30 -15
drivers/accel/amdxdna/amdxdna_pci_drv.c
··· 73 73 74 74 client->pid = pid_nr(rcu_access_pointer(filp->pid)); 75 75 client->xdna = xdna; 76 + client->pasid = IOMMU_PASID_INVALID; 76 77 77 - client->sva = iommu_sva_bind_device(xdna->ddev.dev, current->mm); 78 - if (IS_ERR(client->sva)) { 79 - ret = PTR_ERR(client->sva); 80 - XDNA_ERR(xdna, "SVA bind device failed, ret %d", ret); 81 - goto failed; 82 - } 83 - client->pasid = iommu_sva_get_pasid(client->sva); 84 - if (client->pasid == IOMMU_PASID_INVALID) { 85 - XDNA_ERR(xdna, "SVA get pasid failed"); 86 - ret = -ENODEV; 87 - goto unbind_sva; 78 + if (!amdxdna_iova_on(xdna)) { 79 + client->sva = iommu_sva_bind_device(xdna->ddev.dev, current->mm); 80 + if (IS_ERR(client->sva)) { 81 + ret = PTR_ERR(client->sva); 82 + XDNA_ERR(xdna, "SVA bind device failed, ret %d", ret); 83 + goto failed; 84 + } 85 + client->pasid = iommu_sva_get_pasid(client->sva); 86 + if (client->pasid == IOMMU_PASID_INVALID) { 87 + XDNA_ERR(xdna, "SVA get pasid failed"); 88 + ret = -ENODEV; 89 + goto unbind_sva; 90 + } 88 91 } 89 92 client->mm = current->mm; 90 93 mmgrab(client->mm); ··· 106 103 return 0; 107 104 108 105 unbind_sva: 109 - iommu_sva_unbind_device(client->sva); 106 + if (!IS_ERR_OR_NULL(client->sva)) 107 + iommu_sva_unbind_device(client->sva); 110 108 failed: 111 109 kfree(client); 112 110 ··· 125 121 if (client->dev_heap) 126 122 drm_gem_object_put(to_gobj(client->dev_heap)); 127 123 128 - iommu_sva_unbind_device(client->sva); 124 + if (!IS_ERR_OR_NULL(client->sva)) 125 + iommu_sva_unbind_device(client->sva); 129 126 mmdrop(client->mm); 130 127 131 128 kfree(client); ··· 287 282 fs_reclaim_release(GFP_KERNEL); 288 283 } 289 284 285 + ret = amdxdna_iommu_init(xdna); 286 + if (ret) 287 + return ret; 288 + 290 289 xdna->notifier_wq = alloc_ordered_workqueue("notifier_wq", WQ_MEM_RECLAIM); 291 - if (!xdna->notifier_wq) 292 - return -ENOMEM; 290 + if (!xdna->notifier_wq) { 291 + ret = -ENOMEM; 292 + goto iommu_fini; 293 + } 293 294 294 295 mutex_lock(&xdna->dev_lock); 295 296 ret = xdna->dev_info->ops->init(xdna); ··· 327 316 mutex_unlock(&xdna->dev_lock); 328 317 destroy_notifier_wq: 329 318 destroy_workqueue(xdna->notifier_wq); 319 + iommu_fini: 320 + amdxdna_iommu_fini(xdna); 330 321 return ret; 331 322 } 332 323 ··· 354 341 355 342 xdna->dev_info->ops->fini(xdna); 356 343 mutex_unlock(&xdna->dev_lock); 344 + 345 + amdxdna_iommu_fini(xdna); 357 346 } 358 347 359 348 static const struct dev_pm_ops amdxdna_pm_ops = {
+24
drivers/accel/amdxdna/amdxdna_pci_drv.h
··· 6 6 #ifndef _AMDXDNA_PCI_DRV_H_ 7 7 #define _AMDXDNA_PCI_DRV_H_ 8 8 9 + #include <drm/amdxdna_accel.h> 9 10 #include <drm/drm_print.h> 11 + #include <linux/iommu.h> 12 + #include <linux/iova.h> 10 13 #include <linux/workqueue.h> 11 14 #include <linux/xarray.h> 12 15 ··· 104 101 struct amdxdna_fw_ver fw_ver; 105 102 struct rw_semaphore notifier_lock; /* for mmu notifier*/ 106 103 struct workqueue_struct *notifier_wq; 104 + 105 + struct iommu_group *group; 106 + struct iommu_domain *domain; 107 + struct iova_domain iovad; 107 108 }; 108 109 109 110 /* ··· 152 145 int amdxdna_sysfs_init(struct amdxdna_dev *xdna); 153 146 void amdxdna_sysfs_fini(struct amdxdna_dev *xdna); 154 147 148 + int amdxdna_iommu_init(struct amdxdna_dev *xdna); 149 + void amdxdna_iommu_fini(struct amdxdna_dev *xdna); 150 + int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo); 151 + void amdxdna_iommu_unmap_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo); 152 + void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma_addr); 153 + void amdxdna_iommu_free(struct amdxdna_dev *xdna, size_t size, 154 + void *cpu_addr, dma_addr_t dma_addr); 155 + 156 + static inline bool amdxdna_iova_on(struct amdxdna_dev *xdna) 157 + { 158 + return !!xdna->domain; 159 + } 160 + 161 + static inline bool amdxdna_pasid_on(struct amdxdna_client *client) 162 + { 163 + return client->pasid != IOMMU_PASID_INVALID; 164 + } 155 165 #endif /* _AMDXDNA_PCI_DRV_H_ */
+2 -1
drivers/accel/amdxdna/npu4_regs.c
··· 93 93 { .features = BIT_U64(AIE2_NPU_COMMAND), .major = 6, .min_minor = 15 }, 94 94 { .features = BIT_U64(AIE2_PREEMPT), .major = 6, .min_minor = 12 }, 95 95 { .features = BIT_U64(AIE2_TEMPORAL_ONLY), .major = 6, .min_minor = 12 }, 96 - { .features = GENMASK_ULL(AIE2_TEMPORAL_ONLY, AIE2_NPU_COMMAND), .major = 7 }, 96 + { .features = BIT_U64(AIE2_APP_HEALTH), .major = 6, .min_minor = 18 }, 97 + { .features = AIE2_ALL_FEATURES, .major = 7 }, 97 98 { 0 } 98 99 }; 99 100
+12 -2
drivers/accel/ivpu/ivpu_debugfs.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) 2020-2024 Intel Corporation 3 + * Copyright (C) 2020-2026 Intel Corporation 4 4 */ 5 5 6 6 #include <linux/debugfs.h> ··· 127 127 return 0; 128 128 } 129 129 130 + static int engine_reset_counter_show(struct seq_file *s, void *v) 131 + { 132 + struct ivpu_device *vdev = seq_to_ivpu(s); 133 + 134 + seq_printf(s, "%d\n", atomic_read(&vdev->pm->engine_reset_counter)); 135 + return 0; 136 + } 137 + 130 138 static const struct drm_debugfs_info vdev_debugfs_list[] = { 131 139 {"bo_list", bo_list_show, 0}, 132 140 {"fw_name", fw_name_show, 0}, ··· 145 137 {"reset_counter", reset_counter_show, 0}, 146 138 {"reset_pending", reset_pending_show, 0}, 147 139 {"firewall_irq_counter", firewall_irq_counter_show, 0}, 140 + {"engine_reset_counter", engine_reset_counter_show, 0}, 148 141 }; 149 142 150 143 static int dvfs_mode_get(void *data, u64 *dvfs_mode) ··· 361 352 static int ivpu_reset_engine_fn(void *data, u64 val) 362 353 { 363 354 struct ivpu_device *vdev = (struct ivpu_device *)data; 355 + struct vpu_jsm_msg resp; 364 356 365 - return ivpu_jsm_reset_engine(vdev, (u32)val); 357 + return ivpu_jsm_reset_engine(vdev, (u32)val, &resp); 366 358 } 367 359 368 360 DEFINE_DEBUGFS_ATTRIBUTE(ivpu_reset_engine_fops, NULL, ivpu_reset_engine_fn, "0x%02llx\n");
+1
drivers/accel/ivpu/ivpu_drv.c
··· 665 665 vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID; 666 666 atomic64_set(&vdev->unique_id_counter, 0); 667 667 atomic_set(&vdev->job_timeout_counter, 0); 668 + atomic_set(&vdev->faults_detected, 0); 668 669 xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ); 669 670 xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1); 670 671 xa_init_flags(&vdev->db_xa, XA_FLAGS_ALLOC1);
+2 -1
drivers/accel/ivpu/ivpu_drv.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 /* 3 - * Copyright (C) 2020-2025 Intel Corporation 3 + * Copyright (C) 2020-2026 Intel Corporation 4 4 */ 5 5 6 6 #ifndef __IVPU_DRV_H__ ··· 168 168 struct xarray submitted_jobs_xa; 169 169 struct ivpu_ipc_consumer job_done_consumer; 170 170 atomic_t job_timeout_counter; 171 + atomic_t faults_detected; 171 172 172 173 atomic64_t unique_id_counter; 173 174
+48 -2
drivers/accel/ivpu/ivpu_job.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) 2020-2025 Intel Corporation 3 + * Copyright (C) 2020-2026 Intel Corporation 4 4 */ 5 5 6 6 #include <drm/drm_file.h> ··· 607 607 * status and ensure both are handled in the same way 608 608 */ 609 609 job->file_priv->has_mmu_faults = true; 610 + atomic_set(&vdev->faults_detected, 1); 610 611 queue_work(system_percpu_wq, &vdev->context_abort_work); 611 612 return true; 612 613 } ··· 1116 1115 ivpu_ipc_consumer_del(vdev, &vdev->job_done_consumer); 1117 1116 } 1118 1117 1118 + static int reset_engine_and_mark_faulty_contexts(struct ivpu_device *vdev) 1119 + { 1120 + u32 num_impacted_contexts; 1121 + struct vpu_jsm_msg resp; 1122 + int ret; 1123 + u32 i; 1124 + 1125 + ret = ivpu_jsm_reset_engine(vdev, 0, &resp); 1126 + if (ret) 1127 + return ret; 1128 + 1129 + /* 1130 + * If faults are detected, ignore guilty contexts from engine reset as NPU may not be stuck 1131 + * and could return currently running good context and faulty contexts are already marked 1132 + */ 1133 + if (atomic_cmpxchg(&vdev->faults_detected, 1, 0) == 1) 1134 + return 0; 1135 + 1136 + num_impacted_contexts = resp.payload.engine_reset_done.num_impacted_contexts; 1137 + 1138 + ivpu_warn_ratelimited(vdev, "Engine reset performed, impacted contexts: %u\n", 1139 + num_impacted_contexts); 1140 + 1141 + if (!in_range(num_impacted_contexts, 1, VPU_MAX_ENGINE_RESET_IMPACTED_CONTEXTS - 1)) { 1142 + ivpu_pm_trigger_recovery(vdev, "Cannot determine guilty contexts"); 1143 + return -EIO; 1144 + } 1145 + 1146 + /* No faults detected, NPU likely got stuck. Mark returned contexts as guilty */ 1147 + guard(mutex)(&vdev->context_list_lock); 1148 + 1149 + for (i = 0; i < num_impacted_contexts; i++) { 1150 + u32 ssid = resp.payload.engine_reset_done.impacted_contexts[i].host_ssid; 1151 + struct ivpu_file_priv *file_priv = xa_load(&vdev->context_xa, ssid); 1152 + 1153 + if (file_priv) { 1154 + mutex_lock(&file_priv->lock); 1155 + file_priv->has_mmu_faults = true; 1156 + mutex_unlock(&file_priv->lock); 1157 + } 1158 + } 1159 + 1160 + return 0; 1161 + } 1162 + 1119 1163 void ivpu_context_abort_work_fn(struct work_struct *work) 1120 1164 { 1121 1165 struct ivpu_device *vdev = container_of(work, struct ivpu_device, context_abort_work); ··· 1173 1127 return; 1174 1128 1175 1129 if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) 1176 - if (ivpu_jsm_reset_engine(vdev, 0)) 1130 + if (reset_engine_and_mark_faulty_contexts(vdev)) 1177 1131 goto runtime_put; 1178 1132 1179 1133 mutex_lock(&vdev->context_list_lock);
+15 -4
drivers/accel/ivpu/ivpu_jsm_msg.c
··· 151 151 return ret; 152 152 } 153 153 154 - int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine) 154 + int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine, struct vpu_jsm_msg *resp) 155 155 { 156 156 struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_ENGINE_RESET }; 157 - struct vpu_jsm_msg resp; 158 157 int ret; 159 158 160 159 if (engine != VPU_ENGINE_COMPUTE) ··· 161 162 162 163 req.payload.engine_reset.engine_idx = engine; 163 164 164 - ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_ENGINE_RESET_DONE, &resp, 165 + ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_ENGINE_RESET_DONE, resp, 165 166 VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm); 166 167 if (ret) { 167 168 ivpu_err_ratelimited(vdev, "Failed to reset engine %d: %d\n", engine, ret); 168 169 ivpu_pm_trigger_recovery(vdev, "Engine reset failed"); 170 + return ret; 169 171 } 170 172 171 - return ret; 173 + atomic_inc(&vdev->pm->engine_reset_counter); 174 + 175 + return 0; 172 176 } 173 177 174 178 int ivpu_jsm_preempt_engine(struct ivpu_device *vdev, u32 engine, u32 preempt_id) ··· 556 554 } 557 555 558 556 int ivpu_jsm_state_dump(struct ivpu_device *vdev) 557 + { 558 + struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_STATE_DUMP }; 559 + struct vpu_jsm_msg resp; 560 + 561 + return ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_STATE_DUMP_RSP, &resp, 562 + VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm); 563 + } 564 + 565 + int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev) 559 566 { 560 567 struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_STATE_DUMP }; 561 568
+2 -1
drivers/accel/ivpu/ivpu_jsm_msg.h
··· 14 14 u64 jobq_base, u32 jobq_size); 15 15 int ivpu_jsm_unregister_db(struct ivpu_device *vdev, u32 db_id); 16 16 int ivpu_jsm_get_heartbeat(struct ivpu_device *vdev, u32 engine, u64 *heartbeat); 17 - int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine); 17 + int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine, struct vpu_jsm_msg *response); 18 18 int ivpu_jsm_preempt_engine(struct ivpu_device *vdev, u32 engine, u32 preempt_id); 19 19 int ivpu_jsm_dyndbg_control(struct ivpu_device *vdev, char *command, size_t size); 20 20 int ivpu_jsm_trace_get_capability(struct ivpu_device *vdev, u32 *trace_destination_mask, ··· 44 44 int ivpu_jsm_dct_enable(struct ivpu_device *vdev, u32 active_us, u32 inactive_us); 45 45 int ivpu_jsm_dct_disable(struct ivpu_device *vdev); 46 46 int ivpu_jsm_state_dump(struct ivpu_device *vdev); 47 + int ivpu_jsm_state_dump_no_reply(struct ivpu_device *vdev); 47 48 48 49 #endif
+2 -1
drivers/accel/ivpu/ivpu_mmu.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) 2020-2024 Intel Corporation 3 + * Copyright (C) 2020-2026 Intel Corporation 4 4 */ 5 5 6 6 #include <linux/circ_buf.h> ··· 964 964 file_priv = xa_load(&vdev->context_xa, ssid); 965 965 if (file_priv) { 966 966 if (!READ_ONCE(file_priv->has_mmu_faults)) { 967 + atomic_set(&vdev->faults_detected, 1); 967 968 ivpu_mmu_dump_event(vdev, event); 968 969 WRITE_ONCE(file_priv->has_mmu_faults, true); 969 970 }
+9 -6
drivers/accel/ivpu/ivpu_pm.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * Copyright (C) 2020-2024 Intel Corporation 3 + * Copyright (C) 2020-2026 Intel Corporation 4 4 */ 5 5 6 6 #include <linux/highmem.h> ··· 166 166 ivpu_pm_reset_begin(vdev); 167 167 168 168 if (!pm_runtime_status_suspended(vdev->drm.dev)) { 169 - ivpu_jsm_state_dump(vdev); 169 + ivpu_jsm_state_dump_no_reply(vdev); 170 170 ivpu_dev_coredump(vdev); 171 171 ivpu_suspend(vdev); 172 172 } ··· 205 205 206 206 if (ivpu_jsm_get_heartbeat(vdev, 0, &heartbeat) || heartbeat <= vdev->fw->last_heartbeat) { 207 207 ivpu_err(vdev, "Job timeout detected, heartbeat not progressed\n"); 208 - goto recovery; 208 + goto abort; 209 209 } 210 210 211 211 inference_max_retries = DIV_ROUND_UP(inference_timeout_ms, timeout_ms); 212 212 if (atomic_fetch_inc(&vdev->job_timeout_counter) >= inference_max_retries) { 213 213 ivpu_err(vdev, "Job timeout detected, heartbeat limit (%lld) exceeded\n", 214 214 inference_max_retries); 215 - goto recovery; 215 + goto abort; 216 216 } 217 217 218 218 vdev->fw->last_heartbeat = heartbeat; 219 219 ivpu_start_job_timeout_detection(vdev); 220 220 return; 221 221 222 - recovery: 222 + abort: 223 223 atomic_set(&vdev->job_timeout_counter, 0); 224 - ivpu_pm_trigger_recovery(vdev, "TDR"); 224 + ivpu_jsm_state_dump(vdev); 225 + ivpu_dev_coredump(vdev); 226 + queue_work(system_percpu_wq, &vdev->context_abort_work); 225 227 } 226 228 227 229 void ivpu_start_job_timeout_detection(struct ivpu_device *vdev) ··· 406 404 init_rwsem(&pm->reset_lock); 407 405 atomic_set(&pm->reset_pending, 0); 408 406 atomic_set(&pm->reset_counter, 0); 407 + atomic_set(&pm->engine_reset_counter, 0); 409 408 410 409 INIT_WORK(&pm->recovery_work, ivpu_pm_recovery_work); 411 410 INIT_DELAYED_WORK(&pm->job_timeout_work, ivpu_job_timeout_work);
+1
drivers/accel/ivpu/ivpu_pm.h
··· 18 18 struct rw_semaphore reset_lock; 19 19 atomic_t reset_counter; 20 20 atomic_t reset_pending; 21 + atomic_t engine_reset_counter; 21 22 u8 dct_active_percent; 22 23 }; 23 24
+10
drivers/gpu/drm/bridge/Kconfig
··· 191 191 HDMI signals 192 192 Please say Y if you have such hardware. 193 193 194 + config DRM_LONTIUM_LT8713SX 195 + tristate "Lontium LT8713SX DP MST bridge" 196 + depends on OF 197 + select REGMAP_I2C 198 + help 199 + Driver for Lontium LT8713SX DP MST bridge 200 + chip firmware upgrade, which converts Type-C/DP1.4 201 + to 3 configurable Type-C/DP1.4/HDMI2.0 outputs 202 + Please say Y if you have such hardware. 203 + 194 204 config DRM_ITE_IT66121 195 205 tristate "ITE IT66121 HDMI bridge" 196 206 depends on OF
+1
drivers/gpu/drm/bridge/Makefile
··· 17 17 obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o 18 18 obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o 19 19 obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o 20 + obj-$(CONFIG_DRM_LONTIUM_LT8713SX) += lontium-lt8713sx.o 20 21 obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o 21 22 obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o 22 23 obj-$(CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER) += microchip-lvds.o
+598
drivers/gpu/drm/bridge/lontium-lt8713sx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 4 + */ 5 + 6 + #include <linux/crc8.h> 7 + #include <linux/firmware.h> 8 + #include <linux/gpio/consumer.h> 9 + #include <linux/i2c.h> 10 + #include <linux/interrupt.h> 11 + #include <linux/module.h> 12 + #include <linux/mutex.h> 13 + #include <linux/of_graph.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/regmap.h> 16 + #include <linux/regulator/consumer.h> 17 + #include <linux/sizes.h> 18 + #include <linux/wait.h> 19 + #include <linux/workqueue.h> 20 + 21 + #include <drm/drm_bridge.h> 22 + #include <drm/drm_of.h> 23 + 24 + #define FW_FILE "lt8713sx_fw.bin" 25 + 26 + #define REG_PAGE_CONTROL 0xff 27 + 28 + #define LT8713SX_PAGE_SIZE 256 29 + 30 + DECLARE_CRC8_TABLE(lt8713sx_crc_table); 31 + 32 + struct lt8713sx { 33 + struct device *dev; 34 + struct drm_bridge bridge; 35 + struct drm_bridge *next_bridge; 36 + 37 + struct regmap *regmap; 38 + /* Protects all accesses to registers by stopping the on-chip MCU */ 39 + struct mutex ocm_lock; 40 + 41 + struct gpio_desc *reset_gpio; 42 + struct gpio_desc *enable_gpio; 43 + 44 + struct i2c_client *client; 45 + const struct firmware *fw; 46 + 47 + u8 *fw_buffer; 48 + 49 + u32 main_crc_value; 50 + u32 bank_crc_value[17]; 51 + 52 + int bank_num; 53 + }; 54 + 55 + static void lt8713sx_reset(struct lt8713sx *lt8713sx); 56 + 57 + static const struct regmap_range lt8713sx_ranges[] = { 58 + { 59 + .range_min = 0x0000, 60 + .range_max = 0xffff 61 + }, 62 + }; 63 + 64 + static const struct regmap_access_table lt8713sx_table = { 65 + .yes_ranges = lt8713sx_ranges, 66 + .n_yes_ranges = ARRAY_SIZE(lt8713sx_ranges), 67 + }; 68 + 69 + static const struct regmap_range_cfg lt8713sx_range_cfg = { 70 + .name = "lt8713sx", 71 + .range_min = 0x0000, 72 + .range_max = 0xffff, 73 + .selector_reg = REG_PAGE_CONTROL, 74 + .selector_mask = 0xff, 75 + .selector_shift = 0, 76 + .window_start = 0, 77 + .window_len = 0x100, 78 + }; 79 + 80 + static const struct regmap_config lt8713sx_regmap_config = { 81 + .reg_bits = 8, 82 + .val_bits = 8, 83 + .volatile_table = &lt8713sx_table, 84 + .ranges = &lt8713sx_range_cfg, 85 + .num_ranges = 1, 86 + .cache_type = REGCACHE_NONE, 87 + .max_register = 0xffff, 88 + }; 89 + 90 + static void lt8713sx_i2c_enable(struct lt8713sx *lt8713sx) 91 + { 92 + regmap_write(lt8713sx->regmap, 0xe0ee, 0x01); 93 + } 94 + 95 + static void lt8713sx_i2c_disable(struct lt8713sx *lt8713sx) 96 + { 97 + regmap_write(lt8713sx->regmap, 0xe0ee, 0x00); 98 + } 99 + 100 + static int lt8713sx_prepare_firmware_data(struct lt8713sx *lt8713sx) 101 + { 102 + int ret = 0; 103 + u64 sz_12k = 12 * SZ_1K; 104 + 105 + ret = request_firmware(&lt8713sx->fw, FW_FILE, lt8713sx->dev); 106 + if (ret < 0) { 107 + dev_err(lt8713sx->dev, "request firmware failed\n"); 108 + return ret; 109 + } 110 + 111 + dev_dbg(lt8713sx->dev, "Firmware size: %zu bytes\n", lt8713sx->fw->size); 112 + 113 + if (lt8713sx->fw->size > SZ_256K - 1) { 114 + dev_err(lt8713sx->dev, "Firmware size exceeds 256KB limit\n"); 115 + release_firmware(lt8713sx->fw); 116 + return -EINVAL; 117 + } 118 + 119 + lt8713sx->fw_buffer = kvmalloc(SZ_256K, GFP_KERNEL); 120 + if (!lt8713sx->fw_buffer) { 121 + release_firmware(lt8713sx->fw); 122 + return -ENOMEM; 123 + } 124 + 125 + memset(lt8713sx->fw_buffer, 0xff, SZ_256K); 126 + 127 + /* main firmware */ 128 + memcpy(lt8713sx->fw_buffer, lt8713sx->fw->data, SZ_64K - 1); 129 + 130 + lt8713sx->fw_buffer[SZ_64K - 1] = 131 + crc8(lt8713sx_crc_table, lt8713sx->fw_buffer, SZ_64K - 1, 0); 132 + lt8713sx->main_crc_value = lt8713sx->fw_buffer[SZ_64K - 1]; 133 + dev_dbg(lt8713sx->dev, 134 + "Main Firmware Data Crc = 0x%02X\n", lt8713sx->main_crc_value); 135 + 136 + /* bank firmware */ 137 + memcpy(lt8713sx->fw_buffer + SZ_64K, 138 + lt8713sx->fw->data + SZ_64K, 139 + lt8713sx->fw->size - SZ_64K); 140 + 141 + lt8713sx->bank_num = (lt8713sx->fw->size - SZ_64K + sz_12k - 1) / sz_12k; 142 + dev_dbg(lt8713sx->dev, "Bank Number Total is %d.\n", lt8713sx->bank_num); 143 + 144 + for (int i = 0; i < lt8713sx->bank_num; i++) { 145 + lt8713sx->bank_crc_value[i] = 146 + crc8(lt8713sx_crc_table, lt8713sx->fw_buffer + SZ_64K + i * sz_12k, 147 + sz_12k, 0); 148 + dev_dbg(lt8713sx->dev, "Bank number:%d; Firmware Data Crc:0x%02X\n", 149 + i, lt8713sx->bank_crc_value[i]); 150 + } 151 + return 0; 152 + } 153 + 154 + static void lt8713sx_config_parameters(struct lt8713sx *lt8713sx) 155 + { 156 + regmap_write(lt8713sx->regmap, 0xe05e, 0xc1); 157 + regmap_write(lt8713sx->regmap, 0xe058, 0x00); 158 + regmap_write(lt8713sx->regmap, 0xe059, 0x50); 159 + regmap_write(lt8713sx->regmap, 0xe05a, 0x10); 160 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 161 + regmap_write(lt8713sx->regmap, 0xe058, 0x21); 162 + } 163 + 164 + static void lt8713sx_wren(struct lt8713sx *lt8713sx) 165 + { 166 + regmap_write(lt8713sx->regmap, 0xe103, 0xbf); 167 + regmap_write(lt8713sx->regmap, 0xe103, 0xff); 168 + regmap_write(lt8713sx->regmap, 0xe05a, 0x04); 169 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 170 + } 171 + 172 + static void lt8713sx_wrdi(struct lt8713sx *lt8713sx) 173 + { 174 + regmap_write(lt8713sx->regmap, 0xe05a, 0x08); 175 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 176 + } 177 + 178 + static void lt8713sx_fifo_reset(struct lt8713sx *lt8713sx) 179 + { 180 + regmap_write(lt8713sx->regmap, 0xe103, 0xbf); 181 + regmap_write(lt8713sx->regmap, 0xe103, 0xff); 182 + } 183 + 184 + static void lt8713sx_disable_sram_write(struct lt8713sx *lt8713sx) 185 + { 186 + regmap_write(lt8713sx->regmap, 0xe055, 0x00); 187 + } 188 + 189 + static void lt8713sx_sram_to_flash(struct lt8713sx *lt8713sx) 190 + { 191 + regmap_write(lt8713sx->regmap, 0xe05a, 0x30); 192 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 193 + } 194 + 195 + static void lt8713sx_i2c_to_sram(struct lt8713sx *lt8713sx) 196 + { 197 + regmap_write(lt8713sx->regmap, 0xe055, 0x80); 198 + regmap_write(lt8713sx->regmap, 0xe05e, 0xc0); 199 + regmap_write(lt8713sx->regmap, 0xe058, 0x21); 200 + } 201 + 202 + static u8 lt8713sx_read_flash_status(struct lt8713sx *lt8713sx) 203 + { 204 + u32 flash_status = 0; 205 + 206 + regmap_write(lt8713sx->regmap, 0xe103, 0x3f); 207 + regmap_write(lt8713sx->regmap, 0xe103, 0xff); 208 + 209 + regmap_write(lt8713sx->regmap, 0xe05e, 0x40); 210 + regmap_write(lt8713sx->regmap, 0xe056, 0x05); /* opcode=read status register */ 211 + regmap_write(lt8713sx->regmap, 0xe055, 0x25); 212 + regmap_write(lt8713sx->regmap, 0xe055, 0x01); 213 + regmap_write(lt8713sx->regmap, 0xe058, 0x21); 214 + 215 + regmap_read(lt8713sx->regmap, 0xe05f, &flash_status); 216 + dev_dbg(lt8713sx->dev, "flash_status:%x\n", flash_status); 217 + 218 + return flash_status; 219 + } 220 + 221 + static void lt8713sx_block_erase(struct lt8713sx *lt8713sx) 222 + { 223 + u32 i = 0; 224 + u8 flash_status = 0; 225 + u8 blocknum = 0x00; 226 + u32 flashaddr = 0x00; 227 + 228 + for (blocknum = 0; blocknum < 8; blocknum++) { 229 + flashaddr = blocknum * SZ_32K; 230 + regmap_write(lt8713sx->regmap, 0xe05a, 0x04); 231 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 232 + regmap_write(lt8713sx->regmap, 0xe05b, flashaddr >> 16); 233 + regmap_write(lt8713sx->regmap, 0xe05c, flashaddr >> 8); 234 + regmap_write(lt8713sx->regmap, 0xe05d, flashaddr); 235 + regmap_write(lt8713sx->regmap, 0xe05a, 0x01); 236 + regmap_write(lt8713sx->regmap, 0xe05a, 0x00); 237 + msleep(100); 238 + i = 0; 239 + while (1) { 240 + flash_status = lt8713sx_read_flash_status(lt8713sx); 241 + if ((flash_status & 0x01) == 0) 242 + break; 243 + 244 + if (i > 50) 245 + break; 246 + 247 + i++; 248 + msleep(50); 249 + } 250 + } 251 + dev_dbg(lt8713sx->dev, "erase flash done.\n"); 252 + } 253 + 254 + static void lt8713sx_load_main_fw_to_sram(struct lt8713sx *lt8713sx) 255 + { 256 + regmap_write(lt8713sx->regmap, 0xe068, 0x00); 257 + regmap_write(lt8713sx->regmap, 0xe069, 0x00); 258 + regmap_write(lt8713sx->regmap, 0xe06a, 0x00); 259 + regmap_write(lt8713sx->regmap, 0xe065, 0x00); 260 + regmap_write(lt8713sx->regmap, 0xe066, 0xff); 261 + regmap_write(lt8713sx->regmap, 0xe067, 0xff); 262 + regmap_write(lt8713sx->regmap, 0xe06b, 0x00); 263 + regmap_write(lt8713sx->regmap, 0xe06c, 0x00); 264 + regmap_write(lt8713sx->regmap, 0xe060, 0x01); 265 + msleep(200); 266 + regmap_write(lt8713sx->regmap, 0xe060, 0x00); 267 + } 268 + 269 + static void lt8713sx_load_bank_fw_to_sram(struct lt8713sx *lt8713sx, u64 addr) 270 + { 271 + regmap_write(lt8713sx->regmap, 0xe068, ((addr & 0xff0000) >> 16)); 272 + regmap_write(lt8713sx->regmap, 0xe069, ((addr & 0x00ff00) >> 8)); 273 + regmap_write(lt8713sx->regmap, 0xe06a, (addr & 0x0000ff)); 274 + regmap_write(lt8713sx->regmap, 0xe065, 0x00); 275 + regmap_write(lt8713sx->regmap, 0xe066, 0x30); 276 + regmap_write(lt8713sx->regmap, 0xe067, 0x00); 277 + regmap_write(lt8713sx->regmap, 0xe06b, 0x00); 278 + regmap_write(lt8713sx->regmap, 0xe06c, 0x00); 279 + regmap_write(lt8713sx->regmap, 0xe060, 0x01); 280 + msleep(50); 281 + regmap_write(lt8713sx->regmap, 0xe060, 0x00); 282 + } 283 + 284 + static int lt8713sx_write_data(struct lt8713sx *lt8713sx, const u8 *data, u64 filesize) 285 + { 286 + int page = 0, num = 0, i = 0, val; 287 + 288 + page = (filesize % LT8713SX_PAGE_SIZE) ? 289 + ((filesize / LT8713SX_PAGE_SIZE) + 1) : (filesize / LT8713SX_PAGE_SIZE); 290 + 291 + dev_dbg(lt8713sx->dev, 292 + "Writing to Sram=%u pages, total size = %llu bytes\n", page, filesize); 293 + 294 + for (num = 0; num < page; num++) { 295 + dev_dbg(lt8713sx->dev, "page[%d]\n", num); 296 + lt8713sx_i2c_to_sram(lt8713sx); 297 + 298 + for (i = 0; i < LT8713SX_PAGE_SIZE; i++) { 299 + if ((num * LT8713SX_PAGE_SIZE + i) < filesize) 300 + val = *(data + (num * LT8713SX_PAGE_SIZE + i)); 301 + else 302 + val = 0xff; 303 + regmap_write(lt8713sx->regmap, 0xe059, val); 304 + } 305 + 306 + lt8713sx_wren(lt8713sx); 307 + lt8713sx_sram_to_flash(lt8713sx); 308 + } 309 + 310 + lt8713sx_wrdi(lt8713sx); 311 + lt8713sx_disable_sram_write(lt8713sx); 312 + 313 + return 0; 314 + } 315 + 316 + static void lt8713sx_main_upgrade_result(struct lt8713sx *lt8713sx) 317 + { 318 + u32 main_crc_result; 319 + 320 + regmap_read(lt8713sx->regmap, 0xe023, &main_crc_result); 321 + 322 + dev_dbg(lt8713sx->dev, "Main CRC HW: 0x%02X\n", main_crc_result); 323 + dev_dbg(lt8713sx->dev, "Main CRC FW: 0x%02X\n", lt8713sx->main_crc_value); 324 + 325 + if (main_crc_result == lt8713sx->main_crc_value) 326 + dev_info(lt8713sx->dev, "Main Firmware Upgrade Success.\n"); 327 + else 328 + dev_err(lt8713sx->dev, "Main Firmware Upgrade Failed.\n"); 329 + } 330 + 331 + static void lt8713sx_bank_upgrade_result(struct lt8713sx *lt8713sx, u8 banknum) 332 + { 333 + u32 bank_crc_result; 334 + 335 + regmap_read(lt8713sx->regmap, 0xe023, &bank_crc_result); 336 + 337 + dev_dbg(lt8713sx->dev, "Bank %d CRC Result: 0x%02X\n", banknum, bank_crc_result); 338 + 339 + if (bank_crc_result == lt8713sx->bank_crc_value[banknum]) 340 + dev_info(lt8713sx->dev, "Bank %d Firmware Upgrade Success.\n", banknum); 341 + else 342 + dev_err(lt8713sx->dev, "Bank %d Firmware Upgrade Failed.\n", banknum); 343 + } 344 + 345 + static void lt8713sx_bank_result_check(struct lt8713sx *lt8713sx) 346 + { 347 + int i; 348 + u64 addr = 0x010000; 349 + 350 + for (i = 0; i < lt8713sx->bank_num; i++) { 351 + lt8713sx_load_bank_fw_to_sram(lt8713sx, addr); 352 + lt8713sx_bank_upgrade_result(lt8713sx, i); 353 + addr += 0x3000; 354 + } 355 + } 356 + 357 + static int lt8713sx_firmware_upgrade(struct lt8713sx *lt8713sx) 358 + { 359 + int ret; 360 + 361 + lt8713sx_config_parameters(lt8713sx); 362 + 363 + lt8713sx_block_erase(lt8713sx); 364 + 365 + if (lt8713sx->fw->size < SZ_64K) { 366 + ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, SZ_64K); 367 + if (ret < 0) { 368 + dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); 369 + return ret; 370 + } 371 + } else { 372 + ret = lt8713sx_write_data(lt8713sx, lt8713sx->fw_buffer, lt8713sx->fw->size); 373 + if (ret < 0) { 374 + dev_err(lt8713sx->dev, "Failed to write firmware data: %d\n", ret); 375 + return ret; 376 + } 377 + } 378 + dev_dbg(lt8713sx->dev, "Write Data done.\n"); 379 + 380 + return 0; 381 + } 382 + 383 + static int lt8713sx_firmware_update(struct lt8713sx *lt8713sx) 384 + { 385 + int ret = 0; 386 + 387 + guard(mutex)(&lt8713sx->ocm_lock); 388 + lt8713sx_i2c_enable(lt8713sx); 389 + 390 + ret = lt8713sx_prepare_firmware_data(lt8713sx); 391 + if (ret < 0) { 392 + dev_err(lt8713sx->dev, "Failed to prepare firmware data: %d\n", ret); 393 + goto error; 394 + } 395 + 396 + ret = lt8713sx_firmware_upgrade(lt8713sx); 397 + if (ret < 0) { 398 + dev_err(lt8713sx->dev, "Upgrade failure.\n"); 399 + goto error; 400 + } 401 + 402 + /* Validate CRC */ 403 + lt8713sx_load_main_fw_to_sram(lt8713sx); 404 + lt8713sx_main_upgrade_result(lt8713sx); 405 + lt8713sx_wrdi(lt8713sx); 406 + lt8713sx_fifo_reset(lt8713sx); 407 + lt8713sx_bank_result_check(lt8713sx); 408 + lt8713sx_wrdi(lt8713sx); 409 + 410 + error: 411 + lt8713sx_i2c_disable(lt8713sx); 412 + if (!ret) 413 + lt8713sx_reset(lt8713sx); 414 + 415 + kvfree(lt8713sx->fw_buffer); 416 + lt8713sx->fw_buffer = NULL; 417 + 418 + if (lt8713sx->fw) { 419 + release_firmware(lt8713sx->fw); 420 + lt8713sx->fw = NULL; 421 + } 422 + 423 + return ret; 424 + } 425 + 426 + static void lt8713sx_reset(struct lt8713sx *lt8713sx) 427 + { 428 + dev_dbg(lt8713sx->dev, "reset bridge.\n"); 429 + gpiod_set_value_cansleep(lt8713sx->reset_gpio, 1); 430 + msleep(20); 431 + 432 + gpiod_set_value_cansleep(lt8713sx->reset_gpio, 0); 433 + msleep(20); 434 + 435 + dev_dbg(lt8713sx->dev, "reset done.\n"); 436 + } 437 + 438 + static int lt8713sx_regulator_enable(struct lt8713sx *lt8713sx) 439 + { 440 + int ret; 441 + 442 + ret = devm_regulator_get_enable(lt8713sx->dev, "vdd"); 443 + if (ret < 0) 444 + return dev_err_probe(lt8713sx->dev, ret, "failed to enable vdd regulator\n"); 445 + 446 + usleep_range(1000, 10000); 447 + 448 + ret = devm_regulator_get_enable(lt8713sx->dev, "vcc"); 449 + if (ret < 0) 450 + return dev_err_probe(lt8713sx->dev, ret, "failed to enable vcc regulator\n"); 451 + return 0; 452 + } 453 + 454 + static int lt8713sx_bridge_attach(struct drm_bridge *bridge, 455 + struct drm_encoder *encoder, 456 + enum drm_bridge_attach_flags flags) 457 + { 458 + struct lt8713sx *lt8713sx = container_of(bridge, struct lt8713sx, bridge); 459 + 460 + return drm_bridge_attach(encoder, 461 + lt8713sx->next_bridge, 462 + bridge, flags); 463 + } 464 + 465 + static int lt8713sx_gpio_init(struct lt8713sx *lt8713sx) 466 + { 467 + struct device *dev = lt8713sx->dev; 468 + 469 + lt8713sx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 470 + if (IS_ERR(lt8713sx->reset_gpio)) 471 + return dev_err_probe(dev, PTR_ERR(lt8713sx->reset_gpio), 472 + "failed to acquire reset gpio\n"); 473 + 474 + /* power enable gpio */ 475 + lt8713sx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); 476 + if (IS_ERR(lt8713sx->enable_gpio)) 477 + return dev_err_probe(dev, PTR_ERR(lt8713sx->enable_gpio), 478 + "failed to acquire enable gpio\n"); 479 + return 0; 480 + } 481 + 482 + static ssize_t lt8713sx_firmware_store(struct device *dev, 483 + struct device_attribute *attr, 484 + const char *buf, size_t len) 485 + { 486 + struct lt8713sx *lt8713sx = dev_get_drvdata(dev); 487 + int ret; 488 + 489 + ret = lt8713sx_firmware_update(lt8713sx); 490 + if (ret < 0) 491 + return ret; 492 + return len; 493 + } 494 + 495 + static DEVICE_ATTR_WO(lt8713sx_firmware); 496 + 497 + static struct attribute *lt8713sx_attrs[] = { 498 + &dev_attr_lt8713sx_firmware.attr, 499 + NULL, 500 + }; 501 + 502 + static const struct attribute_group lt8713sx_attr_group = { 503 + .attrs = lt8713sx_attrs, 504 + }; 505 + 506 + static const struct attribute_group *lt8713sx_attr_groups[] = { 507 + &lt8713sx_attr_group, 508 + NULL, 509 + }; 510 + 511 + static const struct drm_bridge_funcs lt8713sx_bridge_funcs = { 512 + .attach = lt8713sx_bridge_attach, 513 + }; 514 + 515 + static int lt8713sx_probe(struct i2c_client *client) 516 + { 517 + struct lt8713sx *lt8713sx; 518 + struct device *dev = &client->dev; 519 + int ret; 520 + 521 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 522 + return dev_err_probe(dev, -ENODEV, "device doesn't support I2C\n"); 523 + 524 + lt8713sx = devm_drm_bridge_alloc(dev, struct lt8713sx, bridge, &lt8713sx_bridge_funcs); 525 + if (IS_ERR(lt8713sx)) 526 + return PTR_ERR(lt8713sx); 527 + 528 + lt8713sx->dev = dev; 529 + lt8713sx->client = client; 530 + i2c_set_clientdata(client, lt8713sx); 531 + 532 + ret = devm_mutex_init(lt8713sx->dev, &lt8713sx->ocm_lock); 533 + if (ret) 534 + return ret; 535 + 536 + lt8713sx->regmap = devm_regmap_init_i2c(client, &lt8713sx_regmap_config); 537 + if (IS_ERR(lt8713sx->regmap)) 538 + return dev_err_probe(dev, PTR_ERR(lt8713sx->regmap), "regmap i2c init failed\n"); 539 + 540 + ret = drm_of_find_panel_or_bridge(lt8713sx->dev->of_node, 1, -1, NULL, 541 + &lt8713sx->next_bridge); 542 + if (ret < 0) 543 + return ret; 544 + 545 + ret = lt8713sx_gpio_init(lt8713sx); 546 + if (ret < 0) 547 + return ret; 548 + 549 + ret = lt8713sx_regulator_enable(lt8713sx); 550 + if (ret) 551 + return ret; 552 + 553 + lt8713sx_reset(lt8713sx); 554 + 555 + lt8713sx->bridge.funcs = &lt8713sx_bridge_funcs; 556 + lt8713sx->bridge.of_node = dev->of_node; 557 + lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; 558 + drm_bridge_add(&lt8713sx->bridge); 559 + 560 + crc8_populate_msb(lt8713sx_crc_table, 0x31); 561 + 562 + return 0; 563 + } 564 + 565 + static void lt8713sx_remove(struct i2c_client *client) 566 + { 567 + struct lt8713sx *lt8713sx = i2c_get_clientdata(client); 568 + 569 + drm_bridge_remove(&lt8713sx->bridge); 570 + } 571 + 572 + static struct i2c_device_id lt8713sx_id[] = { 573 + { "lontium,lt8713sx", 0 }, 574 + { /* sentinel */ } 575 + }; 576 + 577 + static const struct of_device_id lt8713sx_match_table[] = { 578 + { .compatible = "lontium,lt8713sx" }, 579 + { /* sentinel */ } 580 + }; 581 + MODULE_DEVICE_TABLE(of, lt8713sx_match_table); 582 + 583 + static struct i2c_driver lt8713sx_driver = { 584 + .driver = { 585 + .name = "lt8713sx", 586 + .of_match_table = lt8713sx_match_table, 587 + .dev_groups = lt8713sx_attr_groups, 588 + }, 589 + .probe = lt8713sx_probe, 590 + .remove = lt8713sx_remove, 591 + .id_table = lt8713sx_id, 592 + }; 593 + 594 + module_i2c_driver(lt8713sx_driver); 595 + MODULE_LICENSE("GPL"); 596 + MODULE_DESCRIPTION("lt8713sx drm bridge driver"); 597 + MODULE_AUTHOR("Vishnu Saini <vishnu.saini@oss.qualcomm.com>"); 598 + MODULE_FIRMWARE(FW_FILE);
+5 -3
drivers/gpu/drm/bridge/waveshare-dsi.c
··· 66 66 dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO | 67 67 MIPI_DSI_CLOCK_NON_CONTINUOUS; 68 68 dsi->format = MIPI_DSI_FMT_RGB888; 69 - dsi->lanes = drm_of_get_data_lanes_count_ep(dev->of_node, 0, 0, 1, 4); 70 - if (dsi->lanes < 0) { 69 + ret = drm_of_get_data_lanes_count_ep(dev->of_node, 0, 0, 1, 4); 70 + if (ret < 0) { 71 71 dev_warn(dev, "Invalid or missing DSI lane count %d, falling back to 2 lanes\n", 72 - dsi->lanes); 72 + ret); 73 73 dsi->lanes = 2; /* Old DT backward compatibility */ 74 + } else { 75 + dsi->lanes = ret; 74 76 } 75 77 76 78 ret = devm_mipi_dsi_attach(dev, dsi);
+8
drivers/gpu/drm/drm_atomic.c
··· 475 475 drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask); 476 476 drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask); 477 477 drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); 478 + drm_printf(p, "\tbackground_color=%llx\n", state->background_color); 478 479 479 480 if (crtc->funcs->atomic_print_state) 480 481 crtc->funcs->atomic_print_state(p, state); ··· 1588 1587 const struct drm_crtc_state *old_crtc_state = 1589 1588 drm_atomic_get_old_crtc_state(state, crtc); 1590 1589 struct drm_plane *plane; 1590 + int ret; 1591 1591 1592 1592 WARN_ON(!drm_atomic_get_new_crtc_state(state, crtc)); 1593 1593 ··· 1602 1600 1603 1601 if (IS_ERR(plane_state)) 1604 1602 return PTR_ERR(plane_state); 1603 + 1604 + if (plane_state->color_pipeline) { 1605 + ret = drm_atomic_add_affected_colorops(state, plane); 1606 + if (ret) 1607 + return ret; 1608 + } 1605 1609 } 1606 1610 return 0; 1607 1611 }
+1
drivers/gpu/drm/drm_atomic_state_helper.c
··· 75 75 struct drm_crtc *crtc) 76 76 { 77 77 crtc_state->crtc = crtc; 78 + crtc_state->background_color = DRM_ARGB64_PREP(0xffff, 0, 0, 0); 78 79 } 79 80 EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_reset); 80 81
+4
drivers/gpu/drm/drm_atomic_uapi.c
··· 454 454 &replaced); 455 455 state->color_mgmt_changed |= replaced; 456 456 return ret; 457 + } else if (property == config->background_color_property) { 458 + state->background_color = val; 457 459 } else if (property == config->prop_out_fence_ptr) { 458 460 s32 __user *fence_ptr = u64_to_user_ptr(val); 459 461 ··· 503 501 *val = (state->ctm) ? state->ctm->base.id : 0; 504 502 else if (property == config->gamma_lut_property) 505 503 *val = (state->gamma_lut) ? state->gamma_lut->base.id : 0; 504 + else if (property == config->background_color_property) 505 + *val = state->background_color; 506 506 else if (property == config->prop_out_fence_ptr) 507 507 *val = 0; 508 508 else if (property == crtc->scaling_filter_property)
+35 -4
drivers/gpu/drm/drm_blend.c
··· 191 191 * plane does not expose the "alpha" property, then this is 192 192 * assumed to be 1.0 193 193 * 194 - * Note that all the property extensions described here apply either to the 195 - * plane or the CRTC (e.g. for the background color, which currently is not 196 - * exposed and assumed to be black). 197 - * 198 194 * SCALING_FILTER: 199 195 * Indicates scaling filter to be used for plane scaler 200 196 * ··· 203 207 * 204 208 * Drivers can set up this property for a plane by calling 205 209 * drm_plane_create_scaling_filter_property 210 + * 211 + * The property extensions described above all apply to the plane. Drivers 212 + * may also expose the following crtc property extension: 213 + * 214 + * BACKGROUND_COLOR: 215 + * Background color is set up with drm_crtc_attach_background_color_property(), 216 + * and expects a 64-bit ARGB value following DRM_FORMAT_ARGB16161616, as 217 + * generated by the DRM_ARGB64_PREP*() helpers. It controls the color of a 218 + * full-screen layer that exists below all planes. This color will be used 219 + * for pixels not covered by any plane and may also be blended with plane 220 + * contents as allowed by a plane's alpha values. 221 + * The background color defaults to black, and is assumed to be black for 222 + * drivers that do not expose this property. Although background color 223 + * isn't a plane, it is assumed that the color provided here undergoes the 224 + * CRTC degamma/CSC/gamma transformations applied after the planes blending. 225 + * Note that the color value includes an alpha channel, hence non-opaque 226 + * background color values are allowed, but since physically transparent 227 + * monitors do not (yet) exists, the final alpha value may not reach the 228 + * video sink or it may simply ignore it. 206 229 */ 207 230 208 231 /** ··· 636 621 return 0; 637 622 } 638 623 EXPORT_SYMBOL(drm_plane_create_blend_mode_property); 624 + 625 + /** 626 + * drm_crtc_attach_background_color_property - attach background color property 627 + * @crtc: drm crtc 628 + * 629 + * Attaches the background color property to @crtc. The property defaults to 630 + * solid black and will accept 64-bit ARGB values in the format generated by 631 + * DRM_ARGB64_PREP*() helpers. 632 + */ 633 + void drm_crtc_attach_background_color_property(struct drm_crtc *crtc) 634 + { 635 + drm_object_attach_property(&crtc->base, 636 + crtc->dev->mode_config.background_color_property, 637 + DRM_ARGB64_PREP(0xffff, 0, 0, 0)); 638 + } 639 + EXPORT_SYMBOL(drm_crtc_attach_background_color_property);
+19 -9
drivers/gpu/drm/drm_colorop.c
··· 171 171 list_del(&colorop->head); 172 172 config->num_colorop--; 173 173 174 - if (colorop->state && colorop->state->data) { 175 - drm_property_blob_put(colorop->state->data); 176 - colorop->state->data = NULL; 177 - } 178 - 179 - kfree(colorop->state); 174 + if (colorop->state) 175 + drm_colorop_atomic_destroy_state(colorop, colorop->state); 180 176 } 181 177 EXPORT_SYMBOL(drm_colorop_cleanup); 182 178 ··· 462 466 463 467 if (state->data) 464 468 drm_property_blob_get(state->data); 465 - 466 - state->bypass = true; 467 469 } 468 470 469 471 struct drm_colorop_state * ··· 479 485 return state; 480 486 } 481 487 488 + /** 489 + * __drm_atomic_helper_colorop_destroy_state - release colorop state 490 + * @state: colorop state object to release 491 + * 492 + * Releases all resources stored in the colorop state without actually freeing 493 + * the memory of the colorop state. This is useful for drivers that subclass the 494 + * colorop state. 495 + */ 496 + static void __drm_atomic_helper_colorop_destroy_state(struct drm_colorop_state *state) 497 + { 498 + drm_property_blob_put(state->data); 499 + } 500 + 482 501 void drm_colorop_atomic_destroy_state(struct drm_colorop *colorop, 483 502 struct drm_colorop_state *state) 484 503 { 504 + __drm_atomic_helper_colorop_destroy_state(state); 485 505 kfree(state); 486 506 } 487 507 ··· 546 538 547 539 void drm_colorop_reset(struct drm_colorop *colorop) 548 540 { 549 - kfree(colorop->state); 541 + if (colorop->state) 542 + drm_colorop_atomic_destroy_state(colorop, colorop->state); 543 + 550 544 colorop->state = kzalloc_obj(*colorop->state); 551 545 552 546 if (colorop->state)
+6
drivers/gpu/drm/drm_mode_config.c
··· 380 380 return -ENOMEM; 381 381 dev->mode_config.gamma_lut_size_property = prop; 382 382 383 + prop = drm_property_create_range(dev, 0, 384 + "BACKGROUND_COLOR", 0, U64_MAX); 385 + if (!prop) 386 + return -ENOMEM; 387 + dev->mode_config.background_color_property = prop; 388 + 383 389 prop = drm_property_create(dev, 384 390 DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB, 385 391 "IN_FORMATS", 0);
+1
drivers/gpu/drm/imx/ipuv3/Kconfig
··· 15 15 depends on DRM_IMX 16 16 select DRM_BRIDGE 17 17 select DRM_BRIDGE_CONNECTOR 18 + select DRM_DISPLAY_HELPER 18 19 select DRM_IMX_LEGACY_BRIDGE 19 20 select DRM_PANEL_BRIDGE 20 21 select VIDEOMODE_HELPERS
+11 -12
drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c
··· 890 890 { 891 891 struct ipu_plane *ipu_plane; 892 892 const uint64_t *modifiers = ipu_format_modifiers; 893 - unsigned int zpos = (type == DRM_PLANE_TYPE_PRIMARY) ? 0 : 1; 893 + unsigned int primary_zpos = 1; 894 894 unsigned int format_count; 895 895 const uint32_t *formats; 896 896 int ret; ··· 915 915 type, NULL); 916 916 if (IS_ERR(ipu_plane)) { 917 917 DRM_ERROR("failed to allocate and initialize %s plane\n", 918 - zpos ? "overlay" : "primary"); 918 + (type == DRM_PLANE_TYPE_PRIMARY) ? "primary" : "overlay"); 919 919 return ipu_plane; 920 920 } 921 921 ··· 923 923 ipu_plane->dma = dma; 924 924 ipu_plane->dp_flow = dp; 925 925 926 - if (type == DRM_PLANE_TYPE_PRIMARY) 926 + if (type == DRM_PLANE_TYPE_PRIMARY) { 927 927 drm_plane_helper_add(&ipu_plane->base, &ipu_primary_plane_helper_funcs); 928 - else 929 - drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs); 930 - 931 - if (dp == IPU_DP_FLOW_SYNC_BG || dp == IPU_DP_FLOW_SYNC_FG) 932 - ret = drm_plane_create_zpos_property(&ipu_plane->base, zpos, 0, 933 - 1); 934 - else 935 928 ret = drm_plane_create_zpos_immutable_property(&ipu_plane->base, 936 - 0); 929 + primary_zpos); 930 + } else { 931 + drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs); 932 + ret = drm_plane_create_zpos_property(&ipu_plane->base, 933 + primary_zpos + 1, 0, 934 + primary_zpos + 1); 935 + } 937 936 if (ret) 938 937 return ERR_PTR(ret); 939 938 ··· 948 949 ret = ipu_plane_get_resources(dev, ipu_plane); 949 950 if (ret) { 950 951 DRM_ERROR("failed to get %s plane resources: %pe\n", 951 - zpos ? "overlay" : "primary", &ret); 952 + (type == DRM_PLANE_TYPE_PRIMARY) ? "primary" : "overlay", &ret); 952 953 return ERR_PTR(ret); 953 954 } 954 955
+12 -3
drivers/gpu/drm/imx/ipuv3/parallel-display.c
··· 110 110 output_fmt = imxpd->bus_format ? : MEDIA_BUS_FMT_RGB888_1X24; 111 111 112 112 /* Now make sure the requested output format is supported. */ 113 - if ((imxpd->bus_format && imxpd->bus_format != output_fmt) || 114 - !imx_pd_format_supported(output_fmt)) { 113 + if (!imx_pd_format_supported(output_fmt)) { 115 114 *num_input_fmts = 0; 116 115 return NULL; 117 116 } ··· 120 121 if (!input_fmts) 121 122 return NULL; 122 123 123 - input_fmts[0] = output_fmt; 124 + /* 125 + * Prefer bus format set via legacy "interface-pix-fmt" DT property 126 + * over panel bus format. This is necessary to retain support for 127 + * DTs which configure the IPUv3 parallel output as 24bit, but 128 + * connect 18bit DPI panels to it with hardware swizzling. 129 + */ 130 + if (imxpd->bus_format && imxpd->bus_format != output_fmt) 131 + input_fmts[0] = imxpd->bus_format; 132 + else 133 + input_fmts[0] = output_fmt; 134 + 124 135 return input_fmts; 125 136 } 126 137
+3
drivers/gpu/drm/panel/panel-edp.c
··· 2025 2025 EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"), 2026 2026 EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d45, &delay_200_500_e80, "NV116WHM-N4B"), 2027 2027 EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d73, &delay_200_500_e80, "NE140WUM-N6S"), 2028 + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0db3, &delay_200_500_e80, "NV153WUM-N42"), 2028 2029 EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ddf, &delay_200_500_e80, "NV116WHM-T01"), 2029 2030 2030 2031 EDP_PANEL_ENTRY('C', 'M', 'N', 0x1130, &delay_200_500_e50, "N116BGE-EB2"), ··· 2062 2061 EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d6, &delay_200_500_e80_d50, "N140BGA-EA4"), 2063 2062 EDP_PANEL_ENTRY('C', 'M', 'N', 0x14e5, &delay_200_500_e80_d50, "N140HGA-EA1"), 2064 2063 EDP_PANEL_ENTRY('C', 'M', 'N', 0x1565, &delay_200_500_e80, "N156HCA-EAB"), 2064 + EDP_PANEL_ENTRY('C', 'M', 'N', 0x156b, &delay_200_500_e80_d50, "N153JCA-ELK"), 2065 2065 EDP_PANEL_ENTRY('C', 'M', 'N', 0x162b, &delay_200_500_e80_d50, "N160JCE-ELL"), 2066 2066 EDP_PANEL_ENTRY('C', 'M', 'N', 0x7402, &delay_200_500_e200_d50, "N116BCA-EAK"), 2067 2067 ··· 2081 2079 EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"), 2082 2080 EDP_PANEL_ENTRY('C', 'S', 'W', 0x147c, &delay_200_500_e50_d100, "MNE007QB3-1"), 2083 2081 EDP_PANEL_ENTRY('C', 'S', 'W', 0x1519, &delay_200_500_e80_d50, "MNF601BS1-3"), 2082 + EDP_PANEL_ENTRY('C', 'S', 'W', 0x1529, &delay_200_500_e80_d50, "MNF307QS3-2"), 2084 2083 2085 2084 EDP_PANEL_ENTRY('E', 'T', 'C', 0x0000, &delay_50_500_e200_d200_po2e335, "LP079QX1-SP0V"), 2086 2085
+30 -64
drivers/gpu/drm/panel/panel-novatek-nt36672a.c
··· 79 79 return container_of(panel, struct nt36672a_panel, base); 80 80 } 81 81 82 - static int nt36672a_send_cmds(struct drm_panel *panel, const struct nt36672a_panel_cmd *cmds, 83 - int num) 82 + static void nt36672a_send_cmds(struct mipi_dsi_multi_context *dsi_ctx, 83 + const struct nt36672a_panel_cmd *cmds, int num) 84 84 { 85 - struct nt36672a_panel *pinfo = to_nt36672a_panel(panel); 86 85 unsigned int i; 87 - int err; 88 86 89 87 for (i = 0; i < num; i++) { 90 88 const struct nt36672a_panel_cmd *cmd = &cmds[i]; 91 89 92 - err = mipi_dsi_dcs_write(pinfo->link, cmd->data[0], cmd->data + 1, 1); 93 - 94 - if (err < 0) 95 - return err; 90 + /* cmd->data[0] is the DCS command, cmd->data[1] is the parameter */ 91 + mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd->data, sizeof(cmd->data)); 96 92 } 97 - 98 - return 0; 99 93 } 100 94 101 - static int nt36672a_panel_power_off(struct drm_panel *panel) 95 + static void nt36672a_panel_power_off(struct drm_panel *panel) 102 96 { 103 97 struct nt36672a_panel *pinfo = to_nt36672a_panel(panel); 104 - int ret = 0; 98 + int ret; 105 99 106 100 gpiod_set_value(pinfo->reset_gpio, 1); 107 101 108 102 ret = regulator_bulk_disable(ARRAY_SIZE(pinfo->supplies), pinfo->supplies); 109 103 if (ret) 110 104 dev_err(panel->dev, "regulator_bulk_disable failed %d\n", ret); 111 - 112 - return ret; 113 105 } 114 106 115 107 static int nt36672a_panel_unprepare(struct drm_panel *panel) 116 108 { 117 109 struct nt36672a_panel *pinfo = to_nt36672a_panel(panel); 118 - int ret; 110 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->link }; 119 111 120 112 /* send off cmds */ 121 - ret = nt36672a_send_cmds(panel, pinfo->desc->off_cmds, 122 - pinfo->desc->num_off_cmds); 113 + nt36672a_send_cmds(&dsi_ctx, pinfo->desc->off_cmds, 114 + pinfo->desc->num_off_cmds); 123 115 124 - if (ret < 0) 125 - dev_err(panel->dev, "failed to send DCS off cmds: %d\n", ret); 126 - 127 - ret = mipi_dsi_dcs_set_display_off(pinfo->link); 128 - if (ret < 0) 129 - dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret); 116 + /* Reset error to continue with display off even if send_cmds failed */ 117 + dsi_ctx.accum_err = 0; 118 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 119 + /* Reset error to continue power-down even if display off failed */ 120 + dsi_ctx.accum_err = 0; 130 121 131 122 /* 120ms delay required here as per DCS spec */ 132 123 msleep(120); 133 124 134 - ret = mipi_dsi_dcs_enter_sleep_mode(pinfo->link); 135 - if (ret < 0) 136 - dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret); 125 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 137 126 138 127 /* 0x3C = 60ms delay */ 139 128 msleep(60); 140 129 141 - ret = nt36672a_panel_power_off(panel); 142 - if (ret < 0) 143 - dev_err(panel->dev, "power_off failed ret = %d\n", ret); 130 + nt36672a_panel_power_off(panel); 144 131 145 - return ret; 132 + return 0; 146 133 } 147 134 148 135 static int nt36672a_panel_power_on(struct nt36672a_panel *pinfo) ··· 157 170 static int nt36672a_panel_prepare(struct drm_panel *panel) 158 171 { 159 172 struct nt36672a_panel *pinfo = to_nt36672a_panel(panel); 160 - int err; 173 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->link }; 161 174 162 - err = nt36672a_panel_power_on(pinfo); 163 - if (err < 0) 164 - goto poweroff; 175 + dsi_ctx.accum_err = nt36672a_panel_power_on(pinfo); 165 176 166 177 /* send first part of init cmds */ 167 - err = nt36672a_send_cmds(panel, pinfo->desc->on_cmds_1, 168 - pinfo->desc->num_on_cmds_1); 178 + nt36672a_send_cmds(&dsi_ctx, pinfo->desc->on_cmds_1, 179 + pinfo->desc->num_on_cmds_1); 169 180 170 - if (err < 0) { 171 - dev_err(panel->dev, "failed to send DCS Init 1st Code: %d\n", err); 172 - goto poweroff; 173 - } 174 - 175 - err = mipi_dsi_dcs_exit_sleep_mode(pinfo->link); 176 - if (err < 0) { 177 - dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); 178 - goto poweroff; 179 - } 181 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 180 182 181 183 /* 0x46 = 70 ms delay */ 182 - msleep(70); 184 + mipi_dsi_msleep(&dsi_ctx, 70); 183 185 184 - err = mipi_dsi_dcs_set_display_on(pinfo->link); 185 - if (err < 0) { 186 - dev_err(panel->dev, "failed to Set Display ON: %d\n", err); 187 - goto poweroff; 188 - } 186 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 189 187 190 188 /* Send rest of the init cmds */ 191 - err = nt36672a_send_cmds(panel, pinfo->desc->on_cmds_2, 192 - pinfo->desc->num_on_cmds_2); 189 + nt36672a_send_cmds(&dsi_ctx, pinfo->desc->on_cmds_2, 190 + pinfo->desc->num_on_cmds_2); 193 191 194 - if (err < 0) { 195 - dev_err(panel->dev, "failed to send DCS Init 2nd Code: %d\n", err); 196 - goto poweroff; 197 - } 192 + mipi_dsi_msleep(&dsi_ctx, 120); 198 193 199 - msleep(120); 194 + if (dsi_ctx.accum_err < 0) 195 + gpiod_set_value(pinfo->reset_gpio, 0); 200 196 201 - return 0; 202 - 203 - poweroff: 204 - gpiod_set_value(pinfo->reset_gpio, 0); 205 - return err; 197 + return dsi_ctx.accum_err; 206 198 } 207 199 208 200 static int nt36672a_panel_get_modes(struct drm_panel *panel,
+1 -2
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
··· 808 808 * commands payload not being saved to memory. 809 809 */ 810 810 dsisetr = rzg2l_mipi_dsi_link_read(dsi, DSISETR); 811 - dsisetr &= ~DSISETR_MRPSZ; 812 - dsisetr |= FIELD_PREP(DSISETR_MRPSZ, RZG2L_DCS_BUF_SIZE); 811 + FIELD_MODIFY(DSISETR_MRPSZ, &dsisetr, RZG2L_DCS_BUF_SIZE); 813 812 rzg2l_mipi_dsi_link_write(dsi, DSISETR, dsisetr); 814 813 815 814 return 0;
+23 -1
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
··· 1080 1080 return -EINVAL; 1081 1081 } 1082 1082 1083 + if ((cstate->background_color << 16) && 1084 + (fb->format->has_alpha || pstate->alpha != 0xffff)) { 1085 + drm_dbg_kms(vop2->drm, 1086 + "Alpha-blending with background color is unsupported\n"); 1087 + return -EINVAL; 1088 + } 1089 + 1083 1090 return 0; 1084 1091 } 1085 1092 ··· 1559 1552 struct vop2_video_port *vp = to_vop2_video_port(crtc); 1560 1553 struct vop2 *vop2 = vp->vop2; 1561 1554 struct drm_display_mode *mode = &crtc->state->adjusted_mode; 1555 + u64 bgcolor = crtc->state->background_color; 1562 1556 u16 vtotal = mode->crtc_vtotal; 1563 1557 u16 hdisplay = mode->crtc_hdisplay; 1564 1558 u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; ··· 1605 1597 vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val); 1606 1598 } 1607 1599 1608 - vop2_vp_write(vp, RK3568_VP_DSP_BG, 0); 1600 + /* 1601 + * Background color is programmed with 10 bits of precision. 1602 + * Since performance is more important than accuracy here, 1603 + * make use of the DRM_ARGB64_GET*_BPCS() helpers. 1604 + */ 1605 + val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, DRM_ARGB64_GETR_BPCS(bgcolor, 10)); 1606 + FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, DRM_ARGB64_GETG_BPCS(bgcolor, 10)); 1607 + FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, DRM_ARGB64_GETB_BPCS(bgcolor, 10)); 1608 + vop2_vp_write(vp, RK3568_VP_DSP_BG, val); 1609 1609 } 1610 1610 1611 1611 static int us_to_vertical_line(struct drm_display_mode *mode, int us) ··· 1999 1983 drm_get_bus_format_name(vcstate->bus_format)); 2000 1984 seq_printf(s, "\toutput_mode[%x]", vcstate->output_mode); 2001 1985 seq_printf(s, " color_space[%d]\n", vcstate->color_space); 1986 + seq_printf(s, "\tbackground color (10bpc): r=0x%x g=0x%x b=0x%x\n", 1987 + DRM_ARGB64_GETR_BPCS(cstate->background_color, 10), 1988 + DRM_ARGB64_GETG_BPCS(cstate->background_color, 10), 1989 + DRM_ARGB64_GETB_BPCS(cstate->background_color, 10)); 2002 1990 seq_printf(s, " Display mode: %dx%d%s%d\n", 2003 1991 mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p", 2004 1992 drm_mode_vrefresh(mode)); ··· 2490 2470 if (ret) 2491 2471 return dev_err_probe(drm->dev, ret, 2492 2472 "crtc init for video_port%d failed\n", i); 2473 + 2474 + drm_crtc_attach_background_color_property(&vp->crtc); 2493 2475 2494 2476 drm_crtc_helper_add(&vp->crtc, &vop2_crtc_helper_funcs); 2495 2477 if (vop2->lut_regs) {
+4
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
··· 658 658 #define RK3588_VP_CLK_CTRL__DCLK_OUT_DIV GENMASK(3, 2) 659 659 #define RK3588_VP_CLK_CTRL__DCLK_CORE_DIV GENMASK(1, 0) 660 660 661 + #define RK3568_VP_DSP_BG__DSP_BG_RED GENMASK(29, 20) 662 + #define RK3568_VP_DSP_BG__DSP_BG_GREEN GENMASK(19, 10) 663 + #define RK3568_VP_DSP_BG__DSP_BG_BLUE GENMASK(9, 0) 664 + 661 665 #define RK3568_VP_POST_SCL_CTRL__VSCALEDOWN BIT(1) 662 666 #define RK3568_VP_POST_SCL_CTRL__HSCALEDOWN BIT(0) 663 667
+2 -1
drivers/gpu/drm/sun4i/sun4i_backend.c
··· 881 881 &sun4i_backend_regmap_config); 882 882 if (IS_ERR(backend->engine.regs)) { 883 883 dev_err(dev, "Couldn't create the backend regmap\n"); 884 - return PTR_ERR(backend->engine.regs); 884 + ret = PTR_ERR(backend->engine.regs); 885 + goto err_disable_ram_clk; 885 886 } 886 887 887 888 list_add_tail(&backend->engine.list, &drv->engine_list);
+2 -14
drivers/gpu/drm/sun4i/sun8i_ui_layer.c
··· 124 124 { 125 125 struct drm_plane_state *state = plane->state; 126 126 struct drm_framebuffer *fb = state->fb; 127 - struct drm_gem_dma_object *gem; 128 127 dma_addr_t dma_addr; 129 128 u32 ch_base; 130 - int bpp; 131 129 132 130 ch_base = sun8i_channel_base(layer); 133 131 134 - /* Get the physical address of the buffer in memory */ 135 - gem = drm_fb_dma_get_gem_obj(fb, 0); 136 - 137 - DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); 138 - 139 - /* Compute the start of the displayed memory */ 140 - bpp = fb->format->cpp[0]; 141 - dma_addr = gem->dma_addr + fb->offsets[0]; 142 - 143 - /* Fixup framebuffer address for src coordinates */ 144 - dma_addr += (state->src.x1 >> 16) * bpp; 145 - dma_addr += (state->src.y1 >> 16) * fb->pitches[0]; 132 + /* Get the start of the displayed memory */ 133 + dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0); 146 134 147 135 /* Set the line width */ 148 136 DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
+2 -25
drivers/gpu/drm/sun4i/sun8i_vi_layer.c
··· 197 197 struct drm_plane_state *state = plane->state; 198 198 struct drm_framebuffer *fb = state->fb; 199 199 const struct drm_format_info *format = fb->format; 200 - struct drm_gem_dma_object *gem; 201 - u32 dx, dy, src_x, src_y; 202 200 dma_addr_t dma_addr; 203 201 u32 ch_base; 204 202 int i; 205 203 206 204 ch_base = sun8i_channel_base(layer); 207 205 208 - /* Adjust x and y to be dividable by subsampling factor */ 209 - src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); 210 - src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); 211 - 212 206 for (i = 0; i < format->num_planes; i++) { 213 - /* Get the physical address of the buffer in memory */ 214 - gem = drm_fb_dma_get_gem_obj(fb, i); 215 - 216 - DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); 217 - 218 - /* Compute the start of the displayed memory */ 219 - dma_addr = gem->dma_addr + fb->offsets[i]; 220 - 221 - dx = src_x; 222 - dy = src_y; 223 - 224 - if (i > 0) { 225 - dx /= format->hsub; 226 - dy /= format->vsub; 227 - } 228 - 229 - /* Fixup framebuffer address for src coordinates */ 230 - dma_addr += dx * format->cpp[i]; 231 - dma_addr += dy * fb->pitches[i]; 207 + /* Get the start of the displayed memory */ 208 + dma_addr = drm_fb_dma_get_gem_addr(fb, state, i); 232 209 233 210 /* Set the line width */ 234 211 DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
-408
drivers/gpu/drm/tilcdc/tilcdc_panel.c
··· 1 - // SPDX-License-Identifier: GPL-2.0-only 2 - /* 3 - * Copyright (C) 2012 Texas Instruments 4 - * Author: Rob Clark <robdclark@gmail.com> 5 - */ 6 - 7 - #include <linux/backlight.h> 8 - #include <linux/gpio/consumer.h> 9 - #include <linux/platform_device.h> 10 - 11 - #include <video/display_timing.h> 12 - #include <video/of_display_timing.h> 13 - #include <video/videomode.h> 14 - 15 - #include <drm/drm_atomic_state_helper.h> 16 - #include <drm/drm_connector.h> 17 - #include <drm/drm_modeset_helper_vtables.h> 18 - #include <drm/drm_probe_helper.h> 19 - #include <drm/drm_simple_kms_helper.h> 20 - 21 - #include "tilcdc_drv.h" 22 - #include "tilcdc_panel.h" 23 - 24 - struct panel_module { 25 - struct tilcdc_module base; 26 - struct tilcdc_panel_info *info; 27 - struct display_timings *timings; 28 - struct backlight_device *backlight; 29 - struct gpio_desc *enable_gpio; 30 - }; 31 - #define to_panel_module(x) container_of(x, struct panel_module, base) 32 - 33 - 34 - /* 35 - * Encoder: 36 - */ 37 - 38 - struct panel_encoder { 39 - struct drm_encoder base; 40 - struct panel_module *mod; 41 - }; 42 - #define to_panel_encoder(x) container_of(x, struct panel_encoder, base) 43 - 44 - static void panel_encoder_dpms(struct drm_encoder *encoder, int mode) 45 - { 46 - struct panel_encoder *panel_encoder = to_panel_encoder(encoder); 47 - struct backlight_device *backlight = panel_encoder->mod->backlight; 48 - struct gpio_desc *gpio = panel_encoder->mod->enable_gpio; 49 - 50 - if (backlight) { 51 - backlight->props.power = mode == DRM_MODE_DPMS_ON ? 52 - BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF; 53 - backlight_update_status(backlight); 54 - } 55 - 56 - if (gpio) 57 - gpiod_set_value_cansleep(gpio, 58 - mode == DRM_MODE_DPMS_ON ? 1 : 0); 59 - } 60 - 61 - static void panel_encoder_prepare(struct drm_encoder *encoder) 62 - { 63 - panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); 64 - } 65 - 66 - static void panel_encoder_commit(struct drm_encoder *encoder) 67 - { 68 - panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON); 69 - } 70 - 71 - static void panel_encoder_mode_set(struct drm_encoder *encoder, 72 - struct drm_display_mode *mode, 73 - struct drm_display_mode *adjusted_mode) 74 - { 75 - /* nothing needed */ 76 - } 77 - 78 - static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = { 79 - .dpms = panel_encoder_dpms, 80 - .prepare = panel_encoder_prepare, 81 - .commit = panel_encoder_commit, 82 - .mode_set = panel_encoder_mode_set, 83 - }; 84 - 85 - static struct drm_encoder *panel_encoder_create(struct drm_device *dev, 86 - struct panel_module *mod) 87 - { 88 - struct panel_encoder *panel_encoder; 89 - struct drm_encoder *encoder; 90 - int ret; 91 - 92 - panel_encoder = devm_kzalloc(dev->dev, sizeof(*panel_encoder), 93 - GFP_KERNEL); 94 - if (!panel_encoder) 95 - return NULL; 96 - 97 - panel_encoder->mod = mod; 98 - 99 - encoder = &panel_encoder->base; 100 - encoder->possible_crtcs = 1; 101 - 102 - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); 103 - if (ret < 0) 104 - goto fail; 105 - 106 - drm_encoder_helper_add(encoder, &panel_encoder_helper_funcs); 107 - 108 - return encoder; 109 - 110 - fail: 111 - drm_encoder_cleanup(encoder); 112 - return NULL; 113 - } 114 - 115 - /* 116 - * Connector: 117 - */ 118 - 119 - struct panel_connector { 120 - struct drm_connector base; 121 - 122 - struct drm_encoder *encoder; /* our connected encoder */ 123 - struct panel_module *mod; 124 - }; 125 - #define to_panel_connector(x) container_of(x, struct panel_connector, base) 126 - 127 - 128 - static void panel_connector_destroy(struct drm_connector *connector) 129 - { 130 - drm_connector_unregister(connector); 131 - drm_connector_cleanup(connector); 132 - } 133 - 134 - static int panel_connector_get_modes(struct drm_connector *connector) 135 - { 136 - struct drm_device *dev = connector->dev; 137 - struct panel_connector *panel_connector = to_panel_connector(connector); 138 - struct display_timings *timings = panel_connector->mod->timings; 139 - int i; 140 - 141 - for (i = 0; i < timings->num_timings; i++) { 142 - struct drm_display_mode *mode; 143 - struct videomode vm; 144 - 145 - if (videomode_from_timings(timings, &vm, i)) 146 - break; 147 - 148 - mode = drm_mode_create(dev); 149 - if (!mode) 150 - break; 151 - 152 - drm_display_mode_from_videomode(&vm, mode); 153 - 154 - mode->type = DRM_MODE_TYPE_DRIVER; 155 - 156 - if (timings->native_mode == i) 157 - mode->type |= DRM_MODE_TYPE_PREFERRED; 158 - 159 - drm_mode_set_name(mode); 160 - drm_mode_probed_add(connector, mode); 161 - } 162 - 163 - return i; 164 - } 165 - 166 - static struct drm_encoder *panel_connector_best_encoder( 167 - struct drm_connector *connector) 168 - { 169 - struct panel_connector *panel_connector = to_panel_connector(connector); 170 - return panel_connector->encoder; 171 - } 172 - 173 - static const struct drm_connector_funcs panel_connector_funcs = { 174 - .destroy = panel_connector_destroy, 175 - .fill_modes = drm_helper_probe_single_connector_modes, 176 - .reset = drm_atomic_helper_connector_reset, 177 - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 178 - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 179 - }; 180 - 181 - static const struct drm_connector_helper_funcs panel_connector_helper_funcs = { 182 - .get_modes = panel_connector_get_modes, 183 - .best_encoder = panel_connector_best_encoder, 184 - }; 185 - 186 - static struct drm_connector *panel_connector_create(struct drm_device *dev, 187 - struct panel_module *mod, struct drm_encoder *encoder) 188 - { 189 - struct panel_connector *panel_connector; 190 - struct drm_connector *connector; 191 - int ret; 192 - 193 - panel_connector = devm_kzalloc(dev->dev, sizeof(*panel_connector), 194 - GFP_KERNEL); 195 - if (!panel_connector) 196 - return NULL; 197 - 198 - panel_connector->encoder = encoder; 199 - panel_connector->mod = mod; 200 - 201 - connector = &panel_connector->base; 202 - 203 - drm_connector_init(dev, connector, &panel_connector_funcs, 204 - DRM_MODE_CONNECTOR_LVDS); 205 - drm_connector_helper_add(connector, &panel_connector_helper_funcs); 206 - 207 - connector->interlace_allowed = 0; 208 - connector->doublescan_allowed = 0; 209 - 210 - ret = drm_connector_attach_encoder(connector, encoder); 211 - if (ret) 212 - goto fail; 213 - 214 - return connector; 215 - 216 - fail: 217 - panel_connector_destroy(connector); 218 - return NULL; 219 - } 220 - 221 - /* 222 - * Module: 223 - */ 224 - 225 - static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) 226 - { 227 - struct panel_module *panel_mod = to_panel_module(mod); 228 - struct tilcdc_drm_private *priv = dev->dev_private; 229 - struct drm_encoder *encoder; 230 - struct drm_connector *connector; 231 - 232 - encoder = panel_encoder_create(dev, panel_mod); 233 - if (!encoder) 234 - return -ENOMEM; 235 - 236 - connector = panel_connector_create(dev, panel_mod, encoder); 237 - if (!connector) 238 - return -ENOMEM; 239 - 240 - priv->encoders[priv->num_encoders++] = encoder; 241 - priv->connectors[priv->num_connectors++] = connector; 242 - 243 - tilcdc_crtc_set_panel_info(priv->crtc, 244 - to_panel_encoder(encoder)->mod->info); 245 - 246 - return 0; 247 - } 248 - 249 - static const struct tilcdc_module_ops panel_module_ops = { 250 - .modeset_init = panel_modeset_init, 251 - }; 252 - 253 - /* 254 - * Device: 255 - */ 256 - 257 - /* maybe move this somewhere common if it is needed by other outputs? */ 258 - static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np) 259 - { 260 - struct device_node *info_np; 261 - struct tilcdc_panel_info *info; 262 - int ret = 0; 263 - 264 - if (!np) { 265 - pr_err("%s: no devicenode given\n", __func__); 266 - return NULL; 267 - } 268 - 269 - info_np = of_get_child_by_name(np, "panel-info"); 270 - if (!info_np) { 271 - pr_err("%s: could not find panel-info node\n", __func__); 272 - return NULL; 273 - } 274 - 275 - info = kzalloc_obj(*info); 276 - if (!info) 277 - goto put_node; 278 - 279 - ret |= of_property_read_u32(info_np, "ac-bias", &info->ac_bias); 280 - ret |= of_property_read_u32(info_np, "ac-bias-intrpt", &info->ac_bias_intrpt); 281 - ret |= of_property_read_u32(info_np, "dma-burst-sz", &info->dma_burst_sz); 282 - ret |= of_property_read_u32(info_np, "bpp", &info->bpp); 283 - ret |= of_property_read_u32(info_np, "fdd", &info->fdd); 284 - ret |= of_property_read_u32(info_np, "sync-edge", &info->sync_edge); 285 - ret |= of_property_read_u32(info_np, "sync-ctrl", &info->sync_ctrl); 286 - ret |= of_property_read_u32(info_np, "raster-order", &info->raster_order); 287 - ret |= of_property_read_u32(info_np, "fifo-th", &info->fifo_th); 288 - 289 - /* optional: */ 290 - info->tft_alt_mode = of_property_read_bool(info_np, "tft-alt-mode"); 291 - info->invert_pxl_clk = of_property_read_bool(info_np, "invert-pxl-clk"); 292 - 293 - if (ret) { 294 - pr_err("%s: error reading panel-info properties\n", __func__); 295 - kfree(info); 296 - info = NULL; 297 - } 298 - 299 - put_node: 300 - of_node_put(info_np); 301 - return info; 302 - } 303 - 304 - static int panel_probe(struct platform_device *pdev) 305 - { 306 - struct device_node *node = pdev->dev.of_node; 307 - struct backlight_device *backlight; 308 - struct panel_module *panel_mod; 309 - struct tilcdc_module *mod; 310 - int ret; 311 - 312 - /* bail out early if no DT data: */ 313 - if (!node) { 314 - dev_err(&pdev->dev, "device-tree data is missing\n"); 315 - return -ENXIO; 316 - } 317 - 318 - panel_mod = devm_kzalloc(&pdev->dev, sizeof(*panel_mod), GFP_KERNEL); 319 - if (!panel_mod) 320 - return -ENOMEM; 321 - 322 - backlight = devm_of_find_backlight(&pdev->dev); 323 - if (IS_ERR(backlight)) 324 - return PTR_ERR(backlight); 325 - panel_mod->backlight = backlight; 326 - 327 - panel_mod->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", 328 - GPIOD_OUT_LOW); 329 - if (IS_ERR(panel_mod->enable_gpio)) { 330 - ret = PTR_ERR(panel_mod->enable_gpio); 331 - dev_err(&pdev->dev, "failed to request enable GPIO\n"); 332 - goto fail_backlight; 333 - } 334 - 335 - if (panel_mod->enable_gpio) 336 - dev_info(&pdev->dev, "found enable GPIO\n"); 337 - 338 - mod = &panel_mod->base; 339 - pdev->dev.platform_data = mod; 340 - 341 - tilcdc_module_init(mod, "panel", &panel_module_ops); 342 - 343 - panel_mod->timings = of_get_display_timings(node); 344 - if (!panel_mod->timings) { 345 - dev_err(&pdev->dev, "could not get panel timings\n"); 346 - ret = -EINVAL; 347 - goto fail_free; 348 - } 349 - 350 - panel_mod->info = of_get_panel_info(node); 351 - if (!panel_mod->info) { 352 - dev_err(&pdev->dev, "could not get panel info\n"); 353 - ret = -EINVAL; 354 - goto fail_timings; 355 - } 356 - 357 - return 0; 358 - 359 - fail_timings: 360 - display_timings_release(panel_mod->timings); 361 - 362 - fail_free: 363 - tilcdc_module_cleanup(mod); 364 - 365 - fail_backlight: 366 - if (panel_mod->backlight) 367 - put_device(&panel_mod->backlight->dev); 368 - return ret; 369 - } 370 - 371 - static void panel_remove(struct platform_device *pdev) 372 - { 373 - struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); 374 - struct panel_module *panel_mod = to_panel_module(mod); 375 - struct backlight_device *backlight = panel_mod->backlight; 376 - 377 - if (backlight) 378 - put_device(&backlight->dev); 379 - 380 - display_timings_release(panel_mod->timings); 381 - 382 - tilcdc_module_cleanup(mod); 383 - kfree(panel_mod->info); 384 - } 385 - 386 - static const struct of_device_id panel_of_match[] = { 387 - { .compatible = "ti,tilcdc,panel", }, 388 - { }, 389 - }; 390 - 391 - static struct platform_driver panel_driver = { 392 - .probe = panel_probe, 393 - .remove = panel_remove, 394 - .driver = { 395 - .name = "tilcdc-panel", 396 - .of_match_table = panel_of_match, 397 - }, 398 - }; 399 - 400 - int __init tilcdc_panel_init(void) 401 - { 402 - return platform_driver_register(&panel_driver); 403 - } 404 - 405 - void __exit tilcdc_panel_fini(void) 406 - { 407 - platform_driver_unregister(&panel_driver); 408 - }
+2 -2
drivers/gpu/drm/tilcdc/tilcdc_panel_legacy.c
··· 105 105 106 106 if (!invert_pxl_clk) { 107 107 ret = tilcdc_panel_update_prop(&ocs, new_timing, "pixelclk-active", 108 - &(u32){cpu_to_be32(1)}, sizeof(u32)); 108 + &(__be32){cpu_to_be32(1)}, sizeof(__be32)); 109 109 if (ret) 110 110 goto destroy_ocs; 111 111 } 112 112 113 113 if (!sync_edge) { 114 114 ret = tilcdc_panel_update_prop(&ocs, new_timing, "syncclk-active", 115 - &(u32){cpu_to_be32(1)}, sizeof(u32)); 115 + &(__be32){cpu_to_be32(1)}, sizeof(__be32)); 116 116 if (ret) 117 117 goto destroy_ocs; 118 118 }
+41
drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
··· 759 759 ttm_mock_manager_fini(priv->ttm_dev, snd_mem); 760 760 } 761 761 762 + static void ttm_bo_validate_swapout(struct kunit *test) 763 + { 764 + u32 mem_type = TTM_PL_TT; 765 + struct ttm_test_devices *priv = test->priv; 766 + struct ttm_operation_ctx ctx_init = { }; 767 + enum ttm_bo_type bo_type = ttm_bo_type_device; 768 + struct ttm_resource_manager *man; 769 + struct ttm_placement *placement; 770 + struct ttm_buffer_object *bo; 771 + struct ttm_place *place; 772 + int err; 773 + s64 swapped; 774 + 775 + ttm_mock_manager_init(priv->ttm_dev, mem_type, MANAGER_SIZE); 776 + man = ttm_manager_type(priv->ttm_dev, mem_type); 777 + KUNIT_ASSERT_NOT_NULL(test, man); 778 + 779 + place = ttm_place_kunit_init(test, mem_type, 0); 780 + placement = ttm_placement_kunit_init(test, place, 1); 781 + 782 + bo = kunit_kzalloc(test, sizeof(*bo), GFP_KERNEL); 783 + KUNIT_ASSERT_NOT_NULL(test, bo); 784 + 785 + drm_gem_private_object_init(priv->drm, &bo->base, MANAGER_SIZE); 786 + err = ttm_bo_init_reserved(priv->ttm_dev, bo, bo_type, placement, 787 + PAGE_SIZE, &ctx_init, NULL, NULL, 788 + &dummy_ttm_bo_destroy); 789 + KUNIT_EXPECT_EQ(test, err, 0); 790 + dma_resv_unlock(bo->base.resv); 791 + 792 + swapped = ttm_bo_swapout(priv->ttm_dev, &ctx_init, man, GFP_KERNEL, 793 + MANAGER_SIZE / PAGE_SIZE); 794 + KUNIT_EXPECT_EQ(test, swapped, MANAGER_SIZE / PAGE_SIZE); 795 + KUNIT_EXPECT_EQ(test, bo->resource->mem_type, TTM_PL_SYSTEM); 796 + KUNIT_EXPECT_TRUE(test, bo->ttm->page_flags & TTM_TT_FLAG_SWAPPED); 797 + 798 + ttm_bo_fini(bo); 799 + ttm_mock_manager_fini(priv->ttm_dev, mem_type); 800 + } 801 + 762 802 static void ttm_bo_validate_happy_evict(struct kunit *test) 763 803 { 764 804 u32 mem_type = TTM_PL_VRAM, mem_multihop = TTM_PL_TT, ··· 1193 1153 KUNIT_CASE(ttm_bo_validate_move_fence_signaled), 1194 1154 KUNIT_CASE_PARAM(ttm_bo_validate_move_fence_not_signaled, 1195 1155 ttm_bo_validate_wait_gen_params), 1156 + KUNIT_CASE(ttm_bo_validate_swapout), 1196 1157 KUNIT_CASE(ttm_bo_validate_happy_evict), 1197 1158 KUNIT_CASE(ttm_bo_validate_all_pinned_evict), 1198 1159 KUNIT_CASE(ttm_bo_validate_allowed_only_evict),
+4 -2
drivers/gpu/drm/ttm/ttm_bo.c
··· 32 32 #define pr_fmt(fmt) "[TTM] " fmt 33 33 34 34 #include <drm/drm_print.h> 35 + #include <drm/drm_util.h> 35 36 #include <drm/ttm/ttm_allocation.h> 36 37 #include <drm/ttm/ttm_bo.h> 37 38 #include <drm/ttm/ttm_placement.h> ··· 1209 1208 * @man: The resource manager whose resources / buffer objects are 1210 1209 * goint to be swapped out. 1211 1210 * @gfp_flags: The gfp flags used for shmem page allocations. 1212 - * @target: The desired number of bytes to swap out. 1211 + * @target: The desired number of pages to swap out. 1213 1212 * 1214 - * Return: The number of bytes actually swapped out, or negative error code 1213 + * Return: The number of pages actually swapped out, or negative error code 1215 1214 * on error. 1216 1215 */ 1217 1216 s64 ttm_bo_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, ··· 1231 1230 1232 1231 return ttm_lru_walk_for_evict(&swapout_walk.walk, bdev, man, target); 1233 1232 } 1233 + EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_bo_swapout); 1234 1234 1235 1235 void ttm_bo_tt_destroy(struct ttm_buffer_object *bo) 1236 1236 {
+29 -26
drivers/gpu/drm/v3d/v3d_drv.c
··· 110 110 args->value = !!drm_gem_get_huge_mnt(dev); 111 111 return 0; 112 112 case DRM_V3D_PARAM_GLOBAL_RESET_COUNTER: 113 - mutex_lock(&v3d->reset_lock); 114 - args->value = v3d->reset_counter; 115 - mutex_unlock(&v3d->reset_lock); 113 + args->value = atomic_read(&v3d->reset_counter); 116 114 return 0; 117 115 case DRM_V3D_PARAM_CONTEXT_RESET_COUNTER: 118 - mutex_lock(&v3d->reset_lock); 119 - args->value = v3d_priv->reset_counter; 120 - mutex_unlock(&v3d->reset_lock); 116 + args->value = 0; 117 + for (enum v3d_queue q = 0; q < V3D_MAX_QUEUES; q++) 118 + args->value += atomic_read(&v3d_priv->stats[q]->reset_counter); 121 119 return 0; 122 120 default: 123 121 drm_dbg(dev, "Unknown parameter %d\n", args->param); ··· 129 131 struct v3d_dev *v3d = to_v3d_dev(dev); 130 132 struct v3d_file_priv *v3d_priv; 131 133 struct drm_gpu_scheduler *sched; 132 - int i; 134 + int i, ret; 133 135 134 136 v3d_priv = kzalloc_obj(*v3d_priv); 135 137 if (!v3d_priv) ··· 138 140 v3d_priv->v3d = v3d; 139 141 140 142 for (i = 0; i < V3D_MAX_QUEUES; i++) { 141 - sched = &v3d->queue[i].sched; 142 - drm_sched_entity_init(&v3d_priv->sched_entity[i], 143 - DRM_SCHED_PRIORITY_NORMAL, &sched, 144 - 1, NULL); 143 + v3d_priv->stats[i] = v3d_stats_alloc(); 144 + if (!v3d_priv->stats[i]) { 145 + ret = -ENOMEM; 146 + goto err_stats; 147 + } 145 148 146 - memset(&v3d_priv->stats[i], 0, sizeof(v3d_priv->stats[i])); 147 - seqcount_init(&v3d_priv->stats[i].lock); 149 + sched = &v3d->queue[i].sched; 150 + ret = drm_sched_entity_init(&v3d_priv->sched_entity[i], 151 + DRM_SCHED_PRIORITY_NORMAL, &sched, 152 + 1, NULL); 153 + if (ret) 154 + goto err_sched; 148 155 } 149 156 150 157 v3d_perfmon_open_file(v3d_priv); 151 158 file->driver_priv = v3d_priv; 152 159 153 160 return 0; 161 + 162 + err_sched: 163 + v3d_stats_put(v3d_priv->stats[i]); 164 + err_stats: 165 + for (i--; i >= 0; i--) { 166 + drm_sched_entity_destroy(&v3d_priv->sched_entity[i]); 167 + v3d_stats_put(v3d_priv->stats[i]); 168 + } 169 + kfree(v3d_priv); 170 + return ret; 154 171 } 155 172 156 173 static void 157 174 v3d_postclose(struct drm_device *dev, struct drm_file *file) 158 175 { 159 - struct v3d_dev *v3d = to_v3d_dev(dev); 160 176 struct v3d_file_priv *v3d_priv = file->driver_priv; 161 - unsigned long irqflags; 162 177 enum v3d_queue q; 163 178 164 179 for (q = 0; q < V3D_MAX_QUEUES; q++) { 165 - struct v3d_queue_state *queue = &v3d->queue[q]; 166 - struct v3d_job *job = queue->active_job; 167 - 168 180 drm_sched_entity_destroy(&v3d_priv->sched_entity[q]); 169 - 170 - if (job && job->base.entity == &v3d_priv->sched_entity[q]) { 171 - spin_lock_irqsave(&queue->queue_lock, irqflags); 172 - job->file_priv = NULL; 173 - spin_unlock_irqrestore(&queue->queue_lock, irqflags); 174 - } 181 + v3d_stats_put(v3d_priv->stats[q]); 175 182 } 176 183 177 184 v3d_perfmon_close_file(v3d_priv); ··· 189 186 unsigned int seq; 190 187 191 188 do { 192 - seq = read_seqcount_begin(&stats->lock); 189 + seq = raw_read_seqcount_begin(&stats->lock); 193 190 *active_runtime = stats->enabled_ns; 194 191 if (stats->start_ns) 195 192 *active_runtime += timestamp - stats->start_ns; ··· 204 201 enum v3d_queue queue; 205 202 206 203 for (queue = 0; queue < V3D_MAX_QUEUES; queue++) { 207 - struct v3d_stats *stats = &file_priv->stats[queue]; 204 + struct v3d_stats *stats = file_priv->stats[queue]; 208 205 u64 active_runtime, jobs_completed; 209 206 210 207 v3d_get_stats(stats, timestamp, &active_runtime, &jobs_completed);
+31 -15
drivers/gpu/drm/v3d/v3d_drv.h
··· 38 38 } 39 39 40 40 struct v3d_stats { 41 + struct kref refcount; 42 + 41 43 u64 start_ns; 42 44 u64 enabled_ns; 43 45 u64 jobs_completed; ··· 48 46 * This seqcount is used to protect the access to the GPU stats 49 47 * variables. It must be used as, while we are reading the stats, 50 48 * IRQs can happen and the stats can be updated. 49 + * 50 + * However, we use the raw seqcount helpers to interact with this lock 51 + * to avoid false positives from lockdep, which is unable to detect that 52 + * our readers are never from irq or softirq context, and that, for CPU 53 + * job queues, even the write side never is. 51 54 */ 52 55 seqcount_t lock; 56 + 57 + atomic_t reset_counter; 53 58 }; 54 59 55 60 struct v3d_queue_state { ··· 66 57 u64 emit_seqno; 67 58 68 59 /* Stores the GPU stats for this queue in the global context. */ 69 - struct v3d_stats stats; 60 + struct v3d_stats *stats; 70 61 71 62 /* Currently active job for this queue */ 72 63 struct v3d_job *active_job; 73 64 spinlock_t queue_lock; 74 - /* Protect dma fence for signalling job completion */ 75 - spinlock_t fence_lock; 76 65 }; 77 66 78 67 /* Performance monitor object. The perform lifetime is controlled by userspace ··· 203 196 */ 204 197 struct v3d_perfmon *global_perfmon; 205 198 206 - /* Global reset counter. The counter must be incremented when 207 - * a GPU reset happens. It must be protected by @reset_lock. 208 - */ 209 - unsigned int reset_counter; 199 + /* Global reset counter incremented on each GPU reset. */ 200 + atomic_t reset_counter; 210 201 }; 211 202 212 203 static inline struct v3d_dev * ··· 230 225 struct drm_sched_entity sched_entity[V3D_MAX_QUEUES]; 231 226 232 227 /* Stores the GPU stats for a specific queue for this fd. */ 233 - struct v3d_stats stats[V3D_MAX_QUEUES]; 234 - 235 - /* Per-fd reset counter, must be incremented when a job submitted 236 - * by this fd causes a GPU reset. It must be protected by 237 - * &struct v3d_dev->reset_lock. 238 - */ 239 - unsigned int reset_counter; 228 + struct v3d_stats *stats[V3D_MAX_QUEUES]; 240 229 }; 241 230 242 231 struct v3d_bo { ··· 317 318 * to collect per-process information about the GPU. 318 319 */ 319 320 struct v3d_file_priv *file_priv; 321 + 322 + /* Pointers to this job's per-fd and global queue stats. */ 323 + struct v3d_stats *client_stats; 324 + struct v3d_stats *global_stats; 320 325 321 326 /* Callback for the freeing of the job on refcount going to 0. */ 322 327 void (*free)(struct kref *ref); ··· 601 598 unsigned int count); 602 599 void v3d_performance_query_info_free(struct v3d_performance_query_info *query_info, 603 600 unsigned int count); 604 - void v3d_job_update_stats(struct v3d_job *job, enum v3d_queue q); 601 + struct v3d_stats *v3d_stats_alloc(void); 602 + void v3d_stats_release(struct kref *refcount); 603 + void v3d_job_update_stats(struct v3d_job *job); 605 604 int v3d_sched_init(struct v3d_dev *v3d); 606 605 void v3d_sched_fini(struct v3d_dev *v3d); 606 + 607 + static inline struct v3d_stats *v3d_stats_get(struct v3d_stats *stats) 608 + { 609 + kref_get(&stats->refcount); 610 + return stats; 611 + } 612 + 613 + static inline void v3d_stats_put(struct v3d_stats *stats) 614 + { 615 + kref_put(&stats->refcount, v3d_stats_release); 616 + } 607 617 608 618 /* v3d_perfmon.c */ 609 619 void v3d_perfmon_init(struct v3d_dev *v3d);
+1 -1
drivers/gpu/drm/v3d/v3d_fence.c
··· 15 15 fence->dev = &v3d->drm; 16 16 fence->queue = q; 17 17 fence->seqno = ++queue->emit_seqno; 18 - dma_fence_init(&fence->base, &v3d_fence_ops, &queue->fence_lock, 18 + dma_fence_init(&fence->base, &v3d_fence_ops, &queue->queue_lock, 19 19 queue->fence_context, fence->seqno); 20 20 21 21 return &fence->base;
+27 -16
drivers/gpu/drm/v3d/v3d_gem.c
··· 287 287 for (i = 0; i < V3D_MAX_QUEUES; i++) { 288 288 struct v3d_queue_state *queue = &v3d->queue[i]; 289 289 290 + queue->stats = v3d_stats_alloc(); 291 + if (!queue->stats) { 292 + ret = -ENOMEM; 293 + goto err_stats; 294 + } 295 + 290 296 queue->fence_context = dma_fence_context_alloc(1); 291 - memset(&queue->stats, 0, sizeof(queue->stats)); 292 - seqcount_init(&queue->stats.lock); 293 297 294 298 spin_lock_init(&queue->queue_lock); 295 - spin_lock_init(&queue->fence_lock); 296 299 } 297 300 298 301 spin_lock_init(&v3d->mm_lock); 299 302 ret = drmm_mutex_init(dev, &v3d->bo_lock); 300 303 if (ret) 301 - return ret; 304 + goto err_stats; 302 305 ret = drmm_mutex_init(dev, &v3d->reset_lock); 303 306 if (ret) 304 - return ret; 307 + goto err_stats; 305 308 ret = drmm_mutex_init(dev, &v3d->sched_lock); 306 309 if (ret) 307 - return ret; 310 + goto err_stats; 308 311 ret = drmm_mutex_init(dev, &v3d->cache_clean_lock); 309 312 if (ret) 310 - return ret; 313 + goto err_stats; 311 314 312 315 /* Note: We don't allocate address 0. Various bits of HW 313 316 * treat 0 as special, such as the occlusion query counters ··· 322 319 &v3d->pt_paddr, 323 320 GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); 324 321 if (!v3d->pt) { 325 - drm_mm_takedown(&v3d->mm); 326 322 dev_err(v3d->drm.dev, 327 323 "Failed to allocate page tables. Please ensure you have DMA enabled.\n"); 328 - return -ENOMEM; 324 + ret = -ENOMEM; 325 + goto err_dma_alloc; 329 326 } 330 327 331 328 v3d_init_hw_state(v3d); ··· 334 331 v3d_huge_mnt_init(v3d); 335 332 336 333 ret = v3d_sched_init(v3d); 337 - if (ret) { 338 - drm_mm_takedown(&v3d->mm); 339 - dma_free_coherent(v3d->drm.dev, pt_size, (void *)v3d->pt, 340 - v3d->pt_paddr); 341 - return ret; 342 - } 334 + if (ret) 335 + goto err_sched; 343 336 344 337 return 0; 338 + 339 + err_sched: 340 + dma_free_coherent(v3d->drm.dev, pt_size, (void *)v3d->pt, v3d->pt_paddr); 341 + err_dma_alloc: 342 + drm_mm_takedown(&v3d->mm); 343 + err_stats: 344 + for (i--; i >= 0; i--) 345 + v3d_stats_put(v3d->queue[i].stats); 346 + 347 + return ret; 345 348 } 346 349 347 350 void ··· 361 352 /* Waiting for jobs to finish would need to be done before 362 353 * unregistering V3D. 363 354 */ 364 - for (q = 0; q < V3D_MAX_QUEUES; q++) 355 + for (q = 0; q < V3D_MAX_QUEUES; q++) { 365 356 WARN_ON(v3d->queue[q].active_job); 357 + v3d_stats_put(v3d->queue[q].stats); 358 + } 366 359 367 360 drm_mm_takedown(&v3d->mm); 368 361
+1 -1
drivers/gpu/drm/v3d/v3d_irq.c
··· 92 92 struct v3d_queue_state *queue = &v3d->queue[q]; 93 93 struct v3d_fence *fence = to_v3d_fence(queue->active_job->irq_fence); 94 94 95 - v3d_job_update_stats(queue->active_job, q); 95 + v3d_job_update_stats(queue->active_job); 96 96 trace_irq(&v3d->drm, fence->seqno); 97 97 98 98 queue->active_job = NULL;
+51 -82
drivers/gpu/drm/v3d/v3d_sched.c
··· 66 66 return container_of(sched_job, struct v3d_cpu_job, base.base); 67 67 } 68 68 69 + void v3d_stats_release(struct kref *refcount) 70 + { 71 + struct v3d_stats *stats = container_of(refcount, typeof(*stats), refcount); 72 + 73 + kfree(stats); 74 + } 75 + 76 + struct v3d_stats *v3d_stats_alloc(void) 77 + { 78 + struct v3d_stats *stats; 79 + 80 + stats = kzalloc_obj(*stats); 81 + if (!stats) 82 + return NULL; 83 + 84 + kref_init(&stats->refcount); 85 + seqcount_init(&stats->lock); 86 + 87 + return stats; 88 + } 89 + 69 90 static void 70 91 v3d_sched_job_free(struct drm_sched_job *sched_job) 71 92 { ··· 158 137 } 159 138 160 139 static void 161 - v3d_job_start_stats(struct v3d_job *job, enum v3d_queue queue) 140 + v3d_stats_start(struct v3d_stats *stats, u64 now) 162 141 { 163 - struct v3d_dev *v3d = job->v3d; 164 - struct v3d_file_priv *file = job->file_priv; 165 - struct v3d_stats *global_stats = &v3d->queue[queue].stats; 166 - struct v3d_stats *local_stats = &file->stats[queue]; 142 + raw_write_seqcount_begin(&stats->lock); 143 + stats->start_ns = now; 144 + raw_write_seqcount_end(&stats->lock); 145 + } 146 + 147 + static void 148 + v3d_job_start_stats(struct v3d_job *job) 149 + { 167 150 u64 now = local_clock(); 168 - unsigned long flags; 169 151 170 - /* 171 - * We only need to disable local interrupts to appease lockdep who 172 - * otherwise would think v3d_job_start_stats vs v3d_stats_update has an 173 - * unsafe in-irq vs no-irq-off usage problem. This is a false positive 174 - * because all the locks are per queue and stats type, and all jobs are 175 - * completely one at a time serialised. More specifically: 176 - * 177 - * 1. Locks for GPU queues are updated from interrupt handlers under a 178 - * spin lock and started here with preemption disabled. 179 - * 180 - * 2. Locks for CPU queues are updated from the worker with preemption 181 - * disabled and equally started here with preemption disabled. 182 - * 183 - * Therefore both are consistent. 184 - * 185 - * 3. Because next job can only be queued after the previous one has 186 - * been signaled, and locks are per queue, there is also no scope for 187 - * the start part to race with the update part. 188 - */ 189 - if (IS_ENABLED(CONFIG_LOCKDEP)) 190 - local_irq_save(flags); 191 - else 192 - preempt_disable(); 193 - 194 - write_seqcount_begin(&local_stats->lock); 195 - local_stats->start_ns = now; 196 - write_seqcount_end(&local_stats->lock); 197 - 198 - write_seqcount_begin(&global_stats->lock); 199 - global_stats->start_ns = now; 200 - write_seqcount_end(&global_stats->lock); 201 - 202 - if (IS_ENABLED(CONFIG_LOCKDEP)) 203 - local_irq_restore(flags); 204 - else 205 - preempt_enable(); 152 + preempt_disable(); 153 + v3d_stats_start(job->client_stats, now); 154 + v3d_stats_start(job->global_stats, now); 155 + preempt_enable(); 206 156 } 207 157 208 158 static void 209 159 v3d_stats_update(struct v3d_stats *stats, u64 now) 210 160 { 211 - write_seqcount_begin(&stats->lock); 161 + raw_write_seqcount_begin(&stats->lock); 212 162 stats->enabled_ns += now - stats->start_ns; 213 163 stats->jobs_completed++; 214 164 stats->start_ns = 0; 215 - write_seqcount_end(&stats->lock); 165 + raw_write_seqcount_end(&stats->lock); 216 166 } 217 167 218 168 void 219 - v3d_job_update_stats(struct v3d_job *job, enum v3d_queue q) 169 + v3d_job_update_stats(struct v3d_job *job) 220 170 { 221 - struct v3d_dev *v3d = job->v3d; 222 - struct v3d_queue_state *queue = &v3d->queue[q]; 223 - struct v3d_stats *global_stats = &queue->stats; 224 171 u64 now = local_clock(); 225 - unsigned long flags; 226 172 227 - /* See comment in v3d_job_start_stats() */ 228 - if (IS_ENABLED(CONFIG_LOCKDEP)) 229 - local_irq_save(flags); 230 - else 231 - preempt_disable(); 232 - 233 - /* Don't update the local stats if the file context has already closed */ 234 - spin_lock(&queue->queue_lock); 235 - if (job->file_priv) 236 - v3d_stats_update(&job->file_priv->stats[q], now); 237 - spin_unlock(&queue->queue_lock); 238 - 239 - v3d_stats_update(global_stats, now); 240 - 241 - if (IS_ENABLED(CONFIG_LOCKDEP)) 242 - local_irq_restore(flags); 243 - else 244 - preempt_enable(); 173 + preempt_disable(); 174 + v3d_stats_update(job->client_stats, now); 175 + v3d_stats_update(job->global_stats, now); 176 + preempt_enable(); 245 177 } 246 178 247 179 static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) ··· 237 263 trace_v3d_submit_cl(dev, false, to_v3d_fence(fence)->seqno, 238 264 job->start, job->end); 239 265 240 - v3d_job_start_stats(&job->base, V3D_BIN); 266 + v3d_job_start_stats(&job->base); 241 267 v3d_switch_perfmon(v3d, &job->base); 242 268 243 269 /* Set the current and end address of the control list. ··· 291 317 trace_v3d_submit_cl(dev, true, to_v3d_fence(fence)->seqno, 292 318 job->start, job->end); 293 319 294 - v3d_job_start_stats(&job->base, V3D_RENDER); 320 + v3d_job_start_stats(&job->base); 295 321 v3d_switch_perfmon(v3d, &job->base); 296 322 297 323 /* XXX: Set the QCFG */ ··· 330 356 331 357 trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno); 332 358 333 - v3d_job_start_stats(&job->base, V3D_TFU); 359 + v3d_job_start_stats(&job->base); 334 360 335 361 V3D_WRITE(V3D_TFU_IIA(v3d->ver), job->args.iia); 336 362 V3D_WRITE(V3D_TFU_IIS(v3d->ver), job->args.iis); ··· 380 406 381 407 trace_v3d_submit_csd(dev, to_v3d_fence(fence)->seqno); 382 408 383 - v3d_job_start_stats(&job->base, V3D_CSD); 409 + v3d_job_start_stats(&job->base); 384 410 v3d_switch_perfmon(v3d, &job->base); 385 411 386 412 csd_cfg0_reg = V3D_CSD_QUEUED_CFG0(v3d->ver); ··· 668 694 return NULL; 669 695 } 670 696 671 - v3d_job_start_stats(&job->base, V3D_CPU); 697 + v3d_job_start_stats(&job->base); 672 698 trace_v3d_cpu_job_begin(&v3d->drm, job->job_type); 673 699 674 700 cpu_job_function[job->job_type](job); 675 701 676 702 trace_v3d_cpu_job_end(&v3d->drm, job->job_type); 677 - v3d_job_update_stats(&job->base, V3D_CPU); 703 + v3d_job_update_stats(&job->base); 678 704 679 705 /* Synchronous operation, so no fence to wait on. */ 680 706 return NULL; ··· 686 712 struct v3d_job *job = to_v3d_job(sched_job); 687 713 struct v3d_dev *v3d = job->v3d; 688 714 689 - v3d_job_start_stats(job, V3D_CACHE_CLEAN); 715 + v3d_job_start_stats(job); 690 716 691 717 v3d_clean_caches(v3d); 692 718 693 - v3d_job_update_stats(job, V3D_CACHE_CLEAN); 719 + v3d_job_update_stats(job); 694 720 695 721 /* Synchronous operation, so no fence to wait on. */ 696 722 return NULL; ··· 701 727 enum v3d_queue q) 702 728 { 703 729 struct v3d_job *job = to_v3d_job(sched_job); 704 - struct v3d_file_priv *v3d_priv = job->file_priv; 705 - unsigned long irqflags; 706 730 enum v3d_queue i; 707 731 708 732 mutex_lock(&v3d->reset_lock); ··· 715 743 /* get the GPU back into the init state */ 716 744 v3d_reset(v3d); 717 745 718 - v3d->reset_counter++; 719 - spin_lock_irqsave(&v3d->queue[q].queue_lock, irqflags); 720 - if (v3d_priv) 721 - v3d_priv->reset_counter++; 722 - spin_unlock_irqrestore(&v3d->queue[q].queue_lock, irqflags); 746 + atomic_inc(&v3d->reset_counter); 747 + atomic_inc(&job->client_stats->reset_counter); 723 748 724 749 for (i = 0; i < V3D_MAX_QUEUES; i++) 725 750 drm_sched_resubmit_jobs(&v3d->queue[i].sched);
+6
drivers/gpu/drm/v3d/v3d_submit.c
··· 103 103 if (job->perfmon) 104 104 v3d_perfmon_put(job->perfmon); 105 105 106 + v3d_stats_put(job->client_stats); 107 + v3d_stats_put(job->global_stats); 108 + 106 109 kfree(job); 107 110 } 108 111 ··· 205 202 } 206 203 207 204 kref_init(&job->refcount); 205 + 206 + job->client_stats = v3d_stats_get(v3d_priv->stats[queue]); 207 + job->global_stats = v3d_stats_get(v3d->queue[queue].stats); 208 208 209 209 return 0; 210 210
+1 -1
drivers/gpu/drm/v3d/v3d_sysfs.c
··· 20 20 len += sysfs_emit(buf, "queue\ttimestamp\tjobs\truntime\n"); 21 21 22 22 for (queue = 0; queue < V3D_MAX_QUEUES; queue++) { 23 - struct v3d_stats *stats = &v3d->queue[queue].stats; 23 + struct v3d_stats *stats = v3d->queue[queue].stats; 24 24 u64 active_runtime, jobs_completed; 25 25 26 26 v3d_get_stats(stats, timestamp, &active_runtime, &jobs_completed);
+7 -1
drivers/gpu/drm/vkms/vkms_composer.c
··· 475 475 { 476 476 struct vkms_plane_state **plane = crtc_state->active_planes; 477 477 u32 n_active_planes = crtc_state->num_active_planes; 478 + u64 bgcolor = crtc_state->base.background_color; 478 479 479 - const struct pixel_argb_u16 background_color = { .a = 0xffff }; 480 + const struct pixel_argb_u16 background_color = { 481 + .a = 0xffff, 482 + .r = DRM_ARGB64_GETR(bgcolor), 483 + .g = DRM_ARGB64_GETG(bgcolor), 484 + .b = DRM_ARGB64_GETB(bgcolor), 485 + }; 480 486 481 487 int crtc_y_limit = crtc_state->base.mode.vdisplay; 482 488 int crtc_x_limit = crtc_state->base.mode.hdisplay;
+3
drivers/gpu/drm/vkms/vkms_crtc.c
··· 4 4 5 5 #include <drm/drm_atomic.h> 6 6 #include <drm/drm_atomic_helper.h> 7 + #include <drm/drm_blend.h> 7 8 #include <drm/drm_managed.h> 8 9 #include <drm/drm_print.h> 9 10 #include <drm/drm_probe_helper.h> ··· 227 226 } 228 227 229 228 drm_crtc_enable_color_mgmt(crtc, 0, false, VKMS_LUT_SIZE); 229 + 230 + drm_crtc_attach_background_color_property(crtc); 230 231 231 232 spin_lock_init(&vkms_out->lock); 232 233 spin_lock_init(&vkms_out->composer_lock);
+3 -1
include/drm/drm_blend.h
··· 31 31 #define DRM_MODE_BLEND_COVERAGE 1 32 32 #define DRM_MODE_BLEND_PIXEL_NONE 2 33 33 34 - struct drm_device; 35 34 struct drm_atomic_state; 35 + struct drm_crtc; 36 + struct drm_device; 36 37 struct drm_plane; 37 38 38 39 static inline bool drm_rotation_90_or_270(unsigned int rotation) ··· 59 58 struct drm_atomic_state *state); 60 59 int drm_plane_create_blend_mode_property(struct drm_plane *plane, 61 60 unsigned int supported_modes); 61 + void drm_crtc_attach_background_color_property(struct drm_crtc *crtc); 62 62 #endif
+12
include/drm/drm_crtc.h
··· 275 275 struct drm_property_blob *gamma_lut; 276 276 277 277 /** 278 + * @background_color: 279 + * 280 + * RGB value representing the CRTC's background color. The background 281 + * color (aka "canvas color") of a CRTC is the color that will be used 282 + * for pixels not covered by a plane, or covered by transparent pixels 283 + * of a plane. The value here should be built using DRM_ARGB64_PREP*() 284 + * helpers, while the individual color components can be extracted with 285 + * desired precision via the DRM_ARGB64_GET*() macros. 286 + */ 287 + u64 background_color; 288 + 289 + /** 278 290 * @target_vblank: 279 291 * 280 292 * Target vertical blank period when a page flip
+5
include/drm/drm_mode_config.h
··· 836 836 * gamma LUT as supported by the driver (read-only). 837 837 */ 838 838 struct drm_property *gamma_lut_size_property; 839 + /** 840 + * @background_color_property: Optional CRTC property to set the 841 + * background color. 842 + */ 843 + struct drm_property *background_color_property; 839 844 840 845 /** 841 846 * @suggested_x_property: Optional connector property with a hint for
+1 -17
include/linux/math.h
··· 89 89 } \ 90 90 ) 91 91 92 - /* 93 - * Divide positive or negative dividend by positive or negative divisor 94 - * and round to closest integer. Result is undefined for negative 95 - * divisors if the dividend variable type is unsigned and for negative 96 - * dividends if the divisor variable type is unsigned. 97 - */ 98 - #define DIV_ROUND_CLOSEST(x, divisor)( \ 99 - { \ 100 - typeof(x) __x = x; \ 101 - typeof(divisor) __d = divisor; \ 102 - (((typeof(x))-1) > 0 || \ 103 - ((typeof(divisor))-1) > 0 || \ 104 - (((__x) > 0) == ((__d) > 0))) ? \ 105 - (((__x) + ((__d) / 2)) / (__d)) : \ 106 - (((__x) - ((__d) / 2)) / (__d)); \ 107 - } \ 108 - ) 92 + #define DIV_ROUND_CLOSEST __KERNEL_DIV_ROUND_CLOSEST 109 93 /* 110 94 * Same as above but for u64 dividends. divisor must be a 32-bit 111 95 * number.
+80
include/uapi/drm/drm_mode.h
··· 27 27 #ifndef _DRM_MODE_H 28 28 #define _DRM_MODE_H 29 29 30 + #include <linux/bits.h> 31 + #include <linux/const.h> 32 + 30 33 #include "drm.h" 31 34 32 35 #if defined(__cplusplus) ··· 1551 1548 __u32 fb_id; 1552 1549 __u32 pad; 1553 1550 }; 1551 + 1552 + /* 1553 + * Put 16-bit ARGB values into a standard 64-bit representation that can be 1554 + * used for ioctl parameters, inter-driver communication, etc. 1555 + * 1556 + * If the component values being provided contain less than 16 bits of 1557 + * precision, use a conversion ratio to get a better color approximation. 1558 + * The ratio is computed as (2^16 - 1) / (2^bpc - 1), where bpc and 16 are 1559 + * the input and output precision, respectively. 1560 + * Also note bpc must be greater than 0. 1561 + */ 1562 + #define __DRM_ARGB64_PREP(c, shift) \ 1563 + (((__u64)(c) & __GENMASK(15, 0)) << (shift)) 1564 + 1565 + #define __DRM_ARGB64_PREP_BPC(c, shift, bpc) \ 1566 + ({ \ 1567 + __u16 mask = __GENMASK((bpc) - 1, 0); \ 1568 + __u16 conv = __KERNEL_DIV_ROUND_CLOSEST((mask & (c)) * \ 1569 + __GENMASK(15, 0), mask);\ 1570 + __DRM_ARGB64_PREP(conv, shift); \ 1571 + }) 1572 + 1573 + #define DRM_ARGB64_PREP(alpha, red, green, blue) \ 1574 + ( \ 1575 + __DRM_ARGB64_PREP(alpha, 48) | \ 1576 + __DRM_ARGB64_PREP(red, 32) | \ 1577 + __DRM_ARGB64_PREP(green, 16) | \ 1578 + __DRM_ARGB64_PREP(blue, 0) \ 1579 + ) 1580 + 1581 + #define DRM_ARGB64_PREP_BPC(alpha, red, green, blue, bpc) \ 1582 + ({ \ 1583 + __typeof__(bpc) __bpc = bpc; \ 1584 + __DRM_ARGB64_PREP_BPC(alpha, 48, __bpc) | \ 1585 + __DRM_ARGB64_PREP_BPC(red, 32, __bpc) | \ 1586 + __DRM_ARGB64_PREP_BPC(green, 16, __bpc) | \ 1587 + __DRM_ARGB64_PREP_BPC(blue, 0, __bpc); \ 1588 + }) 1589 + 1590 + /* 1591 + * Extract the specified color component from a standard 64-bit ARGB value. 1592 + * 1593 + * If the requested precision is less than 16 bits, make use of a conversion 1594 + * ratio calculated as (2^bpc - 1) / (2^16 - 1), where bpc and 16 are the 1595 + * output and input precision, respectively. 1596 + * 1597 + * If speed is more important than accuracy, use DRM_ARGB64_GET*_BPCS() 1598 + * instead of DRM_ARGB64_GET*_BPC() in order to replace the expensive 1599 + * division with a simple bit right-shift operation. 1600 + */ 1601 + #define __DRM_ARGB64_GET(c, shift) \ 1602 + ((__u16)(((__u64)(c) >> (shift)) & __GENMASK(15, 0))) 1603 + 1604 + #define __DRM_ARGB64_GET_BPC(c, shift, bpc) \ 1605 + ({ \ 1606 + __u16 comp = __DRM_ARGB64_GET(c, shift); \ 1607 + __KERNEL_DIV_ROUND_CLOSEST(comp * __GENMASK((bpc) - 1, 0), \ 1608 + __GENMASK(15, 0)); \ 1609 + }) 1610 + 1611 + #define __DRM_ARGB64_GET_BPCS(c, shift, bpc) \ 1612 + (__DRM_ARGB64_GET(c, shift) >> (16 - (bpc))) 1613 + 1614 + #define DRM_ARGB64_GETA(c) __DRM_ARGB64_GET(c, 48) 1615 + #define DRM_ARGB64_GETR(c) __DRM_ARGB64_GET(c, 32) 1616 + #define DRM_ARGB64_GETG(c) __DRM_ARGB64_GET(c, 16) 1617 + #define DRM_ARGB64_GETB(c) __DRM_ARGB64_GET(c, 0) 1618 + 1619 + #define DRM_ARGB64_GETA_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 48, bpc) 1620 + #define DRM_ARGB64_GETR_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 32, bpc) 1621 + #define DRM_ARGB64_GETG_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 16, bpc) 1622 + #define DRM_ARGB64_GETB_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 0, bpc) 1623 + 1624 + #define DRM_ARGB64_GETA_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 48, bpc) 1625 + #define DRM_ARGB64_GETR_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 32, bpc) 1626 + #define DRM_ARGB64_GETG_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 16, bpc) 1627 + #define DRM_ARGB64_GETB_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 0, bpc) 1554 1628 1555 1629 #if defined(__cplusplus) 1556 1630 }
+18
include/uapi/linux/const.h
··· 50 50 51 51 #define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 52 52 53 + /* 54 + * Divide positive or negative dividend by positive or negative divisor 55 + * and round to closest integer. Result is undefined for negative 56 + * divisors if the dividend variable type is unsigned and for negative 57 + * dividends if the divisor variable type is unsigned. 58 + */ 59 + #define __KERNEL_DIV_ROUND_CLOSEST(x, divisor) \ 60 + ({ \ 61 + __typeof__(x) __x = x; \ 62 + __typeof__(divisor) __d = divisor; \ 63 + \ 64 + (((__typeof__(x))-1) > 0 || \ 65 + ((__typeof__(divisor))-1) > 0 || \ 66 + (((__x) > 0) == ((__d) > 0))) ? \ 67 + (((__x) + ((__d) / 2)) / (__d)) : \ 68 + (((__x) - ((__d) / 2)) / (__d)); \ 69 + }) 70 + 53 71 #endif /* _UAPI_LINUX_CONST_H */