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 EA9273876CC; Sat, 25 Jul 2026 04:51:24 +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=1784955085; cv=none; b=Qs+XlnzSUsiNoW6/7I9nh/RrlJ65avxinL7M1gFa5p3oY3khBqav3eWOpr7M5IRlfiGdPLSjM87mKHhJQs7OlzPIqw7MH01B1nMjwwCOcEFh+J/pT93UwsoGLwiZ1HQx0SaQSDoK64vI83BnHzuZn42g4mpQ52rdWZnfNoo9Fi0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784955085; c=relaxed/simple; bh=52g3Q79Zbmp9v9BCSs9XcTldDgIFbi7tCat3qHn0jk8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kWjVWmIjiLWNx6xuvQcKRVptzcinbOw8QBxzPz44vzujm/5zzJmNKLI+dOF4E/d8wIIoYGyWVEE1xM9U1txBiYbzzq+j6C1hMVhVN7AhkXo2UutQ+Eq77mpz+tj7g+7Rawt76YD3J91aGabKOZr4/WAUZJNq2LKhKyL5I/wbF1I= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OlEbM0nf; 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="OlEbM0nf" Received: by smtp.kernel.org (Postfix) with ESMTPS id 94F09C2BCFF; Sat, 25 Jul 2026 04:51:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784955084; bh=52g3Q79Zbmp9v9BCSs9XcTldDgIFbi7tCat3qHn0jk8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=OlEbM0nfT8ewmov0jFD/uNgYfhvWGWwObHV9azMflXHNN1RjDh53Rmq5vFofZo7mv DXE3xfArRovhCnFVqEiY4i+wa858zr6o2hD68c5JonYMmK9QWmHm1JNGgriBSXf4D0 SVv7mVADxMTzSTUD7/NlHafnwdnUSNsEaVFlXSWLkQbrAUHH5FIZmOPl4cVPT63QGd t8EJCFBiVCa6OoFxJ9gvpmC0K1TkW32CuquVXUka6FgzWKF/PZgtQYKibuZ/+N+N8l iClH9hzewWmOYaCkUPb+0hQfXSPVwSqaHhgE5MDjamxgTCHtNegx+8FFIv6aakT+Iq 6t/so78O+AaBw== 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 82098C531D0; Sat, 25 Jul 2026 04:51:24 +0000 (UTC) From: Cody Kang via B4 Relay Date: Sat, 25 Jul 2026 00:51:17 -0400 Subject: [PATCH RESEND 08/17] drm/spacemit: add Saturn DPU core types, cmdlist and display MMU Precedence: bulk X-Mailing-List: devicetree@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: <20260725-k3-display-v1-8-6de34d80e86c@gmail.com> References: <20260725-k3-display-v1-0-6de34d80e86c@gmail.com> In-Reply-To: <20260725-k3-display-v1-0-6de34d80e86c@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 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.15.1 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784955080; l=46312; i=codykang.hk@gmail.com; s=codykang; h=from:subject:message-id; bh=/eeHajJV4c26AyKGJWUukzQ1CY+46Gcqa3NtAW/VMdY=; b=1Pn9n3rNG9hzRu058NB7jyS+bRFZ4kpvvjweLzzQKEvuNWkm8T/q0J65sYtBrdWuGbX9ZhPyz qI96q9yRsovCtGPSu5+qzCtbNLrojsIsVmrn4JDAGwMzgLtrZsFBXdl 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 --- 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 | 259 ++++++++++++++++++++ drivers/gpu/drm/spacemit/spacemit_dmmu.c | 103 ++++++++ drivers/gpu/drm/spacemit/spacemit_dmmu.h | 40 ++++ drivers/gpu/drm/spacemit/spacemit_dpu_reg.h | 108 +++++++++ drivers/gpu/drm/spacemit/spacemit_drm.h | 68 ++++++ 9 files changed, 1413 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 000000000000..22d77d56eca1 --- /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 000000000000..abfe34a67a4b --- /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 000000000000..787d38e9e135 --- /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(cur_cl, 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 000000000000..d5acdbeb3b0e --- /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 000000000000..27754866bc9a --- /dev/null +++ b/drivers/gpu/drm/spacemit/spacemit_crtc.h @@ -0,0 +1,259 @@ +/* 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