From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D3BCA29E10F; Sun, 9 Aug 2026 13:15:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786281322; cv=none; b=TWGLcO8gWD4myfW2dRPs6PZ8cxthJmyjc2NiBx9L2lnAxizaOa4/ZUuODAEiB9A09iMLfEBQJ+rSiHBrXdnCXOvOyDOHHhuQ7FQC/6PJfNCk2A1LCGpXQDEH8Q8OH2ZQqbgVrlDqBzVSo8YtBC/ssINGgwW+Fc4IRyIr66v4I0Q= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786281322; c=relaxed/simple; bh=d3hk7S6cwDtGRjhxnqeKNqNv2dECeNGKbwrhSJcNI94=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=uYQmekxSsxAeu1pFniMpWejAO1OnD3iUBHVnRGBvjQlaFfPW+CsIyTQEI5tf8BqYj3hroCFSanVFwLFkFpwWhdGxrJl6u4pgSuVXf/GpsVSYhYY6ga7mr8YD7uqEeyHIaKIQ3tuGe4/pnx3iLgZhBE466X85yULGAiQNXOJG5CA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CvIoeumx; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CvIoeumx" Received: by smtp.kernel.org (Postfix) with ESMTPS id EC775C2BD00; Sun, 9 Aug 2026 13:15:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1786281321; bh=d3hk7S6cwDtGRjhxnqeKNqNv2dECeNGKbwrhSJcNI94=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=CvIoeumxnyIvQynPJkYtSy2NpC7axHdV3huqZhYzbZWmL2dBjMyMF7wO+m69uMRUT TMyrUSJejRLvm1GVZlt8c9MWgwNu0S1+SMkST5/mfcK/sPHg9K0ChBG+aO2/s9dG8n caXGDSK8Q/3KmN/0nSlgfyTDZzxrF1dUoBwb4IN/gxEVmNgjeLgJYL6fFNjo6RV3rI ZN1lJI8Ey/2LkVl7dlwpd7ao1m1XJ/AQHodEbNuK/QpOGJ/BtXTjtnwnPfA5eL+X/i +vfAT/4FXmBJy8JoqfPRHiY4DginlU8uMASY3PDU79I8X6nmJEuZiDoqxF3ZnaAHOt VBgO8ZQHzx54w== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D522AC5AD68; Sun, 9 Aug 2026 13:15:20 +0000 (UTC) From: Cody Kang via B4 Relay Date: Sun, 09 Aug 2026 21:14:17 +0800 Subject: [PATCH v2 08/17] drm/spacemit: add Saturn DPU core types, cmdlist and display MMU Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260809-k3-display-v2-8-327d7910bf71@gmail.com> References: <20260809-k3-display-v2-0-327d7910bf71@gmail.com> In-Reply-To: <20260809-k3-display-v2-0-327d7910bf71@gmail.com> To: David Airlie , Simona Vetter , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Yixun Lan , Vinod Koul , Neil Armstrong , Haylen Chu , Michael Turquette , Stephen Boyd , Brian Masney , Philipp Zabel , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Yao Zi , =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Guodong Xu Cc: dri-devel@lists.freedesktop.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, linux-phy@lists.infradead.org, linux-clk@vger.kernel.org, Cody Kang X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1786281298; l=45314; i=codykang.hk@gmail.com; s=codykang; h=from:subject:message-id; bh=0aA0Vkq4JndUPsyOa8h984LQsYZfoTg6AN4cbfpx1t4=; b=8ZYqSpu8xSWQxP3XeIP9vS9kVZrvZaxNhTMWUKqESK4ucGTIuXMVEBoK+0DrjiiUQi1aRZ8MR PWS9dfsOUHrDJQVuea9NM4TbVpV9qNrfMzhsBrlV3hil2bqkvtt/B7z X-Developer-Key: i=codykang.hk@gmail.com; a=ed25519; pk=Osn6/cs9Su/mHHw4P19cmVc3v2FMQfiWahjsN0vEkuk= X-Endpoint-Received: by B4 Relay for codykang.hk@gmail.com/codykang with auth_id=895 X-Original-From: Cody Kang Reply-To: codykang.hk@gmail.com From: Cody Kang Register programming of the Saturn DPU is batched: the driver builds per-module register lists in memory and the hardware command-list engine replays them at frame boundaries, while scanout buffers are mapped through the DPU's private address translation unit (DMMU) rather than scanned out by physical address. Add the shared driver types, the MMIO field accessors, the trace events, the command-list builder and the DMMU table code. Some DPU configuration registers are write-only, so a read-modify-write would corrupt neighbouring fields; byte- and halfword-aligned fields are therefore written at their native width instead. Signed-off-by: Cody Kang --- v2: - hold the DPU reset lines as one reset_control_bulk_data pipe_resets[5] array in spacemit_drm_private and drop the pointers duplicated into struct spacemit_crtc (Philipp Zabel) - drop the INT_VSYNC_UPDATE enum member; it never had an irq_list[] mapping and the register model has no such status bit (Sashiko) - drop the unreachable multi-planar arms in spacemit_dmmu_map(); every advertised format is single-planar (Sashiko) - drop the unused cmdlist parameter from the crtc_cmdlist op (Sashiko) --- drivers/gpu/drm/spacemit/dpu/dpu_saturn.h | 35 +++ drivers/gpu/drm/spacemit/dpu/dpu_trace.h | 350 ++++++++++++++++++++++++++++ drivers/gpu/drm/spacemit/spacemit_cmdlist.c | 304 ++++++++++++++++++++++++ drivers/gpu/drm/spacemit/spacemit_cmdlist.h | 146 ++++++++++++ drivers/gpu/drm/spacemit/spacemit_crtc.h | 246 +++++++++++++++++++ drivers/gpu/drm/spacemit/spacemit_dmmu.c | 80 +++++++ drivers/gpu/drm/spacemit/spacemit_dmmu.h | 40 ++++ drivers/gpu/drm/spacemit/spacemit_dpu_reg.h | 108 +++++++++ drivers/gpu/drm/spacemit/spacemit_drm.h | 61 +++++ 9 files changed, 1370 insertions(+) diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_saturn.h b/drivers/gpu/drm/spacemit/dpu/dpu_saturn.h new file mode 100644 index 0000000000000..22d77d56eca1a --- /dev/null +++ b/drivers/gpu/drm/spacemit/dpu/dpu_saturn.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025-2026 SpacemiT Co., Ltd. + * + */ + +#ifndef _DPU_SATURN_H_ +#define _DPU_SATURN_H_ + +#include "../spacemit_crtc.h" + +struct dpu_format_id { + u32 format; /* DRM fourcc */ + u8 id; + u8 bpp; /* bits per pixel */ +}; + +#define SPACEMIT_DPU_INVALID_FORMAT_ID 0xff + +enum format_features { + FORMAT_RGB = BIT(0), +}; + +struct spacemit_hw_rdma { + u16 formats; +}; + +int dpu_parse_clocks(struct device *dev, struct dpu_clk_context *clk_ctx); +u8 spacemit_plane_hw_get_format_id(u32 format); +u32 saturn_conf_dpuctrl_rdma(struct spacemit_crtc *a_crtc); +extern const struct spacemit_hw_device k3_saturn_dpu; +void saturn_enable_irq_mask(struct spacemit_crtc *a_crtc, bool enable, + u32 offset, u32 mask); +void spacemit_cfg_rdy_timer_handler(struct timer_list *t); +#endif diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_trace.h b/drivers/gpu/drm/spacemit/dpu/dpu_trace.h new file mode 100644 index 0000000000000..abfe34a67a4bd --- /dev/null +++ b/drivers/gpu/drm/spacemit/dpu/dpu_trace.h @@ -0,0 +1,350 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025-2026 SpacemiT Co., Ltd. + * + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM spacemit_dpu + +#if !defined(_DPU_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _DPU_TRACE_H_ + +#include +#include +#include + +#include "dpu_saturn.h" + +TRACE_EVENT(drm_display_mode_info, + TP_PROTO(struct drm_display_mode *mode), + TP_ARGS(mode), + TP_STRUCT__entry( + __field(u16, hdisplay) + __field(u16, hbp) + __field(u16, hfp) + __field(u16, hsync) + __field(u16, vdisplay) + __field(u16, vbp) + __field(u16, vfp) + __field(u16, vsync) + ), + TP_fast_assign( + __entry->hdisplay = mode->hdisplay; + __entry->hbp = mode->htotal - mode->hsync_end; + __entry->hfp = mode->hsync_start - mode->hdisplay; + __entry->hsync = mode->hsync_end - mode->hsync_start; + __entry->vdisplay = mode->vdisplay; + __entry->vbp = mode->vtotal - mode->vsync_end; + __entry->vfp = mode->vsync_start - mode->vdisplay; + __entry->vsync = mode->vsync_end - mode->vsync_start; + ), + TP_printk("drm_display_mode: hdisplay=%d vdisplay=%d hsync=%d vsync=%d hbp=%d hfp=%d vbp=%d vfp=%d", + __entry->hdisplay, __entry->vdisplay, __entry->hsync, __entry->vsync, + __entry->hbp, __entry->hfp, __entry->vbp, __entry->vfp + ) +); + +TRACE_EVENT(dpu_plane_info, + TP_PROTO(struct drm_plane_state *state, struct drm_framebuffer *fb, + u32 rdma_id, u32 alpha, u32 rotation), + TP_ARGS(state, fb, rdma_id, alpha, rotation), + TP_STRUCT__entry( + __field(u32, rdma_id) + __field(u32, src_w) + __field(u32, src_h) + __field(u32, src_x) + __field(u32, src_y) + __field(u32, crtc_w) + __field(u32, crtc_h) + __field(u32, crtc_x) + __field(u32, crtc_y) + __field(u32, width) + __field(u32, height) + __field(u32, format) + __field(u32, blend_mode) + __field(u32, alpha) + __field(u32, zpos) + __field(u32, rotation) + ), + TP_fast_assign( + __entry->rdma_id = rdma_id; + __entry->src_w = state->src_w >> 16; + __entry->src_h = state->src_h >> 16; + __entry->src_x = state->src_x >> 16; + __entry->src_y = state->src_y >> 16; + __entry->crtc_w = state->crtc_w; + __entry->crtc_h = state->crtc_h; + __entry->crtc_x = state->crtc_x; + __entry->crtc_y = state->crtc_y; + __entry->width = fb->width; + __entry->height = fb->height; + __entry->format = fb->format->format; + __entry->blend_mode = state->pixel_blend_mode; + __entry->alpha = alpha; + __entry->zpos = state->zpos; + __entry->rotation = rotation; + ), + TP_printk("rdma_id=%d src: w=%d h=%d x=%d y=%d crtc: w=%d h=%d x=%d y=%d width=%d height=%d fmt=0x%x blend=%d alpha=%d zpos=%d rot=%d", + __entry->rdma_id, __entry->src_w, + __entry->src_h, __entry->src_x, + __entry->src_y, __entry->crtc_w, + __entry->crtc_h, __entry->crtc_x, + __entry->crtc_y, __entry->width, + __entry->height, __entry->format, + __entry->blend_mode, __entry->alpha, + __entry->zpos, __entry->rotation) +); + +DECLARE_EVENT_CLASS(dpu_status_template, + TP_PROTO(const char *name, int status), + TP_ARGS(name, status), + TP_STRUCT__entry( + __string(name_str, name) + __field(int, status) + ), + TP_fast_assign( + __assign_str(name_str); + __entry->status = status; + ), + TP_printk("%s: 0x%x", + __get_str(name_str), __entry->status) +); + +DEFINE_EVENT(dpu_status_template, dpuctrl, + TP_PROTO(const char *name, int status), + TP_ARGS(name, status) +); +DEFINE_EVENT(dpu_status_template, spacemit_plane_update_hw_channel, + TP_PROTO(const char *name, int status), + TP_ARGS(name, status) +); +DEFINE_EVENT(dpu_status_template, saturn_irq_enable, + TP_PROTO(const char *name, int status), + TP_ARGS(name, status) +); +DEFINE_EVENT(dpu_status_template, saturn_enable_vsync, + TP_PROTO(const char *name, int status), + TP_ARGS(name, status) +); +DEFINE_EVENT(dpu_status_template, dpu_isr_status, + TP_PROTO(const char *name, int status), + TP_ARGS(name, status) +); + +DECLARE_EVENT_CLASS(dpu_uint64_t_data_template, + TP_PROTO(const char *name, u64 data), + TP_ARGS(name, data), + TP_STRUCT__entry( + __string(name_str, name) + __field(u64, data) + ), + TP_fast_assign( + __assign_str(name_str); + __entry->data = data; + ), + TP_printk("%s: %lld", + __get_str(name_str), __entry->data) +); + +DEFINE_EVENT(dpu_uint64_t_data_template, u64_data, + TP_PROTO(const char *name, u64 data), + TP_ARGS(name, data) +); + +TRACE_EVENT(spacemit_dpu_reg_write, + TP_PROTO(u32 data, u32 base, const char *name), + TP_ARGS(data, base, name), + TP_STRUCT__entry( + __field(u32, data) + __field(u32, base) + __string(name_str, name) + ), + TP_fast_assign( + __entry->data = data; + __entry->base = base; + __assign_str(name_str); + ), + TP_printk("dpu write 0x%x to addr 0x%x field %s", + __entry->data, __entry->base, __get_str(name_str)) +); + +TRACE_EVENT(spacemit_plane_disable_hw_channel, + TP_PROTO(u32 layer_id, u32 rdma_id), + TP_ARGS(layer_id, rdma_id), + TP_STRUCT__entry( + __field(u32, layer_id) + __field(u32, rdma_id) + ), + TP_fast_assign( + __entry->layer_id = layer_id; + __entry->rdma_id = rdma_id; + ), + TP_printk("layer_id:%d rdma_id:%d", + __entry->layer_id, __entry->rdma_id) +); + +DECLARE_EVENT_CLASS(dpu_func_template, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id), + TP_STRUCT__entry( + __field(u32, dev_id) + ), + TP_fast_assign( + __entry->dev_id = dev_id; + ), + TP_printk("dev_id=%d", __entry->dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_check, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_update, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_duplicate_state, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_plane_atomic_destroy_state, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_plane_init, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, dpu_enable_clocks, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, dpu_disable_clocks, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, dpu_uninit, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, dpu_isr, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, dpu_run, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_enable, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_disable, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_check, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_begin, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_atomic_flush, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_enable_vblank, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_disable_vblank, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_run, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_init, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, spacemit_crtc_uninit, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DEFINE_EVENT(dpu_func_template, saturn_ctrl_cfg_ready_timer, + TP_PROTO(u32 dev_id), + TP_ARGS(dev_id) +); +DECLARE_EVENT_CLASS(dpu_ctrl_template, + TP_PROTO(int id, int enable), + TP_ARGS(id, enable), + TP_STRUCT__entry( + __field(u32, id) + __field(u32, enable) + ), + TP_fast_assign( + __entry->id = id; + __entry->enable = enable; + ), + TP_printk("id=%d enable:%d", + __entry->id, __entry->enable) +); +DEFINE_EVENT(dpu_ctrl_template, saturn_ctrl_cfg_ready, + TP_PROTO(int id, int enable), + TP_ARGS(id, enable) +); + +DEFINE_EVENT(dpu_ctrl_template, saturn_ctrl_sw_start, + TP_PROTO(int id, int enable), + TP_ARGS(id, enable) +); + +TRACE_EVENT(plat_cmdlist_dump_node, + TP_PROTO(char *dump), + TP_ARGS(dump), + TP_STRUCT__entry( + __string(dump_str, dump) + ), + TP_fast_assign( + __assign_str(dump_str); + ), + TP_printk("%s", __get_str(dump_str)) +); + +TRACE_EVENT(cmdlist_dump_node, + TP_PROTO(char *type, int rdma_id, u8 dev_id, int zpos, u16 nod_len), + TP_ARGS(type, rdma_id, dev_id, zpos, nod_len), + TP_STRUCT__entry( + __string(type_str, type) + __field(int, rdma_id) + __field(u8, dev_id) + __field(int, zpos) + __field(u16, nod_len) + ), + TP_fast_assign( + __assign_str(type_str); + __entry->rdma_id = rdma_id; + __entry->dev_id = dev_id; + __entry->zpos = zpos; + __entry->nod_len = nod_len; + ), + TP_printk("cmdlist[%s]: rdma_id:%d crtc:%d zpos:%d rows:%d", + __get_str(type_str), + __entry->rdma_id, + __entry->dev_id, + __entry->zpos, + __entry->nod_len) +); +#endif /* _DPU_TRACE_H_ */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_FILE +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/spacemit/dpu/ + +#define TRACE_INCLUDE_FILE dpu_trace +#include diff --git a/drivers/gpu/drm/spacemit/spacemit_cmdlist.c b/drivers/gpu/drm/spacemit/spacemit_cmdlist.c new file mode 100644 index 0000000000000..f4f14eaa12a0f --- /dev/null +++ b/drivers/gpu/drm/spacemit/spacemit_cmdlist.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025-2026 SpacemiT Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "spacemit_cmdlist.h" +#include "spacemit_crtc.h" +#include "spacemit_dpu_reg.h" +#include "spacemit_drm.h" +#include "dpu/dpu_saturn.h" +#include "dpu/dpu_trace.h" + +struct spacemit_plane_state *cl_to_spacemit_pstate(const struct cmdlist *cl) +{ + return container_of(cl, struct spacemit_plane_state, cl); +} + +struct spacemit_crtc_state *cl_to_spacemit_cstate(const struct cmdlist *cl) +{ + return container_of(cl, struct spacemit_crtc_state, cl); +} + +struct cmdlist *plane_to_cl(struct drm_plane *plane) +{ + struct spacemit_plane_state *spacemit_pstate = + to_spacemit_plane_state(plane->state); + + return &spacemit_pstate->cl; +} + +struct cmdlist *crtc_to_cl(struct drm_crtc *crtc) +{ + struct spacemit_crtc_state *spacemit_cstate = + to_spacemit_crtc_state(crtc->state); + + return &spacemit_cstate->cl; +} + +static void cmdlist_dump_node(struct cmdlist *cl) +{ + struct spacemit_drm_private *priv; + struct spacemit_hw_device *hwdev; + struct spacemit_crtc *a_crtc; + char *type; + int zpos = -1, rdma_id = -1; + + if (!cl) + return; + + if (cl->type == CMDLIST_PLANE) { + struct spacemit_plane_state *spacemit_pstate = + cl_to_spacemit_pstate(cl); + + a_crtc = to_spacemit_crtc(spacemit_pstate->state.crtc); + rdma_id = spacemit_pstate->rdma_id; + zpos = (int)(spacemit_pstate->state.zpos); + type = "RDMA"; + } else if (cl->type == CMDLIST_CRTC) { + struct spacemit_crtc_state *spacemit_cstate = + cl_to_spacemit_cstate(cl); + + a_crtc = to_spacemit_crtc(spacemit_cstate->base.crtc); + type = "CRTC"; + } else { + return; + } + + trace_cmdlist_dump_node(type, rdma_id, a_crtc->dev_id, zpos, + cl->nod_len); + + priv = a_crtc->crtc.dev->dev_private; + hwdev = priv->hwdev; + hwdev->cmdlist_dump_node(cl); +} + +void cmdlist_regs_packing(struct cmdlist *cl, enum cmdlist_mode_type mod, + struct cmdlist_regs *cl_regs) +{ + struct spacemit_crtc *a_crtc; + struct spacemit_drm_private *priv; + struct spacemit_hw_device *hwdev; + u32 *reg_base; + int i; + u32 index; + + if (!cl_regs) + return; + + if (cl->type == CMDLIST_PLANE) { + struct spacemit_plane_state *spacemit_pstate = + cl_to_spacemit_pstate(cl); + + a_crtc = to_spacemit_crtc(spacemit_pstate->state.crtc); + + drm_dbg(a_crtc->crtc.dev, "rch_id = %d, ch_y = %u\n", + spacemit_pstate->rdma_id, + spacemit_pstate->state.crtc_y); + } else if (cl->type == CMDLIST_CRTC) { + struct spacemit_crtc_state *spacemit_cstate = + cl_to_spacemit_cstate(cl); + + a_crtc = to_spacemit_crtc(spacemit_cstate->base.crtc); + } else { + WARN_ONCE(1, "unsupported cmdlist type %d\n", cl->type); + return; + } + + priv = a_crtc->crtc.dev->dev_private; + hwdev = priv->hwdev; + reg_base = cl_regs->module; + + cl->mode_mask |= mod; + + /* + * Without the atomic_check() buffer the staged register writes vanish. + */ + if (drm_WARN_ON(a_crtc->crtc.dev, !cl->va)) + return; + + for (i = 0; i < cl_regs->size;) { + u32 value[CMDLIST_ROW_REGS] = {0x0}; + u32 offset, strobe = 0; + u8 regs_in_row = 0; + + if (cl_regs->flags[i]) { + offset = cl_regs->base + i * sizeof(u32); + for (u8 j = 0; j < CMDLIST_ROW_REGS; j++) { + index = i + j; + if (likely(index < cl_regs->size)) { + if (cl_regs->flags[index]) { + value[j] = reg_base[index]; + regs_in_row++; + strobe |= CMDLIST_REG_STROBE(j); + } + } else { + break; + } + } + hwdev->cmdlist_fill_data_row(cl, strobe, offset, value); + /* + * the row covers the whole window; a shorter stride + * would revisit covered words as duplicate rows + */ + i += CMDLIST_ROW_REGS; + cl->nod_len++; + } else { + i++; + } + } + + drm_dbg(a_crtc->crtc.dev, "row_num = %d\n", cl->nod_len); +} + +void cmdlist_sort_by_group(struct drm_crtc *crtc) +{ + struct cmdlist *first_cl; + struct cmdlist *last_cl; + struct cmdlist *p; + struct cmdlist *prev; + struct drm_plane *plane; + struct spacemit_crtc_rdma *rdmas = + to_spacemit_crtc_state(crtc->state)->rdmas; + struct spacemit_drm_private *priv = crtc->dev->dev_private; + + drm_atomic_crtc_for_each_plane(plane, crtc) { + struct spacemit_plane_state *spacemit_pstate = + to_spacemit_plane_state(plane->state); + u32 rdma_id = spacemit_pstate->rdma_id; + + first_cl = &spacemit_pstate->cl; + if (!first_cl->va) + continue; + + last_cl = first_cl; + last_cl->next = NULL; + + rdmas[rdma_id].in_use = true; + if (priv->cmdlist_groups[rdma_id]) { + p = priv->cmdlist_groups[rdma_id]; + prev = NULL; + while (p) { + if (cl_to_spacemit_pstate(p)->state.crtc_y < + spacemit_pstate->state.crtc_y) { + prev = p; + p = p->next; + } else { + break; + } + } + if (!prev) { + priv->cmdlist_groups[rdma_id] = first_cl; + last_cl->next = p; + } else { + prev->next = first_cl; + last_cl->next = p; + } + } else { + priv->cmdlist_groups[rdma_id] = first_cl; + } + } +} + +void cmdlist_atomic_commit(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + int i; + struct cmdlist *cur_cl, *first_cl; + struct spacemit_crtc *a_crtc = NULL; + u32 chy, addrl, addrh; + struct spacemit_drm_private *priv = crtc->dev->dev_private; + struct spacemit_hw_device *hwdev = priv->hwdev; + struct spacemit_crtc_rdma *cur_rdmas = + to_spacemit_crtc_state(crtc->state)->rdmas; + struct spacemit_crtc_rdma *old_rdmas = + to_spacemit_crtc_state(old_state)->rdmas; + + a_crtc = to_spacemit_crtc(crtc); + + for (i = 0; i < hwdev->rdma_nums; i++) { + if (old_rdmas[i].in_use) + hwdev->enable_cmdlist(a_crtc, hwdev, i, false); + + if (cur_rdmas[i].in_use) { + drm_dbg(crtc->dev, "cmdlist group = %d\n", i); + cur_cl = priv->cmdlist_groups[i]; + first_cl = cur_cl; + while (cur_cl) { + hwdev->cmdlist_fill_conf_row(cur_cl, hwdev, + CMDLIST_CMP_INVALID); + cmdlist_dump_node(cur_cl); + cur_cl = cur_cl->next; + } + chy = cl_to_spacemit_pstate(first_cl)->state.crtc_y; + addrl = (lower_32_bits(priv->cmdlist_groups[i]->pa) & + CMDLIST_ADDRL_ALIGN_MASK) >> CMDLIST_ADDRL_ALIGN_BITS; + + addrh = upper_32_bits(priv->cmdlist_groups[i]->pa); + hwdev->cfg_cmdlist(hwdev, i, chy, addrl, addrh); + hwdev->enable_cmdlist(a_crtc, hwdev, i, true); + priv->cmdlist_groups[i] = NULL; + } + } +} + +void crtc_cmdlist_sort_by_group(struct spacemit_crtc *a_crtc) +{ + struct cmdlist *first_cl; + struct cmdlist *last_cl; + struct drm_crtc *crtc = &a_crtc->crtc; + struct spacemit_drm_private *priv = crtc->dev->dev_private; + struct spacemit_hw_device *hwdev = priv->hwdev; + struct drm_crtc_state *crtc_state = a_crtc->crtc.state; + struct spacemit_crtc_state *spacemit_crtc_state = + to_spacemit_crtc_state(crtc_state); + u8 crtc_id = drm_crtc_index(crtc); + + first_cl = &spacemit_crtc_state->cl; + last_cl = first_cl; + last_cl->next = NULL; + priv->cmdlist_groups[hwdev->rdma_nums + crtc_id] = first_cl; +} + +void crtc_cmdlist_atomic_commit(struct spacemit_crtc *a_crtc) +{ + struct cmdlist *cur_cl; + struct drm_crtc *crtc = &a_crtc->crtc; + struct spacemit_drm_private *priv = crtc->dev->dev_private; + struct spacemit_hw_device *hwdev = priv->hwdev; + u8 crtc_id = drm_crtc_index(crtc); + u8 dev_id = a_crtc->dev_id; + + drm_dbg(crtc->dev, "cmdlist group = %d\n", + hwdev->rdma_nums + crtc_id); + cur_cl = priv->cmdlist_groups[hwdev->rdma_nums + crtc_id]; + while (cur_cl) { + hwdev->cmdlist_fill_conf_row(cur_cl, hwdev, dev_id); + cmdlist_dump_node(cur_cl); + cur_cl = cur_cl->next; + } + hwdev->crtc_cmdlist(hwdev, priv, crtc_id, dev_id); +} + +/* Called from atomic_check(), where -ENOMEM is still an answer. */ +int spacemit_cmdlist_alloc(struct device *dev, struct cmdlist *cl) +{ + if (cl->va) + return 0; + + cl->size = PER_CMDLIST_SIZE; + cl->va = dma_alloc_coherent(dev, cl->size, &cl->pa, + GFP_KERNEL | __GFP_ZERO); + if (!cl->va) + return -ENOMEM; + + return 0; +} diff --git a/drivers/gpu/drm/spacemit/spacemit_cmdlist.h b/drivers/gpu/drm/spacemit/spacemit_cmdlist.h new file mode 100644 index 0000000000000..d5acdbeb3b0e7 --- /dev/null +++ b/drivers/gpu/drm/spacemit/spacemit_cmdlist.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025-2026 SpacemiT Co., Ltd. + * + */ + +#ifndef _SATURN_FW_CMDLIST_H_ +#define _SATURN_FW_CMDLIST_H_ + +#include +#include +#include +#include + +struct spacemit_crtc; +struct spacemit_plane_state; + +#define PER_CMDLIST_SIZE 4096 +#define CMDLIST_ROW_REGS (3) +#define CMDLIST_CMP_INVALID (0xff) +/* poison for the per-cmdlist compose sentinels, never written to hw */ +#define CMDLIST_SENTINEL_POISON 0xDEADBEEF + +/** + * enum cmdlist_type - what surface owns a cmdlist instance + * @CMDLIST_PLANE: Per-plane RDMA register batches. + * @CMDLIST_CRTC: Compose-side batches submitted on every flush. + */ +enum cmdlist_type { + CMDLIST_PLANE = 1, + CMDLIST_CRTC, +}; + +/** + * struct cmdlist - A DMA-backed batch of register writes replayed at CFG_RDY + * @nod_len: Number of register-write rows currently filled. + * @size: Capacity of the DMA buffer in bytes. + * @mode_mask: cmdlist_mode_type sources; gates which rows are pulled. + * @va: Kernel virtual address of the DMA buffer. + * @pa: DMA address programmed into the cmdlist base register. + * @next: Cmdlists in submission order; the CRTC-level one comes last. + * @type: Originating surface; see &enum cmdlist_type. + * @rch_start_cmps_y: Compositor-Y start sentinel. + * @cmdlist_ch_y_other: Companion sentinel to @rch_start_cmps_y. + */ +struct cmdlist { + u16 nod_len; + u32 size; + u32 mode_mask; + void *va; + dma_addr_t pa; + struct cmdlist *next; + u8 type; + u32 rch_start_cmps_y; + u32 cmdlist_ch_y_other; +}; + +enum cmdlist_mode_type { + CMDLIST_MOD_RDMA = BIT(0), + CMDLIST_MOD_COMP = BIT(3), + CMDLIST_MOD_DMMU = BIT(6), +}; + +#define CMDLIST_REG_STROBE(index) (0xf << ((index) * 4)) + +#define CMDLIST_ADDRL_ALIGN_BITS (4) /* From cmdlist_reg_0[] in CMDLIST_REG */ +#define CMDLIST_ADDRL_ALIGN_MASK \ + ((u32)(~(BIT(CMDLIST_ADDRL_ALIGN_BITS) - 1))) +/* NULL on failure is safe: every consumer falls back to direct MMIO. */ +#define alloc_cmdlist_regs(module_name) \ +({ \ + struct cmdlist_regs *cl = kzalloc(sizeof(*cl), GFP_KERNEL); \ + if (cl) { \ + cl->size = module_name##__NWORDS; \ + cl->module = kzalloc(4 * module_name##__NWORDS, GFP_KERNEL); \ + cl->flags = kzalloc(module_name##__NWORDS, GFP_KERNEL); \ + if (!cl->module || !cl->flags) { \ + kfree(cl->module); \ + kfree(cl->flags); \ + kfree(cl); \ + cl = NULL; \ + } \ + } \ + cl; \ +}) + +#define free_cmdlist_regs(cl_p) \ +do { \ + struct cmdlist_regs *cl = (cl_p); \ + if (cl) { \ + kfree(cl->module); \ + kfree(cl->flags); \ + kfree(cl); \ + } \ +} while (0) + +/* + * First touch seeds the word from a live MMIO read: only register blocks that + * read back what was written may be routed through cmdlist. + */ +#define cmdlist_write_reg(hwdev, module_name, module_base, field, data, cl_regs) \ +do { \ + struct cmdlist_regs *cl_p = (cl_regs); \ + u32 *__words = cl_p->module; \ + const unsigned int __w = dpu_field_word(module_name, field); \ + if (cl_p->flags[__w] == 0) { \ + __words[__w] = readl((hwdev)->base + (module_base) + 4 * __w); \ + cl_p->base = (module_base); \ + cl_p->flags[__w] = 1; \ + } \ + __words[__w] &= ~dpu_field_mask(module_name, field); \ + __words[__w] |= FIELD_PREP(dpu_field_mask(module_name, field), \ + (u32)(data)); \ +} while (0) + +/* Raw whole-word variant (the value32[] call sites); no seeding needed. */ +#define cmdlist_write_word(module_base, word, data, cl_regs) \ +do { \ + struct cmdlist_regs *cl_p = (cl_regs); \ + u32 *__words = cl_p->module; \ + cl_p->base = (module_base); \ + cl_p->flags[(word)] = 1; \ + __words[(word)] = (u32)(data); \ +} while (0) + +struct cmdlist_regs { + u32 base; + u16 size; + u32 *module; + u8 *flags; +}; + +void cmdlist_regs_packing(struct cmdlist *cl, enum cmdlist_mode_type mod, + struct cmdlist_regs *cl_regs); +void cmdlist_sort_by_group(struct drm_crtc *crtc); +void cmdlist_atomic_commit(struct drm_crtc *crtc, + struct drm_crtc_state *old_state); +struct spacemit_plane_state *cl_to_spacemit_pstate(const struct cmdlist *cl); +void crtc_cmdlist_sort_by_group(struct spacemit_crtc *a_crtc); +void crtc_cmdlist_atomic_commit(struct spacemit_crtc *a_crtc); +struct spacemit_crtc_state *cl_to_spacemit_cstate(const struct cmdlist *cl); +int spacemit_cmdlist_alloc(struct device *dev, struct cmdlist *cl); +struct cmdlist *plane_to_cl(struct drm_plane *plane); +struct cmdlist *crtc_to_cl(struct drm_crtc *crtc); + +#endif /* _SATURN_FW_CMDLIST_H_ */ diff --git a/drivers/gpu/drm/spacemit/spacemit_crtc.h b/drivers/gpu/drm/spacemit/spacemit_crtc.h new file mode 100644 index 0000000000000..7c03102414d00 --- /dev/null +++ b/drivers/gpu/drm/spacemit/spacemit_crtc.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025-2026 SpacemiT Co., Ltd. + * + */ + +#ifndef _SPACEMIT_CRTC_H_ +#define _SPACEMIT_CRTC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include