diff options
Diffstat (limited to 'drivers/gpu/drm/panfrost/panfrost_device.c')
| -rw-r--r-- | drivers/gpu/drm/panfrost/panfrost_device.c | 123 | 
1 files changed, 107 insertions, 16 deletions
| diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 238fb6d54df4..8136babd3ba9 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -5,6 +5,7 @@  #include <linux/clk.h>  #include <linux/reset.h>  #include <linux/platform_device.h> +#include <linux/pm_domain.h>  #include <linux/regulator/consumer.h>  #include "panfrost_device.h" @@ -87,18 +88,27 @@ static void panfrost_clk_fini(struct panfrost_device *pfdev)  static int panfrost_regulator_init(struct panfrost_device *pfdev)  { -	int ret; +	int ret, i; -	pfdev->regulator = devm_regulator_get(pfdev->dev, "mali"); -	if (IS_ERR(pfdev->regulator)) { -		ret = PTR_ERR(pfdev->regulator); -		dev_err(pfdev->dev, "failed to get regulator: %d\n", ret); +	if (WARN(pfdev->comp->num_supplies > ARRAY_SIZE(pfdev->regulators), +			"Too many supplies in compatible structure.\n")) +		return -EINVAL; + +	for (i = 0; i < pfdev->comp->num_supplies; i++) +		pfdev->regulators[i].supply = pfdev->comp->supply_names[i]; + +	ret = devm_regulator_bulk_get(pfdev->dev, +				      pfdev->comp->num_supplies, +				      pfdev->regulators); +	if (ret < 0) { +		dev_err(pfdev->dev, "failed to get regulators: %d\n", ret);  		return ret;  	} -	ret = regulator_enable(pfdev->regulator); +	ret = regulator_bulk_enable(pfdev->comp->num_supplies, +				    pfdev->regulators);  	if (ret < 0) { -		dev_err(pfdev->dev, "failed to enable regulator: %d\n", ret); +		dev_err(pfdev->dev, "failed to enable regulators: %d\n", ret);  		return ret;  	} @@ -107,7 +117,81 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)  static void panfrost_regulator_fini(struct panfrost_device *pfdev)  { -	regulator_disable(pfdev->regulator); +	regulator_bulk_disable(pfdev->comp->num_supplies, +			pfdev->regulators); +} + +static void panfrost_pm_domain_fini(struct panfrost_device *pfdev) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(pfdev->pm_domain_devs); i++) { +		if (!pfdev->pm_domain_devs[i]) +			break; + +		if (pfdev->pm_domain_links[i]) +			device_link_del(pfdev->pm_domain_links[i]); + +		dev_pm_domain_detach(pfdev->pm_domain_devs[i], true); +	} +} + +static int panfrost_pm_domain_init(struct panfrost_device *pfdev) +{ +	int err; +	int i, num_domains; + +	num_domains = of_count_phandle_with_args(pfdev->dev->of_node, +						 "power-domains", +						 "#power-domain-cells"); + +	/* +	 * Single domain is handled by the core, and, if only a single power +	 * the power domain is requested, the property is optional. +	 */ +	if (num_domains < 2 && pfdev->comp->num_pm_domains < 2) +		return 0; + +	if (num_domains != pfdev->comp->num_pm_domains) { +		dev_err(pfdev->dev, +			"Incorrect number of power domains: %d provided, %d needed\n", +			num_domains, pfdev->comp->num_pm_domains); +		return -EINVAL; +	} + +	if (WARN(num_domains > ARRAY_SIZE(pfdev->pm_domain_devs), +			"Too many supplies in compatible structure.\n")) +		return -EINVAL; + +	for (i = 0; i < num_domains; i++) { +		pfdev->pm_domain_devs[i] = +			dev_pm_domain_attach_by_name(pfdev->dev, +					pfdev->comp->pm_domain_names[i]); +		if (IS_ERR_OR_NULL(pfdev->pm_domain_devs[i])) { +			err = PTR_ERR(pfdev->pm_domain_devs[i]) ? : -ENODATA; +			pfdev->pm_domain_devs[i] = NULL; +			dev_err(pfdev->dev, +				"failed to get pm-domain %s(%d): %d\n", +				pfdev->comp->pm_domain_names[i], i, err); +			goto err; +		} + +		pfdev->pm_domain_links[i] = device_link_add(pfdev->dev, +				pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME | +				DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE); +		if (!pfdev->pm_domain_links[i]) { +			dev_err(pfdev->pm_domain_devs[i], +				"adding device link failed!\n"); +			err = -ENODEV; +			goto err; +		} +	} + +	return 0; + +err: +	panfrost_pm_domain_fini(pfdev); +	return err;  }  int panfrost_device_init(struct panfrost_device *pfdev) @@ -140,37 +224,43 @@ int panfrost_device_init(struct panfrost_device *pfdev)  		goto err_out1;  	} +	err = panfrost_pm_domain_init(pfdev); +	if (err) +		goto err_out2; +  	res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0);  	pfdev->iomem = devm_ioremap_resource(pfdev->dev, res);  	if (IS_ERR(pfdev->iomem)) {  		dev_err(pfdev->dev, "failed to ioremap iomem\n");  		err = PTR_ERR(pfdev->iomem); -		goto err_out2; +		goto err_out3;  	}  	err = panfrost_gpu_init(pfdev);  	if (err) -		goto err_out2; +		goto err_out3;  	err = panfrost_mmu_init(pfdev);  	if (err) -		goto err_out3; +		goto err_out4;  	err = panfrost_job_init(pfdev);  	if (err) -		goto err_out4; +		goto err_out5;  	err = panfrost_perfcnt_init(pfdev);  	if (err) -		goto err_out5; +		goto err_out6;  	return 0; -err_out5: +err_out6:  	panfrost_job_fini(pfdev); -err_out4: +err_out5:  	panfrost_mmu_fini(pfdev); -err_out3: +err_out4:  	panfrost_gpu_fini(pfdev); +err_out3: +	panfrost_pm_domain_fini(pfdev);  err_out2:  	panfrost_reset_fini(pfdev);  err_out1: @@ -186,6 +276,7 @@ void panfrost_device_fini(struct panfrost_device *pfdev)  	panfrost_job_fini(pfdev);  	panfrost_mmu_fini(pfdev);  	panfrost_gpu_fini(pfdev); +	panfrost_pm_domain_fini(pfdev);  	panfrost_reset_fini(pfdev);  	panfrost_regulator_fini(pfdev);  	panfrost_clk_fini(pfdev); |