Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02afa816 authored by Todor Tomov's avatar Todor Tomov Committed by Mauro Carvalho Chehab
Browse files

media: camss: Add basic runtime PM support



There is a PM domain for each of the VFE hardware modules. Add
support for basic runtime PM support to be able to control the
PM domains. When a PM domain needs to be powered on - a device
link is created. When a PM domain needs to be powered off -
its device link is removed. This allows separate and
independent control of the PM domains.

Suspend/Resume is still not supported.

Signed-off-by: default avatarTodor Tomov <todor.tomov@linaro.org>
Signed-off-by: default avatarHans Verkuil <hansverk@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+samsung@kernel.org>
parent 9c3e59de
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
@@ -316,19 +317,27 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
	if (on) {
		u32 hw_version;

		ret = regulator_enable(csid->vdda);
		ret = pm_runtime_get_sync(dev);
		if (ret < 0)
			return ret;

		ret = regulator_enable(csid->vdda);
		if (ret < 0) {
			pm_runtime_put_sync(dev);
			return ret;
		}

		ret = csid_set_clock_rates(csid);
		if (ret < 0) {
			regulator_disable(csid->vdda);
			pm_runtime_put_sync(dev);
			return ret;
		}

		ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
		if (ret < 0) {
			regulator_disable(csid->vdda);
			pm_runtime_put_sync(dev);
			return ret;
		}

@@ -339,6 +348,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
			disable_irq(csid->irq);
			camss_disable_clocks(csid->nclocks, csid->clock);
			regulator_disable(csid->vdda);
			pm_runtime_put_sync(dev);
			return ret;
		}

@@ -348,6 +358,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
		disable_irq(csid->irq);
		camss_disable_clocks(csid->nclocks, csid->clock);
		ret = regulator_disable(csid->vdda);
		pm_runtime_put_sync(dev);
	}

	return ret;
+13 −2
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
@@ -240,13 +241,21 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
		u8 hw_version;
		int ret;

		ret = csiphy_set_clock_rates(csiphy);
		ret = pm_runtime_get_sync(dev);
		if (ret < 0)
			return ret;

		ret = csiphy_set_clock_rates(csiphy);
		if (ret < 0) {
			pm_runtime_put_sync(dev);
			return ret;
		}

		ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
		if (ret < 0)
		if (ret < 0) {
			pm_runtime_put_sync(dev);
			return ret;
		}

		enable_irq(csiphy->irq);

@@ -259,6 +268,8 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
		disable_irq(csiphy->irq);

		camss_disable_clocks(csiphy->nclocks, csiphy->clock);

		pm_runtime_put_sync(dev);
	}

	return 0;
+23 −3
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
@@ -169,6 +170,14 @@ static int ispif_reset(struct ispif_device *ispif)
	u32 val;
	int ret;

	ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0);
	if (ret < 0)
		return ret;

	ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1);
	if (ret < 0)
		return ret;

	ret = camss_enable_clocks(ispif->nclocks_for_reset,
				  ispif->clock_for_reset,
				  to_device(ispif));
@@ -201,12 +210,15 @@ static int ispif_reset(struct ispif_device *ispif)
		msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS));
	if (!time) {
		dev_err(to_device(ispif), "ISPIF reset timeout\n");
		return -EIO;
		ret = -EIO;
	}

	camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset);

	return 0;
	camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0);
	camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE1);

	return ret;
}

/*
@@ -232,12 +244,19 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on)
			goto exit;
		}

		ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
		ret = pm_runtime_get_sync(dev);
		if (ret < 0)
			goto exit;

		ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
		if (ret < 0) {
			pm_runtime_put_sync(dev);
			goto exit;
		}

		ret = ispif_reset(ispif);
		if (ret < 0) {
			pm_runtime_put_sync(dev);
			camss_disable_clocks(ispif->nclocks, ispif->clock);
			goto exit;
		}
@@ -252,6 +271,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on)
			goto exit;
		} else if (ispif->power_count == 1) {
			camss_disable_clocks(ispif->nclocks, ispif->clock);
			pm_runtime_put_sync(dev);
		}

		ispif->power_count--;
+17 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock_types.h>
#include <linux/spinlock.h>
#include <media/media-entity.h>
@@ -1981,6 +1982,14 @@ static int vfe_get(struct vfe_device *vfe)
	mutex_lock(&vfe->power_lock);

	if (vfe->power_count == 0) {
		ret = camss_pm_domain_on(vfe->camss, vfe->id);
		if (ret < 0)
			goto error_pm_domain;

		ret = pm_runtime_get_sync(vfe->camss->dev);
		if (ret < 0)
			goto error_pm_runtime_get;

		ret = vfe_set_clock_rates(vfe);
		if (ret < 0)
			goto error_clocks;
@@ -2012,6 +2021,12 @@ static int vfe_get(struct vfe_device *vfe)
	camss_disable_clocks(vfe->nclocks, vfe->clock);

error_clocks:
	pm_runtime_put_sync(vfe->camss->dev);

error_pm_runtime_get:
	camss_pm_domain_off(vfe->camss, vfe->id);

error_pm_domain:
	mutex_unlock(&vfe->power_lock);

	return ret;
@@ -2034,6 +2049,8 @@ static void vfe_put(struct vfe_device *vfe)
			vfe_halt(vfe);
		}
		camss_disable_clocks(vfe->nclocks, vfe->clock);
		pm_runtime_put_sync(vfe->camss->dev);
		camss_pm_domain_off(vfe->camss, vfe->id);
	}

	vfe->power_count--;
+63 −0
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/videodev2.h>

@@ -393,6 +395,26 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
	return 0;
}

int camss_pm_domain_on(struct camss *camss, int id)
{
	if (camss->version == CAMSS_8x96) {
		camss->genpd_link[id] = device_link_add(camss->dev,
				camss->genpd[id], DL_FLAG_STATELESS |
				DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);

		if (!camss->genpd_link[id])
			return -EINVAL;
	}

	return 0;
}

void camss_pm_domain_off(struct camss *camss, int id)
{
	if (camss->version == CAMSS_8x96)
		device_link_del(camss->genpd_link[id]);
}

/*
 * camss_of_parse_endpoint_node - Parse port endpoint node
 * @dev: Device
@@ -896,6 +918,23 @@ static int camss_probe(struct platform_device *pdev)
		}
	}

	if (camss->version == CAMSS_8x96) {
		camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id(
						camss->dev, PM_DOMAIN_VFE0);
		if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0]))
			return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]);

		camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id(
						camss->dev, PM_DOMAIN_VFE1);
		if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) {
			dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0],
					     true);
			return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]);
		}
	}

	pm_runtime_enable(dev);

	return 0;

err_register_subdevs:
@@ -912,6 +951,13 @@ void camss_delete(struct camss *camss)
	media_device_unregister(&camss->media_dev);
	media_device_cleanup(&camss->media_dev);

	pm_runtime_disable(camss->dev);

	if (camss->version == CAMSS_8x96) {
		dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true);
		dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true);
	}

	kfree(camss);
}

@@ -947,12 +993,29 @@ static const struct of_device_id camss_dt_match[] = {

MODULE_DEVICE_TABLE(of, camss_dt_match);

static int camss_runtime_suspend(struct device *dev)
{
	return 0;
}

static int camss_runtime_resume(struct device *dev)
{
	return 0;
}

static const struct dev_pm_ops camss_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
				pm_runtime_force_resume)
	SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL)
};

static struct platform_driver qcom_camss_driver = {
	.probe = camss_probe,
	.remove = camss_remove,
	.driver = {
		.name = "qcom-camss",
		.of_match_table = camss_dt_match,
		.pm = &camss_pm_ops,
	},
};

Loading