From: Cody Kang via B4 Relay <devnull+codykang.hk.gmail.com@kernel.org>
To: David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>,
Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
Maxime Ripard <mripard@kernel.org>,
Thomas Zimmermann <tzimmermann@suse.de>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>, Yixun Lan <dlan@kernel.org>,
Vinod Koul <vkoul@kernel.org>,
Neil Armstrong <neil.armstrong@linaro.org>,
Haylen Chu <heylenay@4d2.org>,
Michael Turquette <mturquette@baylibre.com>,
Stephen Boyd <sboyd@kernel.org>,
Brian Masney <bmasney@redhat.com>,
Philipp Zabel <p.zabel@pengutronix.de>,
Paul Walmsley <pjw@kernel.org>,
Palmer Dabbelt <palmer@dabbelt.com>,
Albert Ou <aou@eecs.berkeley.edu>,
Alexandre Ghiti <alex@ghiti.fr>
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 <codykang.hk@gmail.com>
Subject: [PATCH RESEND 10/17] drm/spacemit: add Saturn DPU KMS pipeline
Date: Sat, 25 Jul 2026 00:51:19 -0400 [thread overview]
Message-ID: <20260725-k3-display-v1-10-6de34d80e86c@gmail.com> (raw)
In-Reply-To: <20260725-k3-display-v1-0-6de34d80e86c@gmail.com>
From: Cody Kang <codykang.hk@gmail.com>
Add the atomic KMS implementation on top of the hardware backend: one
CRTC and one primary plane per DPU instance. atomic_check validates
the plane rectangle 1:1 against the mode and sizes the per-channel FBC
line buffer; atomic commit builds the cmdlist batches, maps the
framebuffer through the DMMU and arms the cfg-ready handshake, with
vblank events driven from the DPU interrupt.
Signed-off-by: Cody Kang <codykang.hk@gmail.com>
---
drivers/gpu/drm/spacemit/spacemit_crtc.c | 815 +++++++++++++++++++++++++++++
drivers/gpu/drm/spacemit/spacemit_planes.c | 376 +++++++++++++
2 files changed, 1191 insertions(+)
diff --git a/drivers/gpu/drm/spacemit/spacemit_crtc.c b/drivers/gpu/drm/spacemit/spacemit_crtc.c
new file mode 100644
index 000000000000..b75ff6320501
--- /dev/null
+++ b/drivers/gpu/drm/spacemit/spacemit_crtc.c
@@ -0,0 +1,815 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 SpacemiT Co., Ltd.
+ *
+ */
+
+#include <drm/drm_print.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/clients/drm_client_setup.h>
+#include <linux/aperture.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/component.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pm_qos.h>
+#include <linux/property.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/trace_events.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include "spacemit_dpu_ids.h"
+#include <linux/reset.h>
+#include "spacemit_cmdlist.h"
+#include "spacemit_dmmu.h"
+#include "spacemit_drm.h"
+#include "spacemit_crtc.h"
+#include "dpu/dpu_saturn.h"
+#include "dpu/dpu_trace.h"
+
+static int spacemit_crtc_init(struct spacemit_crtc *a_crtc);
+static int spacemit_crtc_uninit(struct spacemit_crtc *a_crtc);
+static int dpu_pm_suspend(struct device *dev);
+static int dpu_pm_resume(struct device *dev);
+static void dpu_reset_assert(struct device *dev, const char *name,
+ struct reset_control *rstc);
+
+static void saturn_count_rdma_use(struct drm_crtc_state *crtc_state)
+{
+ struct drm_plane *plane;
+ struct spacemit_crtc_state *spacemit_state =
+ to_spacemit_crtc_state(crtc_state);
+ struct spacemit_crtc_rdma *rdmas = spacemit_state->rdmas;
+ u32 rdma_id = 0;
+ const struct drm_plane_state *pstate;
+
+ drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
+ rdma_id = to_spacemit_plane_state(pstate)->rdma_id;
+ if (rdma_id != RDMA_INVALID_ID)
+ rdmas[rdma_id].use_cnt++;
+ }
+}
+
+static int spacemit_crtc_atomic_check_fbmem(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state)
+{
+ struct spacemit_crtc_rdma *rdmas =
+ to_spacemit_crtc_state(crtc_state)->rdmas;
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+ struct spacemit_drm_private *priv = crtc->dev->dev_private;
+ struct spacemit_hw_device *hwdev = priv->hwdev;
+ const struct drm_plane_state *pstate;
+ struct drm_plane *plane;
+
+ drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) {
+ u32 rdma_id = to_spacemit_plane_state(pstate)->rdma_id;
+ u32 layer_fbcmem_size =
+ to_spacemit_plane_state(pstate)->fbcmem_size;
+
+ if (rdma_id != RDMA_INVALID_ID)
+ rdmas[rdma_id].fbcmem.size = max(layer_fbcmem_size,
+ rdmas[rdma_id].fbcmem.size);
+ }
+
+ return a_crtc->core->adjust_rdma_fbcmem(hwdev, rdmas);
+}
+
+static void spacemit_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *old_state)
+{
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+ int idx;
+
+ if (!drm_dev_enter(crtc->dev, &idx))
+ return;
+
+ trace_spacemit_crtc_atomic_enable(a_crtc->dev_id);
+
+ /*
+ * U-Boot leaves its boot-logo pipeline running. Hard-stop it with the
+ * resets alone; the clocks are not ours to gate until we have enabled
+ * them.
+ */
+ if (unlikely(a_crtc->first_modeset)) {
+ dpu_reset_assert(a_crtc->dev, "lcd", a_crtc->lcd_reset);
+ dpu_reset_assert(a_crtc->dev, "esc", a_crtc->esc_reset);
+ dpu_reset_assert(a_crtc->dev, "mclk", a_crtc->mclk_reset);
+ dpu_reset_assert(a_crtc->dev, "aclk", a_crtc->aclk_reset);
+ dpu_reset_assert(a_crtc->dev, "dsc", a_crtc->dsc_reset);
+ usleep_range(10000, 20000);
+ a_crtc->first_modeset = false;
+ }
+
+ if (!a_crtc->clocks_on) {
+ a_crtc->clocks_on = true;
+ dpu_pm_resume(a_crtc->dev);
+
+ /*
+ * The DPU must settle after reset deassert or CFG_RDY races
+ * the flush.
+ */
+ usleep_range(10000, 20000);
+ }
+
+ drm_crtc_vblank_on(&a_crtc->crtc);
+
+ spacemit_crtc_init(a_crtc);
+
+ drm_dev_exit(idx);
+}
+
+static void spacemit_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *old_state)
+{
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+ struct drm_device *drm = a_crtc->crtc.dev;
+ int idx;
+
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+ trace_spacemit_crtc_atomic_disable(a_crtc->dev_id);
+
+ spacemit_crtc_uninit(a_crtc);
+
+ drm_crtc_vblank_off(&a_crtc->crtc);
+
+ /*
+ * MMIO on a clock-gated DPU stalls the bus, so an in-flight handler
+ * must finish its status reads before dpu_pm_suspend() gates the
+ * clocks.
+ */
+ if (a_crtc->irq_online > 0)
+ synchronize_irq(a_crtc->irq_online);
+
+ /*
+ * Nothing else cancels the watchdog; it would fire against freed state.
+ */
+ timer_delete_sync(&a_crtc->cfg_rdy_timer);
+
+ /* Close the ISR's MMIO gate before the clocks actually stop. */
+ a_crtc->clocks_on = false;
+ dpu_pm_suspend(a_crtc->dev);
+
+ spin_lock_irq(&drm->event_lock);
+ if (crtc->state->event) {
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ crtc->state->event = NULL;
+ }
+ spin_unlock_irq(&drm->event_lock);
+
+ drm_dev_exit(idx);
+}
+
+static int spacemit_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *atomic_state)
+{
+ struct drm_crtc_state *state =
+ drm_atomic_get_new_crtc_state(atomic_state, crtc);
+ struct spacemit_crtc_state *cstate = to_spacemit_crtc_state(state);
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+ int ret = 0;
+
+ trace_spacemit_crtc_atomic_check(a_crtc->dev_id);
+
+ ret = spacemit_crtc_atomic_check_fbmem(crtc, state);
+ if (ret) {
+ drm_dbg_kms(crtc->dev,
+ "failed to satisfy fbcmem size for all rdmas\n");
+ return ret;
+ }
+
+ saturn_count_rdma_use(state);
+
+ ret = spacemit_cmdlist_alloc(a_crtc->dev, &cstate->cl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void spacemit_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+
+ trace_spacemit_crtc_atomic_begin(a_crtc->dev_id);
+}
+
+static void spacemit_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
+ crtc);
+ int idx;
+
+ if (!drm_dev_enter(crtc->dev, &idx))
+ return;
+
+ trace_spacemit_crtc_atomic_flush(a_crtc->dev_id);
+
+ spacemit_crtc_run(crtc, old_state);
+
+ drm_dev_exit(idx);
+}
+
+static struct drm_crtc_state *
+spacemit_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+ struct spacemit_crtc_state *state;
+ struct spacemit_drm_private *priv = crtc->dev->dev_private;
+ struct spacemit_hw_device *hwdev = priv->hwdev;
+ u8 n_rdma;
+
+ if (WARN_ON(!crtc->state))
+ return NULL;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return NULL;
+
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
+
+ n_rdma = hwdev->rdma_nums;
+ state->rdmas = kzalloc_objs(*state->rdmas, n_rdma);
+ if (!state->rdmas) {
+ kfree(state);
+ return NULL;
+ }
+
+ state->cl.type = CMDLIST_CRTC;
+
+ return &state->base;
+}
+
+static void spacemit_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct spacemit_crtc_state *spacemit_state = NULL;
+ struct spacemit_crtc *a_crtc = NULL;
+
+ if (state) {
+ spacemit_state = to_spacemit_crtc_state(state);
+ __drm_atomic_helper_crtc_destroy_state(state);
+ a_crtc = to_spacemit_crtc(crtc);
+ if (spacemit_state->cl.va)
+ dma_free_coherent(a_crtc->dev,
+ spacemit_state->cl.size,
+ spacemit_state->cl.va,
+ spacemit_state->cl.pa);
+ kfree(spacemit_state->rdmas);
+ kfree(spacemit_state);
+ }
+}
+
+static void spacemit_crtc_reset(struct drm_crtc *crtc)
+{
+ struct spacemit_drm_private *priv = crtc->dev->dev_private;
+ struct spacemit_hw_device *hwdev = priv->hwdev;
+ struct spacemit_crtc_state *state;
+
+ if (crtc->state)
+ spacemit_crtc_destroy_state(crtc, crtc->state);
+
+ state = kzalloc_obj(*state);
+ if (!state) {
+ __drm_atomic_helper_crtc_reset(crtc, NULL);
+ return;
+ }
+
+ state->rdmas = kzalloc_objs(*state->rdmas, hwdev->rdma_nums);
+ if (!state->rdmas) {
+ kfree(state);
+ __drm_atomic_helper_crtc_reset(crtc, NULL);
+ return;
+ }
+
+ __drm_atomic_helper_crtc_reset(crtc, &state->base);
+}
+
+static int spacemit_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+
+ trace_spacemit_crtc_enable_vblank(a_crtc->dev_id);
+
+ if (a_crtc->core && a_crtc->core->enable_vsync)
+ a_crtc->core->enable_vsync(a_crtc);
+
+ return 0;
+}
+
+static void spacemit_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+
+ trace_spacemit_crtc_disable_vblank(a_crtc->dev_id);
+
+ if (a_crtc->core && a_crtc->core->disable_vsync)
+ a_crtc->core->disable_vsync(a_crtc);
+}
+
+static const struct drm_crtc_helper_funcs spacemit_crtc_helper_funcs = {
+ .atomic_check = spacemit_crtc_atomic_check,
+ .atomic_begin = spacemit_crtc_atomic_begin,
+ .atomic_flush = spacemit_crtc_atomic_flush,
+ .atomic_enable = spacemit_crtc_atomic_enable,
+ .atomic_disable = spacemit_crtc_atomic_disable,
+};
+
+static const struct drm_crtc_funcs spacemit_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = spacemit_crtc_reset,
+ .atomic_duplicate_state = spacemit_crtc_duplicate_state,
+ .atomic_destroy_state = spacemit_crtc_destroy_state,
+ .enable_vblank = spacemit_crtc_enable_vblank,
+ .disable_vblank = spacemit_crtc_disable_vblank,
+};
+
+static void spacemit_crtc_put_port(struct drm_device *drm, void *port)
+{
+ of_node_put(port);
+}
+
+static int spacemit_crtc_bind_init(struct drm_device *drm, struct drm_crtc *crtc,
+ struct drm_plane *primary, struct device_node *port)
+{
+ int err;
+
+ /*
+ * drm_of_find_possible_crtcs() walks crtc->port for the life of the
+ * drm_device.
+ */
+ crtc->port = of_node_get(port);
+ err = drmm_add_action_or_reset(drm, spacemit_crtc_put_port, port);
+ if (err)
+ return err;
+
+ err = drm_crtc_init_with_planes(drm, crtc, primary, NULL,
+ &spacemit_crtc_funcs, NULL);
+ if (err) {
+ drm_err(drm, "failed to init crtc\n");
+ return err;
+ }
+
+ drm_mode_crtc_set_gamma_size(crtc, 256);
+
+ drm_crtc_helper_add(crtc, &spacemit_crtc_helper_funcs);
+
+ return 0;
+}
+
+int spacemit_crtc_run(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(crtc);
+
+ trace_spacemit_crtc_run(a_crtc->dev_id);
+
+ if (a_crtc->core && a_crtc->core->run)
+ a_crtc->core->run(crtc, old_state);
+
+ return 0;
+}
+
+static int spacemit_crtc_init(struct spacemit_crtc *a_crtc)
+{
+ trace_spacemit_crtc_init(a_crtc->dev_id);
+
+ if (a_crtc->core && a_crtc->core->init)
+ a_crtc->core->init(a_crtc);
+
+ a_crtc->is_1st_f = true;
+
+ return 0;
+}
+
+static int spacemit_crtc_uninit(struct spacemit_crtc *a_crtc)
+{
+ trace_spacemit_crtc_uninit(a_crtc->dev_id);
+
+ if (a_crtc->core && a_crtc->core->uninit)
+ a_crtc->core->uninit(a_crtc);
+
+ return 0;
+}
+
+static irqreturn_t spacemit_dpu_isr(int irq, void *data)
+{
+ struct spacemit_drm_private *priv = data;
+ struct spacemit_crtc *a_crtc = priv->a_crtc;
+ int idx;
+
+ if (!a_crtc)
+ return IRQ_NONE;
+
+ if (!drm_dev_enter(a_crtc->crtc.dev, &idx))
+ return IRQ_NONE;
+
+ /* Reading a clock-gated DPU's status registers stalls the bus. */
+ if (unlikely(!a_crtc->clocks_on)) {
+ drm_dev_exit(idx);
+ return IRQ_NONE;
+ }
+
+ if (a_crtc->core && a_crtc->core->online_isr)
+ a_crtc->core->online_isr(a_crtc);
+
+ drm_dev_exit(idx);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Acquire at probe, not at bind: the aggregate can rebind without this device
+ * detaching, and the exclusive resets would not be handed out a second time.
+ */
+static int spacemit_dpu_hw_resources_init(struct spacemit_drm_private *priv,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = dpu_parse_clocks(dev, &priv->clk_ctx);
+ if (ret)
+ return ret;
+
+ priv->mclk_reset = devm_reset_control_get_optional_exclusive(dev,
+ "mclk");
+ if (IS_ERR(priv->mclk_reset))
+ return dev_err_probe(dev, PTR_ERR(priv->mclk_reset),
+ "failed to get mclk reset\n");
+ priv->esc_reset = devm_reset_control_get_optional_exclusive(dev, "esc");
+ if (IS_ERR(priv->esc_reset))
+ return dev_err_probe(dev, PTR_ERR(priv->esc_reset),
+ "failed to get esc reset\n");
+ priv->lcd_reset = devm_reset_control_get_optional_exclusive(dev, "lcd");
+ if (IS_ERR(priv->lcd_reset))
+ return dev_err_probe(dev, PTR_ERR(priv->lcd_reset),
+ "failed to get lcd reset\n");
+ priv->aclk_reset = devm_reset_control_get_optional_exclusive(dev,
+ "aclk");
+ if (IS_ERR(priv->aclk_reset))
+ return dev_err_probe(dev, PTR_ERR(priv->aclk_reset),
+ "failed to get aclk reset\n");
+ priv->dsc_reset = devm_reset_control_get_optional_exclusive(dev, "dsc");
+ if (IS_ERR(priv->dsc_reset))
+ return dev_err_probe(dev, PTR_ERR(priv->dsc_reset),
+ "failed to get dsc reset\n");
+
+ priv->irq_online = platform_get_irq_byname(pdev, "online");
+ if (priv->irq_online < 0)
+ return priv->irq_online;
+
+ /*
+ * Keep the line disabled until bind: the handler needs bind-created
+ * state, and a bootloader-left display can raise an interrupt during
+ * probe.
+ */
+ ret = devm_request_irq(dev, priv->irq_online, spacemit_dpu_isr,
+ IRQF_NO_AUTOEN, "DPU_ONLINE", priv);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to request online IRQ\n");
+
+ return 0;
+}
+
+static const struct regmap_config spacemit_dpu_regmap_config = {
+ .name = "dpu",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x53ffc, /* 0x54000-byte DPU MMIO from binding */
+};
+
+/* Bind-scope wrapper so devm_drm_dev_alloc has a struct to embed into. */
+struct spacemit_kms {
+ struct drm_device drm;
+};
+
+static int spacemit_drm_master_bind(struct device *dev)
+{
+ struct spacemit_drm_private *priv = dev_get_drvdata(dev);
+ struct device_node *np = dev->of_node;
+ struct device_node *ports = NULL, *port = NULL;
+ struct spacemit_crtc *a_crtc;
+ struct drm_plane *plane;
+ struct spacemit_kms *kms;
+ struct drm_device *drm;
+ int ret;
+
+ ret = aperture_remove_all_conflicting_devices(spacemit_drm_drv.name);
+ if (ret) {
+ dev_err(dev, "failed to remove existing framebuffers: %d\n",
+ ret);
+ return ret;
+ }
+
+ kms = devm_drm_dev_alloc(dev, &spacemit_drm_drv,
+ struct spacemit_kms, drm);
+ if (IS_ERR(kms))
+ return PTR_ERR(kms);
+ drm = &kms->drm;
+ priv->ddev = drm;
+ drm->dev_private = priv;
+
+ ret = spacemit_drm_mode_config_init(drm);
+ if (ret)
+ return ret;
+
+ a_crtc = drmm_kzalloc(drm, sizeof(*a_crtc), GFP_KERNEL);
+ if (!a_crtc) {
+ ret = -ENOMEM;
+ goto err_dc;
+ }
+ a_crtc->dev = dev;
+ a_crtc->first_modeset = true;
+ priv->a_crtc = a_crtc;
+
+ a_crtc->clk_ctx = priv->clk_ctx;
+ a_crtc->aclk = DPU_AXICLK_DEFAULT;
+ a_crtc->mclk_reset = priv->mclk_reset;
+ a_crtc->esc_reset = priv->esc_reset;
+ a_crtc->lcd_reset = priv->lcd_reset;
+ a_crtc->aclk_reset = priv->aclk_reset;
+ a_crtc->dsc_reset = priv->dsc_reset;
+ a_crtc->irq_online = priv->irq_online;
+
+ /* Both K3 DPU instances are COMPOSER1 in the Saturn IRQ tables. */
+ a_crtc->dev_id = COMPOSER1;
+
+ /* The K3 pipeline always hands RGB888 to the DP/eDP encoder. */
+ a_crtc->out_format = OUTFMT_RGB888;
+
+ a_crtc->core = &dpu_saturn_ops;
+
+ timer_setup(&a_crtc->cfg_rdy_timer, spacemit_cfg_rdy_timer_handler, 0);
+
+ plane = spacemit_plane_init(drm, a_crtc);
+ if (IS_ERR(plane)) {
+ ret = PTR_ERR(plane);
+ goto err_dc;
+ }
+
+ ports = of_get_child_by_name(np, "ports");
+ if (!ports) {
+ dev_err(dev, "CRTC %pOF has no ports node\n", np);
+ ret = -EINVAL;
+ goto err_dc;
+ }
+
+ port = of_get_child_by_name(ports, "port");
+ if (!port) {
+ dev_err(dev, "CRTC %pOF has no port@X node\n", np);
+ ret = -EINVAL;
+ goto err_ports_put;
+ }
+
+ ret = spacemit_crtc_bind_init(drm, &a_crtc->crtc, plane, port);
+ of_node_put(port);
+ of_node_put(ports);
+ port = NULL;
+ ports = NULL;
+ if (ret)
+ goto err_dc;
+
+ /*
+ * component_bind_all() unbinds what it bound before returning an error.
+ */
+ ret = component_bind_all(dev, drm);
+ if (ret) {
+ dev_err(dev, "failed to bind all components\n");
+ goto err_dc;
+ }
+
+ ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
+ if (ret) {
+ dev_err(dev, "failed to initialize vblank\n");
+ goto err_unbind_all;
+ }
+
+ drm_mode_config_reset(drm);
+ drmm_kms_helper_poll_init(drm);
+ drm_helper_hpd_irq_event(drm);
+
+ enable_irq(priv->irq_online);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret < 0)
+ goto err_disable_irq;
+ drm_client_setup(drm, NULL);
+
+ return 0;
+
+err_disable_irq:
+ disable_irq(priv->irq_online);
+
+err_unbind_all:
+ component_unbind_all(dev, drm);
+ goto err_dc;
+err_ports_put:
+ of_node_put(port);
+ of_node_put(ports);
+err_dc:
+ /*
+ * .shutdown tests priv->ddev; never leave it on a half-initialised
+ * device.
+ */
+ priv->ddev = NULL;
+ priv->a_crtc = NULL;
+ return ret;
+}
+
+static void spacemit_drm_master_unbind(struct device *dev)
+{
+ struct spacemit_drm_private *priv = dev_get_drvdata(dev);
+ struct drm_device *drm = priv->ddev;
+
+ disable_irq(priv->irq_online);
+
+ drm_dev_unregister(drm);
+ drm_atomic_helper_shutdown(drm);
+ component_unbind_all(dev, drm);
+
+ /* .shutdown and a later re-bind must not touch the stale instance. */
+ priv->ddev = NULL;
+ priv->a_crtc = NULL;
+}
+
+const struct component_master_ops spacemit_drm_master_ops = {
+ .bind = spacemit_drm_master_bind,
+ .unbind = spacemit_drm_master_unbind,
+};
+
+static int spacemit_dpu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct spacemit_hw_device *hwdev_template;
+ struct spacemit_drm_private *priv;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ if (!np) {
+ dev_err(dev, "no OF node for the DPU\n");
+ return -ENODEV;
+ }
+
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set DMA mask\n");
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = dev;
+ dev_set_drvdata(dev, priv);
+
+ /* The match data is shared const; the MMIO mapping is per-instance. */
+ hwdev_template = device_get_match_data(dev);
+ if (!hwdev_template)
+ return -ENODEV;
+ priv->hwdev = devm_kmemdup(dev, hwdev_template,
+ sizeof(*hwdev_template), GFP_KERNEL);
+ if (!priv->hwdev)
+ return -ENOMEM;
+
+ priv->cmdlist_groups = devm_kcalloc(dev,
+ priv->hwdev->rdma_nums + priv->hwdev->crtc_nums,
+ sizeof(*priv->cmdlist_groups), GFP_KERNEL);
+ if (!priv->cmdlist_groups)
+ return -ENOMEM;
+
+ priv->hwdev->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->hwdev->base))
+ return PTR_ERR(priv->hwdev->base);
+
+ priv->hwdev->regmap = devm_regmap_init_mmio(dev, priv->hwdev->base,
+ &spacemit_dpu_regmap_config);
+ if (IS_ERR(priv->hwdev->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->hwdev->regmap),
+ "failed to init DPU regmap\n");
+
+ ret = spacemit_dpu_hw_resources_init(priv, pdev);
+ if (ret)
+ return ret;
+
+ return spacemit_drm_of_component_probe(dev);
+}
+
+static void spacemit_dpu_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &spacemit_drm_master_ops);
+}
+
+static void spacemit_dpu_shutdown(struct platform_device *pdev)
+{
+ struct spacemit_drm_private *priv = dev_get_drvdata(&pdev->dev);
+ struct drm_device *drm = priv->ddev;
+
+ if (!drm) {
+ dev_warn(&pdev->dev,
+ "drm device is not available, no shutdown\n");
+ return;
+ }
+
+ drm_atomic_helper_shutdown(drm);
+}
+
+/*
+ * The assert and deassert orders are not mirror images: the hardware wants
+ * these.
+ */
+static void dpu_reset_assert(struct device *dev, const char *name,
+ struct reset_control *rstc)
+{
+ int ret = reset_control_assert(rstc);
+
+ if (ret)
+ dev_warn(dev, "failed to assert %s reset: %d\n", name, ret);
+}
+
+static void dpu_reset_deassert(struct device *dev, const char *name,
+ struct reset_control *rstc)
+{
+ int ret = reset_control_deassert(rstc);
+
+ if (ret)
+ dev_warn(dev, "failed to deassert %s reset: %d\n", name, ret);
+}
+
+static int dpu_pm_suspend(struct device *dev)
+{
+ struct spacemit_drm_private *priv = dev_get_drvdata(dev);
+ struct spacemit_crtc *a_crtc = priv->a_crtc;
+
+ /*
+ * Assert before gating: a reset asserted into an already-gated block
+ * has no clock edges to propagate on and leaves the register file
+ * untouched.
+ */
+ dpu_reset_assert(dev, "lcd", a_crtc->lcd_reset);
+ dpu_reset_assert(dev, "esc", a_crtc->esc_reset);
+ dpu_reset_assert(dev, "mclk", a_crtc->mclk_reset);
+ dpu_reset_assert(dev, "aclk", a_crtc->aclk_reset);
+ dpu_reset_assert(dev, "dsc", a_crtc->dsc_reset);
+ usleep_range(10000, 20000);
+
+ if (a_crtc->core && a_crtc->core->disable_clk)
+ a_crtc->core->disable_clk(a_crtc);
+
+ return 0;
+}
+
+static int dpu_pm_resume(struct device *dev)
+{
+ struct spacemit_drm_private *priv = dev_get_drvdata(dev);
+ struct spacemit_crtc *a_crtc = priv->a_crtc;
+
+ dpu_reset_deassert(dev, "mclk", a_crtc->mclk_reset);
+ dpu_reset_deassert(dev, "esc", a_crtc->esc_reset);
+ dpu_reset_deassert(dev, "lcd", a_crtc->lcd_reset);
+ dpu_reset_deassert(dev, "aclk", a_crtc->aclk_reset);
+ dpu_reset_deassert(dev, "dsc", a_crtc->dsc_reset);
+
+ if (a_crtc->core && a_crtc->core->enable_clk)
+ a_crtc->core->enable_clk(a_crtc);
+
+ return 0;
+}
+
+static const struct of_device_id dpu_match_table[] = {
+ {
+ .compatible = "spacemit,k3-saturn-dpu",
+ .data = &k3_saturn_dpu,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dpu_match_table);
+
+static struct platform_driver spacemit_dpu_driver = {
+ .probe = spacemit_dpu_probe,
+ .remove = spacemit_dpu_remove,
+ .shutdown = spacemit_dpu_shutdown,
+ .driver = {
+ .name = "spacemit-dpu-drv",
+ .of_match_table = dpu_match_table,
+ .suppress_bind_attrs = true,
+ },
+};
+module_platform_driver(spacemit_dpu_driver);
+
+MODULE_DESCRIPTION("SpacemiT DRM KMS master driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/spacemit/spacemit_planes.c b/drivers/gpu/drm/spacemit/spacemit_planes.c
new file mode 100644
index 000000000000..3442013b0c37
--- /dev/null
+++ b/drivers/gpu/drm/spacemit/spacemit_planes.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026 SpacemiT Co., Ltd.
+ *
+ */
+
+#include <drm/drm_print.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_color_mgmt.h>
+#include <drm/drm_gem.h>
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include "spacemit_cmdlist.h"
+#include "spacemit_dmmu.h"
+#include "spacemit_drm.h"
+#include "dpu/dpu_saturn.h"
+#include "dpu/dpu_trace.h"
+#include "dpu/saturn_fbcmem.h"
+
+/* The AFBC decoder exists but no modifier has been validated on hardware. */
+static const u64 supported_format_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID
+};
+
+struct spacemit_plane *to_spacemit_plane(struct drm_plane *plane)
+{
+ return container_of(plane, struct spacemit_plane, plane);
+}
+
+static int spacemit_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *atomic_state)
+{
+ struct drm_plane_state *state =
+ drm_atomic_get_new_plane_state(atomic_state, plane);
+ struct drm_framebuffer *fb = state->fb;
+ u32 src_w, src_h, src_x, src_y;
+ u32 crtc_w, crtc_h;
+ struct spacemit_plane_state *cur_state = to_spacemit_plane_state(state);
+ u32 cur_rdma_id = cur_state->rdma_id;
+ struct spacemit_drm_private *priv = plane->dev->dev_private;
+ struct spacemit_hw_device *hwdev = priv->hwdev;
+ struct spacemit_crtc *a_crtc = NULL;
+
+ struct drm_crtc_state *crtc_state = NULL;
+ int ret;
+
+ if (state->crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(atomic_state,
+ state->crtc);
+
+ ret = drm_atomic_helper_check_plane_state(state, crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ true, false);
+ if (ret)
+ return ret;
+
+ if (!state->visible)
+ return 0;
+
+ a_crtc = to_spacemit_crtc(state->crtc);
+ trace_spacemit_plane_atomic_check(a_crtc->dev_id);
+
+ src_x = state->src_x >> 16;
+ src_y = state->src_y >> 16;
+ src_w = state->src_w >> 16;
+ src_h = state->src_h >> 16;
+ crtc_w = state->crtc_w;
+ crtc_h = state->crtc_h;
+
+ /* The programming paths use the raw rectangles, so refuse to clip. */
+ if ((state->src.x1 >> 16) != src_x || (state->src.y1 >> 16) != src_y ||
+ (drm_rect_width(&state->src) >> 16) != src_w ||
+ (drm_rect_height(&state->src) >> 16) != src_h ||
+ state->dst.x1 != state->crtc_x || state->dst.y1 != state->crtc_y ||
+ drm_rect_width(&state->dst) != crtc_w ||
+ drm_rect_height(&state->dst) != crtc_h) {
+ drm_dbg_kms(plane->dev,
+ "clipped plane rectangles not supported\n");
+ return -EINVAL;
+ }
+
+ /* One-dimensional sources are invalid (only 0x0 = constant fill) */
+ if ((src_w == 0) != (src_h == 0)) {
+ drm_dbg_kms(plane->dev, "unsupported src_w %d src_h %d\n",
+ src_w, src_h);
+ return -EINVAL;
+ }
+
+ if (src_w == 0 && src_h == 0)
+ cur_rdma_id = RDMA_INVALID_ID; /* constant-fill layer, no RDMA */
+ else if (cur_rdma_id == RDMA_INVALID_ID)
+ cur_rdma_id = state->zpos; /* first commit: bind channel by zpos */
+ cur_state->rdma_id = cur_rdma_id;
+
+ if (cur_rdma_id != RDMA_INVALID_ID) {
+ if (cur_rdma_id >= hwdev->rdma_nums) {
+ drm_dbg_kms(plane->dev, "invalid rdma id %d\n",
+ cur_rdma_id);
+ return -EINVAL;
+ }
+
+ if (a_crtc->core->cal_layer_fbcmem_size(plane, state)) {
+ drm_dbg_kms(plane->dev,
+ "plane %d: invalid fbcmem size\n",
+ state->zpos);
+ return -EINVAL;
+ }
+
+ cur_state->mmu_tbl.size =
+ ((PAGE_ALIGN(fb->obj[0]->size) >> PAGE_SHIFT) +
+ HW_ALIGN_TTB_NUM) * 4;
+ cur_state->mmu_tbl.va =
+ dma_alloc_coherent(a_crtc->dev, cur_state->mmu_tbl.size,
+ &cur_state->mmu_tbl.pa,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!cur_state->mmu_tbl.va)
+ return -ENOMEM;
+ }
+
+ /*
+ * The commit cannot fail, so take the DMA buffer while -ENOMEM is an
+ * answer.
+ */
+ ret = spacemit_cmdlist_alloc(a_crtc->dev, &cur_state->cl);
+ if (ret)
+ return ret;
+
+ cur_state->format = spacemit_plane_hw_get_format_id(fb->format->format);
+ if (cur_state->format == SPACEMIT_DPU_INVALID_FORMAT_ID) {
+ drm_dbg_kms(plane->dev, "unsupported format %p4cc\n",
+ &fb->format->format);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void spacemit_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ int ret = 0;
+ struct spacemit_crtc *a_crtc = to_spacemit_crtc(plane->state->crtc);
+ struct spacemit_plane_state *spacemit_pstate =
+ to_spacemit_plane_state(plane->state);
+ struct spacemit_drm_private *priv = plane->dev->dev_private;
+ struct spacemit_hw_device *hwdev = priv->hwdev;
+ u32 rdma_id = spacemit_pstate->rdma_id;
+ struct cmdlist *cl = NULL;
+ int idx;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+ return;
+
+ trace_spacemit_plane_atomic_update(a_crtc->dev_id);
+
+ hwdev->get_cl_rdma_buf(a_crtc);
+
+ hwdev->plane_update_hw_channel(plane);
+
+ /* Solid-fill layers have no RDMA and no page table */
+ if (rdma_id < hwdev->rdma_nums && spacemit_pstate->mmu_tbl.va) {
+ u8 tbu_id = rdma_id * 2;
+
+ ret = spacemit_dmmu_map(plane->state->fb,
+ &spacemit_pstate->mmu_tbl, tbu_id,
+ a_crtc->cl_rdma, plane);
+ if (!ret)
+ cmdlist_regs_packing(plane_to_cl(plane),
+ CMDLIST_MOD_RDMA, a_crtc->cl_rdma);
+ else
+ drm_err(plane->dev, "failed to map plane: %d\n", ret);
+ }
+
+ cl = &spacemit_pstate->cl;
+ cl->rch_start_cmps_y = CMDLIST_SENTINEL_POISON;
+ cl->cmdlist_ch_y_other = CMDLIST_SENTINEL_POISON;
+ free_cmdlist_regs(a_crtc->cl_rdma);
+
+ drm_dev_exit(idx);
+}
+
+static void spacemit_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *old_state =
+ drm_atomic_get_old_plane_state(state, plane);
+ struct spacemit_drm_private *priv = plane->dev->dev_private;
+ struct spacemit_hw_device *hwdev = priv->hwdev;
+ int idx;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+ return;
+
+ hwdev->plane_disable_hw_channel(plane, old_state);
+
+ drm_dev_exit(idx);
+}
+
+static void spacemit_plane_reset(struct drm_plane *plane)
+{
+ struct spacemit_plane_state *s;
+
+ if (plane->state) {
+ s = to_spacemit_plane_state(plane->state);
+ __drm_atomic_helper_plane_destroy_state(plane->state);
+ kfree(s);
+ plane->state = NULL;
+ }
+
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (s) {
+ __drm_atomic_helper_plane_reset(plane, &s->state);
+ s->rdma_id = RDMA_INVALID_ID;
+ }
+}
+
+static struct drm_plane_state *
+spacemit_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+ struct spacemit_plane_state *s;
+ struct spacemit_plane_state *old_state =
+ to_spacemit_plane_state(plane->state);
+ struct spacemit_crtc *a_crtc = NULL;
+
+ if (plane->crtc) {
+ a_crtc = to_spacemit_crtc(plane->crtc);
+ trace_spacemit_plane_atomic_duplicate_state(a_crtc->dev_id);
+ }
+
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return NULL;
+
+ __drm_atomic_helper_plane_duplicate_state(plane, &s->state);
+
+ WARN_ON(s->state.plane != plane);
+
+ s->cl.type = CMDLIST_PLANE;
+
+ s->rdma_id = old_state->rdma_id;
+ s->format = old_state->format;
+ s->fbcmem_size = 0;
+
+ return &s->state;
+}
+
+static void spacemit_plane_atomic_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct spacemit_plane_state *spacemit_pstate =
+ to_spacemit_plane_state(state);
+ struct spacemit_crtc *a_crtc = NULL;
+
+ if (state->crtc) {
+ a_crtc = to_spacemit_crtc(state->crtc);
+
+ if (spacemit_pstate->mmu_tbl.va)
+ dma_free_coherent(a_crtc->dev,
+ spacemit_pstate->mmu_tbl.size,
+ spacemit_pstate->mmu_tbl.va,
+ spacemit_pstate->mmu_tbl.pa);
+
+ if (spacemit_pstate->cl.va)
+ dma_free_coherent(a_crtc->dev,
+ spacemit_pstate->cl.size,
+ spacemit_pstate->cl.va,
+ spacemit_pstate->cl.pa);
+ trace_spacemit_plane_atomic_destroy_state(a_crtc->dev_id);
+ }
+ __drm_atomic_helper_plane_destroy_state(state);
+
+ kfree(to_spacemit_plane_state(state));
+}
+
+static void spacemit_plane_create_properties(struct spacemit_plane *p)
+{
+ unsigned int support_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI) |
+ BIT(DRM_MODE_BLEND_COVERAGE);
+
+ drm_plane_create_alpha_property(&p->plane);
+
+ drm_plane_create_blend_mode_property(&p->plane, support_modes);
+}
+
+static const struct drm_plane_helper_funcs spacemit_plane_helper_funcs = {
+ .atomic_check = spacemit_plane_atomic_check,
+ .atomic_update = spacemit_plane_atomic_update,
+ .atomic_disable = spacemit_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs spacemit_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ .reset = spacemit_plane_reset,
+ .atomic_duplicate_state = spacemit_plane_atomic_duplicate_state,
+ .atomic_destroy_state = spacemit_plane_atomic_destroy_state,
+};
+
+struct drm_plane *spacemit_plane_init(struct drm_device *drm,
+ struct spacemit_crtc *a_crtc)
+{
+ struct spacemit_plane *p = NULL;
+ int err, j;
+ u32 *formats;
+ struct spacemit_drm_private *priv = drm->dev_private;
+ struct spacemit_hw_device *hwdev = priv->hwdev;
+ u8 n_formats = hwdev->n_formats;
+ u8 n_fbcmems = hwdev->n_fbcmems;
+ u8 n_rdmas = hwdev->rdma_nums;
+ u32 plane_crtc_mask;
+
+ trace_spacemit_plane_init(a_crtc->dev_id);
+ if (n_fbcmems * 2 != n_rdmas) {
+ drm_err(drm, "mismatched rdma/fbcmem counts: %d vs %d\n",
+ n_rdmas, n_fbcmems);
+ return ERR_PTR(-EINVAL);
+ }
+
+ for (j = 0; j < n_fbcmems; j++) {
+ if (hwdev->fbcmem_sizes[j] < FBCMEM_UNIT) {
+ drm_err(drm, "invalid fbcmem_sizes[%d] = %u\n",
+ j, hwdev->fbcmem_sizes[j]);
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ formats = kcalloc(n_formats, sizeof(*formats), GFP_KERNEL);
+ if (!formats)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * The CRTC is created right after this, so its index is the current
+ * count.
+ */
+ plane_crtc_mask = 1 << drm->mode_config.num_crtc;
+
+ /* drmm, not devm: an open DRM fd outlives the platform unbind. */
+ p = drmm_kzalloc(drm, sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ kfree(formats);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ for (j = 0; j < n_formats; j++)
+ formats[j] = hwdev->formats[j].format;
+
+ err = drm_universal_plane_init(drm, &p->plane, plane_crtc_mask,
+ &spacemit_plane_funcs, formats,
+ n_formats, supported_format_modifiers,
+ DRM_PLANE_TYPE_PRIMARY, NULL);
+ kfree(formats);
+ if (err) {
+ drm_err(drm, "failed to init primary plane\n");
+ return ERR_PTR(err);
+ }
+
+ drm_plane_helper_add(&p->plane, &spacemit_plane_helper_funcs);
+
+ spacemit_plane_create_properties(p);
+
+ p->hwdev = hwdev;
+
+ return &p->plane;
+}
--
2.43.0
next prev parent reply other threads:[~2026-07-25 4:51 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-25 4:51 [PATCH RESEND 00/17] drm/spacemit: add SpacemiT K3 display support Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 01/17] dt-bindings: display: spacemit: add K3 Saturn DPU controller Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 02/17] dt-bindings: phy: add SpacemiT K3 Innosilicon DP PHY Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 03/17] dt-bindings: display: spacemit: add K3 Innosilicon DP/eDP controller Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 04/17] dt-bindings: soc: spacemit: allow eDP/DP PHY PLL pixel clocks on K3 APMU Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 05/17] phy: spacemit: add Innosilicon DP TX PHY driver Cody Kang via B4 Relay
2026-07-25 5:02 ` sashiko-bot
2026-07-25 4:51 ` [PATCH RESEND 06/17] clk: spacemit: k3: parent eDP/DP pixel clock to the PHY PLL Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 07/17] drm/spacemit: add Saturn DPU register model Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 08/17] drm/spacemit: add Saturn DPU core types, cmdlist and display MMU Cody Kang via B4 Relay
2026-07-25 5:04 ` sashiko-bot
2026-07-25 4:51 ` [PATCH RESEND 09/17] drm/spacemit: add Saturn DPU hardware backend Cody Kang via B4 Relay
2026-07-25 5:12 ` sashiko-bot
2026-07-25 4:51 ` Cody Kang via B4 Relay [this message]
2026-07-25 5:04 ` [PATCH RESEND 10/17] drm/spacemit: add Saturn DPU KMS pipeline sashiko-bot
2026-07-25 4:51 ` [PATCH RESEND 11/17] drm/spacemit: add Saturn DPU DRM device driver Cody Kang via B4 Relay
2026-07-25 5:05 ` sashiko-bot
2026-07-25 4:51 ` [PATCH RESEND 12/17] drm/spacemit: add Innosilicon DP/eDP controller bridge driver Cody Kang via B4 Relay
2026-07-25 5:04 ` sashiko-bot
2026-07-25 4:51 ` [PATCH RESEND 13/17] MAINTAINERS: add SpacemiT K3 display driver entry Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 14/17] riscv: dts: spacemit: k3: add display nodes Cody Kang via B4 Relay
2026-07-25 5:08 ` sashiko-bot
2026-07-25 4:51 ` [PATCH RESEND 15/17] riscv: dts: spacemit: k3-pico-itx: enable the DisplayPort output Cody Kang via B4 Relay
2026-07-25 5:12 ` sashiko-bot
2026-07-25 4:51 ` [PATCH RESEND 16/17] riscv: dts: spacemit: k3-com260-ifx: " Cody Kang via B4 Relay
2026-07-25 4:51 ` [PATCH RESEND 17/17] riscv: defconfig: spacemit: k3: enable display driver Cody Kang via B4 Relay
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260725-k3-display-v1-10-6de34d80e86c@gmail.com \
--to=devnull+codykang.hk.gmail.com@kernel.org \
--cc=airlied@gmail.com \
--cc=alex@ghiti.fr \
--cc=aou@eecs.berkeley.edu \
--cc=bmasney@redhat.com \
--cc=codykang.hk@gmail.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dlan@kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=heylenay@4d2.org \
--cc=krzk+dt@kernel.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-phy@lists.infradead.org \
--cc=linux-riscv@lists.infradead.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=mturquette@baylibre.com \
--cc=neil.armstrong@linaro.org \
--cc=p.zabel@pengutronix.de \
--cc=palmer@dabbelt.com \
--cc=pjw@kernel.org \
--cc=robh@kernel.org \
--cc=sboyd@kernel.org \
--cc=simona@ffwll.ch \
--cc=spacemit@lists.linux.dev \
--cc=tzimmermann@suse.de \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox