From patchwork Wed Jun 18 10:22:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Wilczynski X-Patchwork-Id: 898996 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 AD41A29CB57 for ; Wed, 18 Jun 2025 10:22:32 +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=1750242155; cv=none; b=Rkq7DEHefYQ/fiUZ6QNr6SsctSR18eAR/DlwLwQx6ANdMxo2Z16DUC1Y4dgjflMOJWFV+c1Hb56GEqn2wZFS3c3IRNM6wFO89DAz/fFFpusylGJxVPRdk6IfDzixlnatgMVgXwMiJoCfUsVXMumyyRwcnvzFiPFRAtTH1rdolOc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750242155; c=relaxed/simple; bh=nY8QLvFp3Ij0BV7LuPvaD1wVpZhZ3ZlZqNG90N8VrxM=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=YUglT3SXUsVE81u8Mjpb5szySYBndLwI5c1+Z4UvXTtG7JEDXGL7AOdV9KmOA+wHzbTDDXrHMXDEEC21V4Sx4Oz9kRnGAs/U5lSlZfhrwaDKZo7GqjNfagUAzrNTMikba9n6HciRo34zFKaX4VTFB2+kLx2guaP8aQkpiIQ9tFc= 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=QImpgKRO; 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="QImpgKRO" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20250618102230euoutp028feaa82cf0e7d60c0e632916d03947e8~KG-Zt5mjo2862228622euoutp02B for ; Wed, 18 Jun 2025 10:22:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20250618102230euoutp028feaa82cf0e7d60c0e632916d03947e8~KG-Zt5mjo2862228622euoutp02B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750242150; bh=0gVgt9U0jeu9CI/25XjajgSAqO26NEtCITyXqrTUvrM=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=QImpgKRO0BJZIqB5RIaAKucUUvRHi8b7wdFQSjArVMAFS8A2YlUh10z17s7U8N5Fn 6HYCRAKudi9O2kiU+ZwHInJT9oXwPkCF+2qAYOg/V7CZGFP6QS3Wtk0QjrxiuYE6t3 ZrtrvSURnYfBB0CTfSIYuXBFyjIrvPd4CHQKaI+0= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250618102229eucas1p2a5d38013ee52a8a4a10d43449073e79e~KG-Y_rFUj2048420484eucas1p23; Wed, 18 Jun 2025 10:22:29 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250618102228eusmtip1c212780be6184f2330e068ae4287d310~KG-YAb6D11210412104eusmtip1q; Wed, 18 Jun 2025 10:22:28 +0000 (GMT) From: Michal Wilczynski Date: Wed, 18 Jun 2025 12:22:10 +0200 Subject: [PATCH v5 4/8] drm/imagination: Use pwrseq for TH1520 GPU power management Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250618-apr_14_for_sending-v5-4-27ed33ea5c6f@samsung.com> In-Reply-To: <20250618-apr_14_for_sending-v5-0-27ed33ea5c6f@samsung.com> To: Drew Fustini , 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 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 X-Mailer: b4 0.15-dev X-CMS-MailID: 20250618102229eucas1p2a5d38013ee52a8a4a10d43449073e79e X-Msg-Generator: CA X-RootMTR: 20250618102229eucas1p2a5d38013ee52a8a4a10d43449073e79e X-EPHeader: CA X-CMS-RootMailID: 20250618102229eucas1p2a5d38013ee52a8a4a10d43449073e79e References: <20250618-apr_14_for_sending-v5-0-27ed33ea5c6f@samsung.com> Update the Imagination PVR DRM driver to leverage the pwrseq framework for managing the power sequence of the GPU on the T-HEAD TH1520 SoC. To cleanly handle the TH1520's specific power requirements in the generic driver, this patch implements the "driver match data" pattern. The pvr_soc_data struct, associated with a compatible string in the of_device_id table, now holds pointers to platform-specific power_on and power_off functions. At probe time, the driver inspects the assigned power_on function pointer. If it points to the pwrseq variant, the driver calls devm_pwrseq_get("gpu-power"), requiring a valid sequencer and deferring probe on failure. Otherwise, it falls back to its standard manual reset initialization. The runtime PM callbacks, pvr_power_device_resume() and pvr_power_device_suspend(), call the power_on and power_off function pointers. Helper functions for both manual and pwrseq-based sequences are introduced to support this. Reviewed-by: Ulf Hansson Signed-off-by: Michal Wilczynski --- drivers/gpu/drm/imagination/Kconfig | 1 + drivers/gpu/drm/imagination/pvr_device.c | 31 +++++++-- drivers/gpu/drm/imagination/pvr_device.h | 19 ++++++ drivers/gpu/drm/imagination/pvr_drv.c | 30 ++++++++- drivers/gpu/drm/imagination/pvr_power.c | 112 ++++++++++++++++++++----------- drivers/gpu/drm/imagination/pvr_power.h | 6 ++ 6 files changed, 152 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/imagination/Kconfig b/drivers/gpu/drm/imagination/Kconfig index 3bfa2ac212dccb73c53bdc2bc259bcba636e7cfc..5f9fff43d6baadc42ebf48d91729bfbf27e06caa 100644 --- a/drivers/gpu/drm/imagination/Kconfig +++ b/drivers/gpu/drm/imagination/Kconfig @@ -11,6 +11,7 @@ config DRM_POWERVR select DRM_SCHED select DRM_GPUVM select FW_LOADER + select POWER_SEQUENCING help Choose this option if you have a system that has an Imagination Technologies PowerVR (Series 6 or later) or IMG GPU. diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c index 8b9ba4983c4cb5bc40342fcafc4259078bc70547..c1c24c441c821ccce59f7cd3f14544a91ef54ea9 100644 --- a/drivers/gpu/drm/imagination/pvr_device.c +++ b/drivers/gpu/drm/imagination/pvr_device.c @@ -23,8 +23,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -618,6 +620,9 @@ pvr_device_init(struct pvr_device *pvr_dev) struct device *dev = drm_dev->dev; int err; + /* Get the platform-specific data based on the compatible string. */ + pvr_dev->soc_data = of_device_get_match_data(dev); + /* * Setup device parameters. We do this first in case other steps * depend on them. @@ -631,10 +636,28 @@ pvr_device_init(struct pvr_device *pvr_dev) if (err) return err; - /* Get the reset line for the GPU */ - err = 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 (pvr_dev->soc_data->power_on == pvr_power_on_sequence_pwrseq) { + pvr_dev->pwrseq = 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 { + /* This platform does not use a sequencer, init reset manually. */ + err = pvr_device_reset_init(pvr_dev); + if (err) + return err; + } /* Explicitly power the GPU so we can access control registers before the FW is booted. */ err = pm_runtime_resume_and_get(dev); diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/imagination/pvr_device.h index 7cb01c38d2a9c3fc71effe789d4dfe54eddd93ee..3f35025e84efac031d3261c849ef9fe105466423 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; +/* Forward declaration from #include #include +#include #include #include #include @@ -234,6 +235,71 @@ pvr_watchdog_init(struct pvr_device *pvr_dev) return 0; } +int pvr_power_on_sequence_pwrseq(struct pvr_device *pvr_dev) +{ + return pwrseq_power_on(pvr_dev->pwrseq); +} + +int pvr_power_off_sequence_pwrseq(struct pvr_device *pvr_dev) +{ + return pwrseq_power_off(pvr_dev->pwrseq); +} + +int pvr_power_on_sequence_manual(struct pvr_device *pvr_dev) +{ + int err; + + err = clk_prepare_enable(pvr_dev->core_clk); + if (err) + return err; + + err = clk_prepare_enable(pvr_dev->sys_clk); + if (err) + goto err_core_clk_disable; + + err = 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 = 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; +} + +int pvr_power_off_sequence_manual(struct pvr_device *pvr_dev) +{ + int err; + + err = 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; +} + int pvr_power_device_suspend(struct device *dev) { @@ -252,11 +318,7 @@ pvr_power_device_suspend(struct device *dev) goto err_drm_dev_exit; } - clk_disable_unprepare(pvr_dev->mem_clk); - clk_disable_unprepare(pvr_dev->sys_clk); - clk_disable_unprepare(pvr_dev->core_clk); - - err = reset_control_assert(pvr_dev->reset); + err = pvr_dev->soc_data->power_off(pvr_dev); err_drm_dev_exit: drm_dev_exit(idx); @@ -276,54 +338,22 @@ pvr_power_device_resume(struct device *dev) if (!drm_dev_enter(drm_dev, &idx)) return -EIO; - err = clk_prepare_enable(pvr_dev->core_clk); + err = pvr_dev->soc_data->power_on(pvr_dev); if (err) goto err_drm_dev_exit; - err = clk_prepare_enable(pvr_dev->sys_clk); - if (err) - goto err_core_clk_disable; - - err = 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 = reset_control_deassert(pvr_dev->reset); - if (err) - goto err_mem_clk_disable; - if (pvr_dev->fw_dev.booted) { err = pvr_power_fw_enable(pvr_dev); if (err) - goto err_reset_assert; + goto err_power_off; } drm_dev_exit(idx); return 0; -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->soc_data->power_off(pvr_dev); err_drm_dev_exit: drm_dev_exit(idx); diff --git a/drivers/gpu/drm/imagination/pvr_power.h b/drivers/gpu/drm/imagination/pvr_power.h index ada85674a7ca762dcf92df40424230e1c3910342..d91d5f3f39b61f81121357f4187b1a6e09b3dec0 100644 --- a/drivers/gpu/drm/imagination/pvr_power.h +++ b/drivers/gpu/drm/imagination/pvr_power.h @@ -41,4 +41,10 @@ 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); +/* Power sequence functions */ +int pvr_power_on_sequence_manual(struct pvr_device *pvr_dev); +int pvr_power_off_sequence_manual(struct pvr_device *pvr_dev); +int pvr_power_on_sequence_pwrseq(struct pvr_device *pvr_dev); +int pvr_power_off_sequence_pwrseq(struct pvr_device *pvr_dev); + #endif /* PVR_POWER_H */