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

Commit b4bc3f3e authored by Deva Ramasubramanian's avatar Deva Ramasubramanian
Browse files

msm: vidc: Add support for interframe power collapse



On targets that support it (namely apq8084), give up control of the
core0 and core1 GDSCs to the hardware so that it can independently
decide to powercollapse when no hardware operations are being performed
on the respective cores.

Change-Id: I09397fde101cc0b91b97c0ae646ea09e8b117b9d
Signed-off-by: default avatarDeva Ramasubramanian <dramasub@codeaurora.org>
parent 15f6afcf
Loading
Loading
Loading
Loading
+40 −22
Original line number Diff line number Diff line
@@ -12,7 +12,6 @@ Required properties:
Optional properties:
- reg : offset and length of the register set for the device.
- interrupts : should contain the vidc interrupt.
- vdd-supply : regulator to supply venus.
- qcom,vidc-cp-map : start and size of device virtual address range for
  secure buffers. Video hardware uses this address range to identify if
  the buffers are secure or non-secure.
@@ -59,6 +58,19 @@ Optional properties:
          internal persist = 0x80
          internal persist1 = 0x100
          internal cmd queue = 0x200
- *-supply: A phandle pointing to the appropriate regulator. Number of
  regulators vary across targets.
- qcom,clock-names: an array of clocks that the driver is supposed to be
  manipulating. The clocks names here correspond to the clock names used in
  clk_get(<name>).
- qcom,clock-configs = an array of bitmaps of clocks' configurations. The index
  of the bitmap corresponds to the clock at the same index in qcom,clock-names.
  The bitmaps describes the actions that the device needs to take regarding the
  clock (i.e. scale it based on load).

  The bitmap is defined as:
  scalable = 0x1 (if the driver should vary the clock's frequency based on load)
  power collapsible = 0x2 (if the driver should disable the clock if no load)

Example:

@@ -67,7 +79,9 @@ Example:
		compatible = "qcom,msm-vidc";
		reg = <0xfdc00000 0xff000>;
		interrupts = <0 44 0>;
		vdd-supply = <&gdsc_venus>;
		venus-supply = <&gdsc>;
		venus-core0-supply = <&gdsc1>;
		venus-core1-supply = <&gdsc2>;
		qcom,vidc-cp-map = <0x1000000 0x40000000>;
		qcom,vidc-ns-map = <0x40000000 0x40000000>;
		qcom,load-freq-tbl = <979200 410000000>,
@@ -105,4 +119,8 @@ Example:
							<0x1f1>;
			};
		};

		qcom,clock-names = "foo_clk", "bar_clk", "baz_clk";
		qcom,clock-properties = <0x3 0x1 0x0>;

	};
+19 −0
Original line number Diff line number Diff line
@@ -265,6 +265,25 @@ int create_pkt_cmd_session_cmd(struct vidc_hal_session_cmd_pkt *pkt,
	return rc;
}

int create_pkt_cmd_sys_power_control(
	struct hfi_cmd_sys_set_property_packet *pkt, u32 enable)
{
	struct hfi_enable *hfi;
	if (!pkt) {
		dprintk(VIDC_ERR, "No input packet\n");
		return -EINVAL;
	}

	pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
		sizeof(struct hfi_enable) + sizeof(u32);
	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
	pkt->num_properties = 1;
	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL;
	hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
	hfi->enable = enable;
	return 0;
}

static u32 get_hfi_buffer(int hal_buffer)
{
	u32 buffer;
+3 −0
Original line number Diff line number Diff line
@@ -26,6 +26,9 @@ int create_pkt_cmd_sys_idle_indicator(
		struct hfi_cmd_sys_set_property_packet *pkt,
		u32 enable);

int create_pkt_cmd_sys_power_control(
	struct hfi_cmd_sys_set_property_packet *pkt, u32 enable);

int create_pkt_set_cmd_sys_resource(
		struct hfi_cmd_sys_set_resource_packet *pkt,
		struct vidc_resource_hdr *resource_hdr,
+213 −4
Original line number Diff line number Diff line
@@ -17,6 +17,11 @@
#include "msm_vidc_debug.h"
#include "msm_vidc_res_parse.h"

enum clock_properties {
	CLOCK_PROP_HAS_SCALING = 1 << 0,
	CLOCK_PROP_HAS_SW_POWER_COLLAPSE = 1 << 1,
};

struct master_slave {
	int masters_ocmem[2];
	int masters_ddr[2];
@@ -157,9 +162,36 @@ static inline void msm_vidc_free_buffer_usage_table(
	res->buffer_usage_set.buffer_usage_tbl = NULL;
}

static inline void msm_vidc_free_regulator_table(
			struct msm_vidc_platform_resources *res)
{
	int c = 0;
	for (c = 0; c < res->regulator_set.count; ++c) {
		struct regulator_info *rinfo =
			&res->regulator_set.regulator_tbl[c];

		kfree(rinfo->name);
		rinfo->name = NULL;
	}

	kfree(res->regulator_set.regulator_tbl);
	res->regulator_set.regulator_tbl = NULL;
	res->regulator_set.count = 0;
}

static inline void msm_vidc_free_clock_table(
			struct msm_vidc_platform_resources *res)
{
	kfree(res->clock_set.clock_tbl);
	res->clock_set.clock_tbl = NULL;
	res->clock_set.count = 0;
}

void msm_vidc_free_platform_resources(
			struct msm_vidc_platform_resources *res)
{
	msm_vidc_free_clock_table(res);
	msm_vidc_free_regulator_table(res);
	msm_vidc_free_freq_table(res);
	msm_vidc_free_reg_table(res);
	msm_vidc_free_bus_vectors(res);
@@ -480,7 +512,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res)
			of_property_read_bool(ctx_node,	"qcom,secure-domain");

		dprintk(VIDC_DBG,
				"domain %s : secure = %d",
				"domain %s : secure = %d\n",
				iommu_map->name,
				iommu_map->is_secure);

@@ -492,7 +524,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res)

		if (rc) {
			dprintk(VIDC_ERR,
					"cannot load partition buffertype information (%d)",
					"cannot load partition buffertype information (%d)\n",
					rc);
			rc = -ENOENT;
			goto err_load_groups;
@@ -546,6 +578,165 @@ err_load_buf_usage:
	return rc;
}

static int msm_vidc_load_regulator_table(
		struct msm_vidc_platform_resources *res)
{
	int rc = 0;
	struct platform_device *pdev = res->pdev;
	struct regulator_set *regulators = &res->regulator_set;
	struct device_node *domains_parent_node = NULL;
	struct property *domains_property = NULL;

	regulators->count = 0;
	regulators->regulator_tbl = NULL;

	domains_parent_node = pdev->dev.of_node;
	for_each_property_of_node(domains_parent_node, domains_property) {
		const char *search_string = "-supply";
		char *supply;
		bool matched = false;
		struct device_node *regulator_node = NULL;
		struct regulator_info *rinfo = NULL;
		void *temp = NULL;

		/* 1) check if current property is possibly a regulator */
		supply = strnstr(domains_property->name, search_string,
				strlen(domains_property->name) + 1);
		matched = supply && (*(supply + strlen(search_string)) == '\0');
		if (!matched)
			continue;

		/* 2) make sure prop isn't being misused */
		regulator_node = of_parse_phandle(domains_parent_node,
				domains_property->name, 0);
		if (IS_ERR(regulator_node)) {
			dprintk(VIDC_WARN, "%s is not a phandle\n",
					domains_property->name);
			continue;
		}

		/* 3) expand our table */
		temp = krealloc(regulators->regulator_tbl,
				sizeof(*regulators->regulator_tbl) *
				(regulators->count + 1), GFP_KERNEL);
		if (!temp) {
			rc = -ENOMEM;
			dprintk(VIDC_ERR,
					"Failed to alloc memory for regulator table\n");
			goto err_reg_tbl_alloc;
		}

		regulators->regulator_tbl = temp;
		regulators->count++;

		/* 4) populate regulator info */
		rinfo = &regulators->regulator_tbl[regulators->count - 1];
		rinfo->name = kstrndup(domains_property->name,
				supply - domains_property->name, GFP_KERNEL);
		if (!rinfo->name) {
			rc = -ENOMEM;
			dprintk(VIDC_ERR,
					"Failed to alloc memory for regulator name\n");
			goto err_reg_name_alloc;
		}

		rinfo->has_hw_power_collapse = of_property_read_bool(
			regulator_node, "qcom,support-hw-trigger");

		dprintk(VIDC_DBG, "Found regulator %s: h/w collapse = %s\n",
				rinfo->name,
				rinfo->has_hw_power_collapse ? "yes" : "no");
	}

	if (!regulators->count)
		dprintk(VIDC_DBG, "No regulators found");

	return 0;

err_reg_name_alloc:
err_reg_tbl_alloc:
	msm_vidc_free_regulator_table(res);
	return rc;
}

static int msm_vidc_load_clock_table(
		struct msm_vidc_platform_resources *res)
{
	int rc = 0, num_clocks = 0, c = 0;
	struct platform_device *pdev = res->pdev;
	int *clock_props = NULL;
	struct venus_clock_set *clocks = &res->clock_set;

	num_clocks = of_property_count_strings(pdev->dev.of_node,
				"qcom,clock-names");
	if (num_clocks <= 0) {
		/* Devices such as Q6 might not have any control over clocks
		 * hence have none specified, which is ok. */
		dprintk(VIDC_DBG, "No clocks found\n");
		clocks->count = 0;
		rc = 0;
		goto err_load_clk_table_fail;
	}

	clock_props = kzalloc(num_clocks * sizeof(*clock_props), GFP_KERNEL);
	if (!clock_props) {
		dprintk(VIDC_ERR, "No memory to read clock properties\n");
		rc = -ENOMEM;
		goto err_load_clk_table_fail;
	}

	rc = of_property_read_u32_array(pdev->dev.of_node,
				"qcom,clock-configs", clock_props,
				num_clocks);
	if (rc) {
		dprintk(VIDC_ERR, "Failed to read clock properties: %d\n", rc);
		goto err_load_clk_prop_fail;
	}

	clocks->clock_tbl = kzalloc(sizeof(*clocks->clock_tbl)
			* num_clocks, GFP_KERNEL);
	if (!clocks->clock_tbl) {
		dprintk(VIDC_ERR, "Failed to allocate memory for clock tbl\n");
		rc = -ENOMEM;
		goto err_load_clk_prop_fail;
	}

	clocks->count = num_clocks;
	dprintk(VIDC_DBG, "Found %d clocks\n", num_clocks);

	for (c = 0; c < num_clocks; ++c) {
		struct venus_clock *vc = &res->clock_set.clock_tbl[c];

		of_property_read_string_index(pdev->dev.of_node,
				"qcom,clock-names", c, &vc->name);

		if (clock_props[c] & CLOCK_PROP_HAS_SCALING) {
			vc->count = res->load_freq_tbl_size;
			vc->load_freq_tbl = res->load_freq_tbl;
		} else {
			vc->count = 0;
			vc->load_freq_tbl = NULL;
		}

		vc->has_sw_power_collapse = !!(clock_props[c] &
				CLOCK_PROP_HAS_SW_POWER_COLLAPSE);

		dprintk(VIDC_DBG,
				"Found clock %s: scales = %s, s/w collapse = %s\n",
				vc->name,
				vc->count ? "yes" : "no",
				vc->has_sw_power_collapse ? "yes" : "no");
	}

	kfree(clock_props);
	return 0;

err_load_clk_prop_fail:
	kfree(clock_props);
err_load_clk_table_fail:
	return rc;
}

int read_platform_resources_from_dt(
		struct msm_vidc_platform_resources *res)
{
@@ -597,16 +788,34 @@ int read_platform_resources_from_dt(
		goto err_load_buffer_usage_table;
	}

	rc = msm_vidc_load_regulator_table(res);
	if (rc) {
		dprintk(VIDC_ERR, "Failed to load list of regulators %d\n", rc);
		goto err_load_regulator_table;
	}

	rc = msm_vidc_load_clock_table(res);
	if (rc) {
		dprintk(VIDC_ERR,
			"Failed to load clock table: %d\n", rc);
		goto err_load_clock_table;
	}

	rc = of_property_read_u32(pdev->dev.of_node, "qcom,max-hw-load",
			&res->max_load);
	if (rc) {
		dprintk(VIDC_ERR,
			"Failed to determine max load supported: %d\n", rc);
		goto err_load_buffer_usage_table;
		goto err_load_max_hw_load;
	}

	return rc;

err_load_max_hw_load:
	msm_vidc_free_clock_table(res);
err_load_clock_table:
	msm_vidc_free_regulator_table(res);
err_load_regulator_table:
	msm_vidc_free_buffer_usage_table(res);
err_load_buffer_usage_table:
	msm_vidc_free_iommu_groups(res);
err_load_iommu_groups:
+26 −0
Original line number Diff line number Diff line
@@ -63,6 +63,30 @@ struct buffer_usage_set {
	u32 count;
};

struct regulator_info {
	struct regulator *regulator;
	bool has_hw_power_collapse;
	char *name;
};

struct regulator_set {
	struct regulator_info *regulator_tbl;
	u32 count;
};

struct venus_clock {
	const char *name;
	struct clk *clk;
	struct load_freq_table *load_freq_tbl;
	u32 count; /* == has_scaling iff count != 0 */
	bool has_sw_power_collapse;
};

struct venus_clock_set {
	struct venus_clock *clock_tbl;
	u32 count;
};

struct msm_vidc_platform_resources {
	uint32_t fw_base_addr;
	uint32_t register_base;
@@ -77,6 +101,8 @@ struct msm_vidc_platform_resources {
	bool has_ocmem;
	uint32_t max_load;
	struct platform_device *pdev;
	struct regulator_set regulator_set;
	struct venus_clock_set clock_set;
};

static inline int is_iommu_present(struct msm_vidc_platform_resources *res)
Loading