drm/amd/amdgpu: Add ISP Generic PM Domain (genpd) support

AMDISP I2C device requires to power on ISP HW to probe the sensor
device. Instead of using the exported symbols from ISP driver to
control the power and clocks remotely,added Generic PM Domain (genpd)
support in amdgpu_isp device for its child devices (amd_isp_capture,
amd_isp_i2c_designware) to set power and clocks using PM methods.

Co-developed-by: Bin Du <bin.du@amd.com>
Signed-off-by: Bin Du <bin.du@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Pratap Nirujogi <pratap.nirujogi@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Pratap Nirujogi 2025-05-29 15:21:48 -04:00 committed by Alex Deucher
parent 9bed716f87
commit fd14786071
2 changed files with 157 additions and 1 deletions

View File

@ -28,6 +28,8 @@
#ifndef __AMDGPU_ISP_H__
#define __AMDGPU_ISP_H__
#include <linux/pm_domain.h>
#define ISP_REGS_OFFSET_END 0x629A4
struct amdgpu_isp;
@ -54,6 +56,7 @@ struct amdgpu_isp {
struct isp_platform_data *isp_pdata;
unsigned int harvest_config;
const struct firmware *fw;
struct generic_pm_domain ispgpd;
};
extern const struct amdgpu_ip_block_version isp_v4_1_0_ip_block;

View File

@ -29,6 +29,12 @@
#include "amdgpu.h"
#include "isp_v4_1_1.h"
#define ISP_PERFORMANCE_STATE_LOW 0
#define ISP_PERFORMANCE_STATE_HIGH 1
#define ISP_HIGH_PERFORMANC_XCLK 788
#define ISP_HIGH_PERFORMANC_ICLK 788
static const unsigned int isp_4_1_1_int_srcid[MAX_ISP411_INT_SRC] = {
ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9,
ISP_4_1__SRCID__ISP_RINGBUFFER_WPT10,
@ -56,6 +62,125 @@ static struct gpiod_lookup_table isp_sensor_gpio_table = {
},
};
static int isp_poweroff(struct generic_pm_domain *genpd)
{
struct amdgpu_isp *isp = container_of(genpd, struct amdgpu_isp, ispgpd);
struct amdgpu_device *adev = isp->adev;
return amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ISP, true, 0);
}
static int isp_poweron(struct generic_pm_domain *genpd)
{
struct amdgpu_isp *isp = container_of(genpd, struct amdgpu_isp, ispgpd);
struct amdgpu_device *adev = isp->adev;
return amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ISP, false, 0);
}
static int isp_set_performance_state(struct generic_pm_domain *genpd,
unsigned int state)
{
struct amdgpu_isp *isp = container_of(genpd, struct amdgpu_isp, ispgpd);
struct amdgpu_device *adev = isp->adev;
u32 iclk, xclk;
int ret;
switch (state) {
case ISP_PERFORMANCE_STATE_HIGH:
xclk = ISP_HIGH_PERFORMANC_XCLK;
iclk = ISP_HIGH_PERFORMANC_ICLK;
break;
case ISP_PERFORMANCE_STATE_LOW:
/* isp runs at default lowest clock-rate on power-on, do nothing */
return 0;
default:
return -EINVAL;
}
ret = amdgpu_dpm_set_soft_freq_range(adev, PP_ISPXCLK, xclk, 0);
if (ret) {
drm_err(&adev->ddev, "failed to set xclk %u to %u: %d\n",
xclk, state, ret);
return ret;
}
ret = amdgpu_dpm_set_soft_freq_range(adev, PP_ISPICLK, iclk, 0);
if (ret) {
drm_err(&adev->ddev, "failed to set iclk %u to %u: %d\n",
iclk, state, ret);
return ret;
}
return 0;
}
static int isp_genpd_add_device(struct device *dev, void *data)
{
struct generic_pm_domain *gpd = data;
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
struct amdgpu_isp *isp = container_of(gpd, struct amdgpu_isp, ispgpd);
struct amdgpu_device *adev = isp->adev;
int ret;
if (!pdev)
return -EINVAL;
if (!dev->type->name) {
drm_dbg(&adev->ddev, "Invalid device type to add\n");
goto exit;
}
if (strcmp(dev->type->name, "mfd_device")) {
drm_dbg(&adev->ddev, "Invalid isp mfd device %s to add\n", pdev->mfd_cell->name);
goto exit;
}
ret = pm_genpd_add_device(gpd, dev);
if (ret) {
drm_err(&adev->ddev, "Failed to add dev %s to genpd %d\n",
pdev->mfd_cell->name, ret);
return -ENODEV;
}
exit:
/* Continue to add */
return 0;
}
static int isp_genpd_remove_device(struct device *dev, void *data)
{
struct generic_pm_domain *gpd = data;
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
struct amdgpu_isp *isp = container_of(gpd, struct amdgpu_isp, ispgpd);
struct amdgpu_device *adev = isp->adev;
int ret;
if (!pdev)
return -EINVAL;
if (!dev->type->name) {
drm_dbg(&adev->ddev, "Invalid device type to remove\n");
goto exit;
}
if (strcmp(dev->type->name, "mfd_device")) {
drm_dbg(&adev->ddev, "Invalid isp mfd device %s to remove\n",
pdev->mfd_cell->name);
goto exit;
}
ret = pm_genpd_remove_device(dev);
if (ret) {
drm_err(&adev->ddev, "Failed to remove dev from genpd %d\n", ret);
return -ENODEV;
}
exit:
/* Continue to remove */
return 0;
}
static int isp_v4_1_1_hw_init(struct amdgpu_isp *isp)
{
struct amdgpu_device *adev = isp->adev;
@ -81,6 +206,17 @@ static int isp_v4_1_1_hw_init(struct amdgpu_isp *isp)
isp_base = adev->rmmio_base;
isp->ispgpd.name = "ISP_v_4_1_1";
isp->ispgpd.power_off = isp_poweroff;
isp->ispgpd.power_on = isp_poweron;
isp->ispgpd.set_performance_state = isp_set_performance_state;
r = pm_genpd_init(&isp->ispgpd, NULL, true);
if (r) {
drm_err(&adev->ddev, "failed to initialize genpd (%d)\n", r);
return -EINVAL;
}
isp->isp_cell = kcalloc(3, sizeof(struct mfd_cell), GFP_KERNEL);
if (!isp->isp_cell) {
r = -ENOMEM;
@ -179,13 +315,27 @@ static int isp_v4_1_1_hw_init(struct amdgpu_isp *isp)
isp->isp_cell[2].platform_data = isp->isp_pdata;
isp->isp_cell[2].pdata_size = sizeof(struct isp_platform_data);
r = mfd_add_hotplug_devices(isp->parent, isp->isp_cell, 3);
/* add only amd_isp_capture and amd_isp_i2c_designware to genpd */
r = mfd_add_hotplug_devices(isp->parent, isp->isp_cell, 2);
if (r) {
drm_err(&adev->ddev,
"%s: add mfd hotplug device failed\n", __func__);
goto failure;
}
r = device_for_each_child(isp->parent, &isp->ispgpd,
isp_genpd_add_device);
if (r) {
drm_err(&adev->ddev, "failed to add devices to genpd (%d)\n", r);
goto failure;
}
r = mfd_add_hotplug_devices(isp->parent, &isp->isp_cell[2], 1);
if (r) {
drm_err(&adev->ddev, "add pinctl hotplug device failed (%d)\n", r);
goto failure;
}
return 0;
failure:
@ -201,6 +351,9 @@ static int isp_v4_1_1_hw_init(struct amdgpu_isp *isp)
static int isp_v4_1_1_hw_fini(struct amdgpu_isp *isp)
{
device_for_each_child(isp->parent, NULL,
isp_genpd_remove_device);
mfd_remove_devices(isp->parent);
kfree(isp->isp_res);