From nobody Wed Feb 11 09:03:51 2026 Received: from mailout2.w1.samsung.com (mailout2.w1.samsung.com [210.118.77.12]) (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 45762218AB4 for ; Thu, 26 Jun 2025 09:33:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.118.77.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750930441; cv=none; b=jfmSMGhTz2J/ek0Ady8D2BGenQfzLmTXBvNumzi2YDnGh2hNrlSvpdl7e0/UDBFNEYQNusU3W2NZL292HTiNyCmsx1SvFiEhHwoiKVVatg5kjDOWalGercLB2m7E3atZN6FlIE8DAzIFygG8dx3d3kfiKCf81a7xRpb6m7AHATg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750930441; c=relaxed/simple; bh=A5mttJi5Qt0qHGRLAnhcJTIqW4e1Ej+mpsba4qdzRAU=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=WHQHWJ6nsTjWof9J8dAl7q+igmlMMpSx6xgM8Ujqb4TasUwm/8LadB+ByFRJ5tNkB5Dhey05GZGCwdinkH9hFgi4liS6vIrDOM0sVGG6uPCMvDME6/R5fSKrxF+VTWxEXtc47IizDCfQyjjPGBGtnZKQCcc2nY/IPshdBNa1WRU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=qsbS8pte; arc=none smtp.client-ip=210.118.77.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="qsbS8pte" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20250626093357euoutp02f2310896f5f31a3bc90628c585de0706~MjfTYM-1C0436504365euoutp02T for ; Thu, 26 Jun 2025 09:33:57 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20250626093357euoutp02f2310896f5f31a3bc90628c585de0706~MjfTYM-1C0436504365euoutp02T DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750930437; bh=A5QkgeJZq0KvaVBeYrJkeNR3RGfx7JGchT01g1k7HCQ=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=qsbS8pteaiYCZ1C+I0lbc+Z3j8smm7xVzcrjiKw5EXsdYogQoUOMVeJ+nuOB4CZhA WOVuUzgCbc3muuYledkAvRl7cSoSMSoI16MfI226+/72IeYRI9mjw+QZtPuQ94vpbA GoaGAIatDEov+Zmy62aqv8Da8OLpdyTBVo00SA4M= Received: from eusmtip2.samsung.com (unknown [203.254.199.222]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250626093356eucas1p1adfcd565173d939f82e15252189c316f~MjfSe7dhM1721217212eucas1p12; Thu, 26 Jun 2025 09:33:56 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip2.samsung.com (KnoxPortal) with ESMTPA id 20250626093355eusmtip2f074f5fcac29418428b9194a6709c8e8~MjfRYd2xB1045310453eusmtip2d; Thu, 26 Jun 2025 09:33:55 +0000 (GMT) From: Michal Wilczynski Date: Thu, 26 Jun 2025 11:33:46 +0200 Subject: [PATCH v7 1/5] drm/imagination: Use pwrseq for TH1520 GPU power management Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250626-apr_14_for_sending-v7-1-6593722e0217@samsung.com> In-Reply-To: <20250626-apr_14_for_sending-v7-0-6593722e0217@samsung.com> To: Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Michal Wilczynski , Bartosz Golaszewski , Philipp Zabel , Frank Binns , Matt Coster , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Ulf Hansson , Marek Szyprowski , Drew Fustini Cc: linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, dri-devel@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.15-dev X-CMS-MailID: 20250626093356eucas1p1adfcd565173d939f82e15252189c316f X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250626093356eucas1p1adfcd565173d939f82e15252189c316f X-EPHeader: CA X-CMS-RootMailID: 20250626093356eucas1p1adfcd565173d939f82e15252189c316f References: <20250626-apr_14_for_sending-v7-0-6593722e0217@samsung.com> Update the Imagination PVR DRM driver to leverage the pwrseq framework for managing the complex power sequence of the GPU on the T-HEAD TH1520 SoC. To cleanly separate platform specific logic from the generic driver, this patch introduces a `pwr_power_sequence_ops` struct containing function pointers for power_on and power_off operations. This allows for different power management strategies to be selected at probe time based on the device's compatible string. A `pvr_device_data` struct, associated with each compatible in the of_device_id table, points to the appropriate ops table (manual or pwrseq). At probe time, the driver inspects the assigned ops struct. If the pwrseq variant is detected, the driver calls devm_pwrseq_get("gpu-power"), deferring probe if the sequencer is not yet available. Otherwise, it falls back to the existing manual clock and reset handling. The runtime PM callbacks now call the appropriate functions via the ops table. Reviewed-by: Ulf Hansson Reviewed-by: Bartosz Golaszewski Signed-off-by: Michal Wilczynski --- drivers/gpu/drm/imagination/pvr_device.c | 36 +++++++- drivers/gpu/drm/imagination/pvr_device.h | 17 ++++ drivers/gpu/drm/imagination/pvr_drv.c | 27 +++++- drivers/gpu/drm/imagination/pvr_power.c | 139 ++++++++++++++++++++++-----= ---- drivers/gpu/drm/imagination/pvr_power.h | 13 +++ 5 files changed, 185 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/ima= gination/pvr_device.c index 8b9ba4983c4cb5bc40342fcafc4259078bc70547..770fc32a6fe485aad41cd92fa14= 31dd233ac20dc 100644 --- a/drivers/gpu/drm/imagination/pvr_device.c +++ b/drivers/gpu/drm/imagination/pvr_device.c @@ -23,8 +23,12 @@ #include #include #include +#include #include #include +#if IS_ENABLED(CONFIG_POWER_SEQUENCING) +#include +#endif #include #include #include @@ -618,6 +622,9 @@ pvr_device_init(struct pvr_device *pvr_dev) struct device *dev =3D drm_dev->dev; int err; =20 + /* Get the platform-specific data based on the compatible string. */ + pvr_dev->device_data =3D of_device_get_match_data(dev); + /* * Setup device parameters. We do this first in case other steps * depend on them. @@ -631,10 +638,31 @@ pvr_device_init(struct pvr_device *pvr_dev) if (err) return err; =20 - /* Get the reset line for the GPU */ - err =3D pvr_device_reset_init(pvr_dev); - if (err) - return err; + /* + * For platforms that require it, get the power sequencer. + * For all others, perform manual reset initialization. + */ +#if IS_ENABLED(CONFIG_POWER_SEQUENCING) + if (pvr_dev->device_data->pwr_ops =3D=3D &pvr_power_sequence_ops_pwrseq) { + pvr_dev->pwrseq =3D devm_pwrseq_get(dev, "gpu-power"); + if (IS_ERR(pvr_dev->pwrseq)) { + /* + * This platform requires a sequencer. If we can't get + * it, we must return the error (including -EPROBE_DEFER + * to wait for the provider to appear) + */ + return dev_err_probe( + dev, PTR_ERR(pvr_dev->pwrseq), + "Failed to get required power sequencer\n"); + } + } else +#endif + { + /* This platform does not use a sequencer, init reset manually. */ + err =3D pvr_device_reset_init(pvr_dev); + if (err) + return err; + } =20 /* Explicitly power the GPU so we can access control registers before the= FW is booted. */ err =3D pm_runtime_resume_and_get(dev); diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/ima= gination/pvr_device.h index 7cb01c38d2a9c3fc71effe789d4dfe54eddd93ee..0d7f7c78573a0766a467fb0c3a5= 77ffe152d0892 100644 --- a/drivers/gpu/drm/imagination/pvr_device.h +++ b/drivers/gpu/drm/imagination/pvr_device.h @@ -37,6 +37,9 @@ struct clk; /* Forward declaration from . */ struct firmware; =20 +/* Forward declaration from #include #include +#if IS_ENABLED(CONFIG_POWER_SEQUENCING) +#include +#endif #include #include #include @@ -234,6 +237,96 @@ pvr_watchdog_init(struct pvr_device *pvr_dev) return 0; } =20 +static int pvr_power_on_sequence_manual(struct pvr_device *pvr_dev) +{ + int err; + + err =3D clk_prepare_enable(pvr_dev->core_clk); + if (err) + return err; + + err =3D clk_prepare_enable(pvr_dev->sys_clk); + if (err) + goto err_core_clk_disable; + + err =3D clk_prepare_enable(pvr_dev->mem_clk); + if (err) + goto err_sys_clk_disable; + + /* + * According to the hardware manual, a delay of at least 32 clock + * cycles is required between de-asserting the clkgen reset and + * de-asserting the GPU reset. Assuming a worst-case scenario with + * a very high GPU clock frequency, a delay of 1 microsecond is + * sufficient to ensure this requirement is met across all + * feasible GPU clock speeds. + */ + udelay(1); + + err =3D reset_control_deassert(pvr_dev->reset); + if (err) + goto err_mem_clk_disable; + + return 0; + +err_mem_clk_disable: + clk_disable_unprepare(pvr_dev->mem_clk); + +err_sys_clk_disable: + clk_disable_unprepare(pvr_dev->sys_clk); + +err_core_clk_disable: + clk_disable_unprepare(pvr_dev->core_clk); + + return err; +} + +static int pvr_power_off_sequence_manual(struct pvr_device *pvr_dev) +{ + int err; + + err =3D reset_control_assert(pvr_dev->reset); + + clk_disable_unprepare(pvr_dev->mem_clk); + clk_disable_unprepare(pvr_dev->sys_clk); + clk_disable_unprepare(pvr_dev->core_clk); + + return err; +} + +const struct pvr_power_sequence_ops pvr_power_sequence_ops_manual =3D { + .power_on =3D pvr_power_on_sequence_manual, + .power_off =3D pvr_power_off_sequence_manual, +}; + +#if IS_ENABLED(CONFIG_POWER_SEQUENCING) +static int pvr_power_on_sequence_pwrseq(struct pvr_device *pvr_dev) +{ + return pwrseq_power_on(pvr_dev->pwrseq); +} + +static int pvr_power_off_sequence_pwrseq(struct pvr_device *pvr_dev) +{ + return pwrseq_power_off(pvr_dev->pwrseq); +} + +const struct pvr_power_sequence_ops pvr_power_sequence_ops_pwrseq =3D { + .power_on =3D pvr_power_on_sequence_pwrseq, + .power_off =3D pvr_power_off_sequence_pwrseq, +}; +#else /* IS_ENABLED(CONFIG_POWER_SEQUENCING) */ +static int pvr_power_sequence_pwrseq_stub(struct pvr_device *pvr_dev) +{ + WARN_ONCE(1, "pwrseq support not enabled in kernel config\n"); + return -EOPNOTSUPP; +} + +const struct pvr_power_sequence_ops pvr_power_sequence_ops_pwrseq =3D { + .power_on =3D pvr_power_sequence_pwrseq_stub, + .power_off =3D pvr_power_sequence_pwrseq_stub, +}; +#endif /* IS_ENABLED(CONFIG_POWER_SEQUENCING) */ + int pvr_power_device_suspend(struct device *dev) { @@ -252,11 +345,7 @@ pvr_power_device_suspend(struct device *dev) goto err_drm_dev_exit; } =20 - clk_disable_unprepare(pvr_dev->mem_clk); - clk_disable_unprepare(pvr_dev->sys_clk); - clk_disable_unprepare(pvr_dev->core_clk); - - err =3D reset_control_assert(pvr_dev->reset); + err =3D pvr_dev->device_data->pwr_ops->power_off(pvr_dev); =20 err_drm_dev_exit: drm_dev_exit(idx); @@ -276,54 +365,22 @@ pvr_power_device_resume(struct device *dev) if (!drm_dev_enter(drm_dev, &idx)) return -EIO; =20 - err =3D clk_prepare_enable(pvr_dev->core_clk); + err =3D pvr_dev->device_data->pwr_ops->power_on(pvr_dev); if (err) goto err_drm_dev_exit; =20 - err =3D clk_prepare_enable(pvr_dev->sys_clk); - if (err) - goto err_core_clk_disable; - - err =3D clk_prepare_enable(pvr_dev->mem_clk); - if (err) - goto err_sys_clk_disable; - - /* - * According to the hardware manual, a delay of at least 32 clock - * cycles is required between de-asserting the clkgen reset and - * de-asserting the GPU reset. Assuming a worst-case scenario with - * a very high GPU clock frequency, a delay of 1 microsecond is - * sufficient to ensure this requirement is met across all - * feasible GPU clock speeds. - */ - udelay(1); - - err =3D reset_control_deassert(pvr_dev->reset); - if (err) - goto err_mem_clk_disable; - if (pvr_dev->fw_dev.booted) { err =3D pvr_power_fw_enable(pvr_dev); if (err) - goto err_reset_assert; + goto err_power_off; } =20 drm_dev_exit(idx); =20 return 0; =20 -err_reset_assert: - reset_control_assert(pvr_dev->reset); - -err_mem_clk_disable: - clk_disable_unprepare(pvr_dev->mem_clk); - -err_sys_clk_disable: - clk_disable_unprepare(pvr_dev->sys_clk); - -err_core_clk_disable: - clk_disable_unprepare(pvr_dev->core_clk); - +err_power_off: + pvr_dev->device_data->pwr_ops->power_off(pvr_dev); err_drm_dev_exit: drm_dev_exit(idx); =20 diff --git a/drivers/gpu/drm/imagination/pvr_power.h b/drivers/gpu/drm/imag= ination/pvr_power.h index ada85674a7ca762dcf92df40424230e1c3910342..6a2f3f6213e5ac2254344ad24d9= 678334c8974ea 100644 --- a/drivers/gpu/drm/imagination/pvr_power.h +++ b/drivers/gpu/drm/imagination/pvr_power.h @@ -41,4 +41,17 @@ pvr_power_put(struct pvr_device *pvr_dev) int pvr_power_domains_init(struct pvr_device *pvr_dev); void pvr_power_domains_fini(struct pvr_device *pvr_dev); =20 +/** + * struct pvr_power_sequence_ops - Platform specific power sequence operat= ions. + * @power_on: Pointer to the platform-specific power on function. + * @power_off: Pointer to the platform-specific power off function. + */ +struct pvr_power_sequence_ops { + int (*power_on)(struct pvr_device *pvr_dev); + int (*power_off)(struct pvr_device *pvr_dev); +}; + +extern const struct pvr_power_sequence_ops pvr_power_sequence_ops_manual; +extern const struct pvr_power_sequence_ops pvr_power_sequence_ops_pwrseq; + #endif /* PVR_POWER_H */ --=20 2.34.1