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

Commit 4ab27b02 authored by Maheshwar Ajja's avatar Maheshwar Ajja
Browse files

msm: vidc: modify bus table governor to add ubwc bus vectors



Modify the existing bus table governor to add ubwc bus vectors
to reduce bus bandwidth when client sets ubwc color format
on video sessions.

Change-Id: I3d90f5d4737951c628d048946f264625b97c7e55
Signed-off-by: default avatarMaheshwar Ajja <majja@codeaurora.org>
parent a660e4d0
Loading
Loading
Loading
Loading
+37 −13
Original line number Diff line number Diff line
@@ -3,23 +3,47 @@
Required properties:
- compatible : "qcom,msm-vidc,governor,table"
- name : name of the governor.
- qcom,bus-table : the table comprised of load (macro blocks per second),
  frequency (KBps) and codec type (u32) to select appropriate bus frequency
  for a given load and codec type.
- qcom,bus-table : node containing individual domain nodes, each with:
  - qcom,codec-mask: a bitmap of supported codec types, every two bits
    represents a codec type.
  - qcom,load-busfreq-tbl: load (in macroblocks/sec) and the corresponding
    bus frequency (in KBps) table.

Optional properties:
- qcom,low-power-mode: a boolean which indicates whether bus profile need
  to be used when client enables low-power mode.
- qcom,ubwc-mode: a boolean which indicates whether the bus profile need
  to be used when client enables UBWC mode.

Example:

venus-ddr-gov {
venus-bus-gov {
	compatible = "qcom,msm-vidc,governor,table";
	name = "qcom,venus-gov";
	qcom,bus-table =
		/* Encoders */
		<244800 787456 0x55555555>,   /* 1080p30E   */
		<108000 350208 0x55555555>,   /* 720p30E    */
		<0 0 0x55555555>,
		/* Decoders */
		<244800 618496 0xffffffff>,   /* 1080p30D   */
		<108000 314368 0xffffffff>,   /* 720p30D    */
		<0 0 0xffffffff>;
	qcom,bus-freq-table {
		qcom,profile-dec {
			qcom,codec-mask = <0xffffffff>;
			qcom,ubwc-mode;
			qcom,load-busfreq-tbl =
				<489600 1205248>,  /* 1080p60D   */
				<244800 618496>,   /* 1080p30D   */
				<216000 618496>,   /* 720p60D    */
				<108000 314368>,   /* 720p30D    */
				<72000  233472>,   /* VGA60D     */
				<36000  118784>,   /* VGA30D     */
				<0      0>;
		};
		qcom,profile-enc {
			qcom,codec-mask = <0x55555555>;
			qcom,low-power-mode;
			qcom,load-busfreq-tbl =
				<244800 787456>,   /* 1080p30E   */
				<216000 350208>,   /* 720p60E    */
				<108000 350208>,   /* 720p30E    */
				<72000  350208>,   /* VGA60E     */
				<36000  136806>,   /* VGA30E     */
				<0      0>;
		};
	};
};
+37 −30
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -105,32 +105,39 @@
		compatible = "qcom,msm-vidc,governor,table";
		name = "venus-ddr-gov";
		status = "ok";
			qcom,bus-table =
				/* Encoders */
				<244800 698000 0x55555555>,   /* 1080p30E   */
				<216000 624000 0x55555555>,   /* 720p60E    */
				<194400 556000 0x55555555>,   /* FWVGA120E  */
				<144000 412000 0x55555555>,   /* VGA120E    */
				<108000 353000 0x55555555>,   /* 720p30E    */
				<97200  316000 0x55555555>,   /* FWVGA60E   */
				<48600  158000 0x55555555>,   /* FWVGA30E   */
				<72000  234000 0x55555555>,   /* VGA60E     */
				<36000  117000 0x55555555>,   /* VGA30E     */
				<18000  60000  0x55555555>,   /* QVGA60E    */
				<9000   30000  0x55555555>,   /* QVGA30E    */
				<0      0      0x55555555>,
				/* Decoders */
				<244800 605000 0xffffffff>,   /* 1080p30D   */
				<216000 540000 0xffffffff>,   /* 720p60D    */
				<194400 484000 0xffffffff>,   /* FWVGA120D  */
				<144000 360000 0xffffffff>,   /* VGA120D    */
				<108000 270000 0xffffffff>,   /* 720p30D    */
				<97200  242000 0xffffffff>,   /* FWVGA60D   */
				<48600  121000 0xffffffff>,   /* FWVGA30D   */
				<72000  180000 0xffffffff>,   /* VGA60D     */
				<36000  90000  0xffffffff>,   /* VGA30D     */
				<18000  45000  0xffffffff>,   /* HVGA30D    */
				<0      0      0xffffffff>;
		qcom,bus-freq-table {
			qcom,profile-enc {
				qcom,codec-mask = <0x55555555>;
				qcom,load-busfreq-tbl =
					<244800 698000>,   /* 1080p30E   */
					<216000 624000>,   /* 720p60E    */
					<194400 556000>,   /* FWVGA120E  */
					<144000 412000>,   /* VGA120E    */
					<108000 353000>,   /* 720p30E    */
					<97200  316000>,   /* FWVGA60E   */
					<48600  158000>,   /* FWVGA30E   */
					<72000  234000>,   /* VGA60E     */
					<36000  117000>,   /* VGA30E     */
					<18000  60000>,    /* QVGA60E    */
					<9000   30000>,    /* QVGA30E    */
					<0      0>;
			};
			qcom,profile-dec {
				qcom,codec-mask = <0xffffffff>;
				qcom,load-busfreq-tbl =
					<244800 605000>,   /* 1080p30D   */
					<216000 540000>,   /* 720p60D    */
					<194400 484000>,   /* FWVGA120D  */
					<144000 360000>,   /* VGA120D    */
					<108000 270000>,   /* 720p30D    */
					<97200  242000>,   /* FWVGA60D   */
					<48600  121000>,   /* FWVGA30D   */
					<72000  180000>,   /* VGA60D     */
					<36000  90000>,    /* VGA30D     */
					<18000  45000>,    /* HVGA30D    */
					<0      0>;
			};
		};
	};

};
+52 −4
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -96,7 +96,7 @@
			label = "venus-ddr";
			qcom,bus-master = <63>;
			qcom,bus-slave = <512>;
			qcom,bus-governor = "msm-vidc-ddr";
			qcom,bus-governor = "venus-ddr-gov";
			qcom,bus-range-kbps = <1000 1205248>;
		};

@@ -109,4 +109,52 @@
			qcom,bus-range-kbps = <1 1>;
		};
	};

	venus-ddr-gov {
		compatible = "qcom,msm-vidc,governor,table";
		name = "venus-ddr-gov";
		status = "ok";
		qcom,bus-freq-table {
			qcom,profile-enc {
				qcom,codec-mask = <0x55555555>;
				qcom,load-busfreq-tbl =
					<979200 1044000>,  /* UHD30E     */
					<864000 887000>,   /* 720p240LPE */
					<489600 666000>,   /* 1080p60E   */
					<432000 578000>,   /* 720p120E   */
					<244800 346000>,   /* 1080p30E   */
					<216000 293000>,   /* 720p60E    */
					<108000 151000>,   /* 720p30E    */
					<0 0>;
			};
			qcom,profile-dec {
				qcom,codec-mask = <0xffffffff>;
				qcom,load-busfreq-tbl =
					<979200 2365000>,  /* UHD30D     */
					<979200 2241000>,  /* 1080p120D  */
					<864000 1978000>,  /* 720p240D   */
					<489600 1133000>,  /* 1080p60D   */
					<432000 994000>,   /* 720p120D   */
					<244800 580000>,   /* 1080p30D   */
					<216000 501000>,   /* 720p60E    */
					<108000 255000>,   /* 720p30D    */
					<0 0>;
			};
			qcom,profile-dec-ubwc {
				qcom,codec-mask = <0xffffffff>;
				qcom,ubwc-mode;
				qcom,load-busfreq-tbl =
					<979200 1892000>,  /* UHD30D     */
					<979200 1763000>,  /* 1080p120D  */
					<864000 1554000>,  /* 720p240D   */
					<489600 895000>,   /* 1080p60D   */
					<432000 781000>,   /* 720p120D   */
					<244800 460000>,   /* 1080p30D   */
					<216000 301000>,   /* 720p60E    */
					<108000 202000>,   /* 720p30D    */
					<0 0>;
			};
		};
	};

};
+194 −60
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -16,74 +16,130 @@
#include <linux/string.h>
#include "governor.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_res_parse.h"
#include "msm_vidc_internal.h"
#include "venus_hfi.h"

struct msm_vidc_bus_table_gov {
enum bus_profile {
	VIDC_BUS_PROFILE_NORMAL			= BIT(0),
	VIDC_BUS_PROFILE_LOW			= BIT(1),
	VIDC_BUS_PROFILE_UBWC			= BIT(2),
};

struct bus_profile_entry {
	struct {
		u32 load, freq, codecs;
		u32 load, freq;
	} *bus_table;
	int bus_table_size;
	u32 bus_table_size;
	u32 codec_mask;
	enum bus_profile profile;
};

struct msm_vidc_bus_table_gov {
	struct bus_profile_entry *bus_prof_entries;
	u32 count;
	struct devfreq_governor devfreq_gov;
};

int msm_vidc_table_get_target_freq(struct devfreq *dev, unsigned long *freq,
		u32 *flag)
static int __get_bus_freq(struct msm_vidc_bus_table_gov *gov,
		struct vidc_bus_vote_data *data,
		enum bus_profile profile)
{
	int i = 0, load = 0, freq = 0;
	enum vidc_vote_data_session sess_type = 0;
	struct bus_profile_entry *entry = NULL;
	bool found = false;

	load = NUM_MBS_PER_SEC(data->width, data->height, data->fps);
	sess_type = VIDC_VOTE_DATA_SESSION_VAL(data->codec, data->domain);

	/* check if ubwc bus profile is present */
	for (i = 0; i < gov->count; i++) {
		entry = &gov->bus_prof_entries[i];
		if (!entry->bus_table || !entry->bus_table_size)
			continue;
		if (!venus_hfi_is_session_supported(
				entry->codec_mask, sess_type))
			continue;
		if (entry->profile == profile) {
			found = true;
			break;
		}
	}

	if (found) {
		/* loop over bus table and select frequency */
		for (i = entry->bus_table_size - 1; i >= 0; --i) {
			/* load is arranged in descending order */
			freq = entry->bus_table[i].freq;
			if (load <= entry->bus_table[i].load)
				break;
		}
	}

	return freq;
}

static int msm_vidc_table_get_target_freq(struct devfreq *dev,
		unsigned long *frequency, u32 *flag)
{
	struct devfreq_dev_status status = {0};
	struct msm_vidc_gov_data *vidc_data = NULL;
	struct msm_vidc_bus_table_gov *gov = NULL;
	enum vidc_vote_data_session sess_type = 0;
	u32 load = 0, i = 0;
	int j = 0;
	enum bus_profile profile = 0;
	int i = 0;

	if (!dev || !freq || !flag) {
	if (!dev || !frequency || !flag) {
		dprintk(VIDC_ERR, "%s: Invalid params %p, %p, %p\n",
			__func__, dev, freq, flag);
			__func__, dev, frequency, flag);
		return -EINVAL;
	}

	gov = container_of(dev->governor,
			struct msm_vidc_bus_table_gov, devfreq_gov);
	if (!gov) {
		dprintk(VIDC_ERR, "%s: governor not found\n", __func__);
		return -EINVAL;
	}

	dev->profile->get_dev_status(dev->dev.parent, &status);
	vidc_data = (struct msm_vidc_gov_data *)status.private_data;

	*freq = 0;
	for (i = 0; i < vidc_data->data_count; ++i) {
		struct vidc_bus_vote_data *curr = &vidc_data->data[i];
		u32 frequency = 0;
	*frequency = 0;
	for (i = 0; i < vidc_data->data_count; i++) {
		struct vidc_bus_vote_data *data = &vidc_data->data[i];
		int freq = 0;

		load = NUM_MBS_PER_SEC(curr->width, curr->height, curr->fps);
		sess_type = VIDC_VOTE_DATA_SESSION_VAL(
			curr->codec, curr->domain);

		if (curr->power_mode == VIDC_POWER_TURBO) {
			dprintk(VIDC_DBG, "found turbo session[%d] %#x\n",
				i, sess_type);
			*freq = INT_MAX;
		if (data->power_mode == VIDC_POWER_TURBO) {
			dprintk(VIDC_DBG, "bus: found turbo session[%d] %#x\n",
				i, VIDC_VOTE_DATA_SESSION_VAL(data->codec,
					data->domain));
			*frequency = INT_MAX;
			goto exit;
		}

		profile = VIDC_BUS_PROFILE_NORMAL;
		if (data->color_formats[0] == HAL_COLOR_FORMAT_NV12_TP10_UBWC ||
			data->color_formats[0] == HAL_COLOR_FORMAT_NV12_UBWC)
			profile = VIDC_BUS_PROFILE_UBWC;

		freq = __get_bus_freq(gov, data, profile);
		/*
		 * loop over bus table and select frequency of
		 * matching session and appropriate load
		 * chose frequency from normal profile
		 * if specific profile frequency was not found.
		 */
		for (j = gov->bus_table_size - 1; j >= 0; --j) {
			bool matches = venus_hfi_is_session_supported(
					gov->bus_table[j].codecs, sess_type);
			if (!matches)
				continue;
		if (!freq)
			freq = __get_bus_freq(gov, data,
				VIDC_BUS_PROFILE_NORMAL);

			frequency = gov->bus_table[j].freq;
			if (load <= gov->bus_table[j].load)
				break;
		}
		*freq += frequency;
		*frequency += (unsigned long)freq;

		dprintk(VIDC_DBG,
			"session[%d] %#x, wxh %dx%d, fps %d, load %d, freq %d, total_freq %ld\n",
			i, sess_type, curr->width, curr->height,
			curr->fps, load, frequency, *freq);
			"session[%d] %#x: wxh %dx%d, fps %d, bus_profile %#x, freq %d, total_freq %ld KBps\n",
			i, VIDC_VOTE_DATA_SESSION_VAL(
			data->codec, data->domain), data->width,
			data->height, data->fps, profile,
			freq, *frequency);
	}
exit:
	return 0;
@@ -111,11 +167,34 @@ int msm_vidc_table_event_handler(struct devfreq *devfreq,
	return rc;
}

static int msm_vidc_free_bus_table(struct platform_device *pdev,
		struct msm_vidc_bus_table_gov *data)
{
	int rc = 0, i = 0;

	if (!pdev || !data) {
		dprintk(VIDC_ERR, "%s: invalid args %p %p\n",
			__func__, pdev, data);
		return -EINVAL;
	}

	for (i = 0; i < data->count; i++)
		data->bus_prof_entries[i].bus_table = NULL;

	data->bus_prof_entries = NULL;
	data->count = 0;

	return rc;
}

static int msm_vidc_load_bus_table(struct platform_device *pdev,
		struct msm_vidc_bus_table_gov *data)
{
	int c = 0;
	int rc = 0, i = 0, j = 0;
	const char *name = NULL;
	struct bus_profile_entry *entry = NULL;
	struct device_node *parent_node = NULL;
	struct device_node *child_node = NULL;

	if (!pdev || !data) {
		dprintk(VIDC_ERR, "%s: invalid args %p %p\n",
@@ -136,33 +215,84 @@ static int msm_vidc_load_bus_table(struct platform_device *pdev,
	data->devfreq_gov.get_target_freq = msm_vidc_table_get_target_freq;
	data->devfreq_gov.event_handler = msm_vidc_table_event_handler;

	data->bus_table_size = of_property_count_elems_of_size(
			pdev->dev.of_node, "qcom,bus-table",
			sizeof(*data->bus_table));
	if (data->bus_table_size <= 0) {
		dprintk(VIDC_ERR, "%s: invalid bus table size %d\n",
			__func__, data->bus_table_size);
		return -EINVAL;
	parent_node = of_find_node_by_name(pdev->dev.of_node,
			"qcom,bus-freq-table");
	if (!parent_node) {
		dprintk(VIDC_DBG, "Node qcom,bus-freq-table not found.\n");
		return 0;
	}

	data->bus_table = devm_kcalloc(&pdev->dev, data->bus_table_size,
			sizeof(*data->bus_table), GFP_KERNEL);
	if (!data->bus_table) {
		dprintk(VIDC_ERR, "%s: allocation failed\n", __func__);
	data->count = of_get_child_count(parent_node);
	if (!data->count) {
		dprintk(VIDC_DBG, "No child nodes in qcom,bus-freq-table\n");
		return 0;
	}

	data->bus_prof_entries = devm_kzalloc(&pdev->dev,
			sizeof(*data->bus_prof_entries) * data->count,
			GFP_KERNEL);
	if (!data->bus_prof_entries) {
		dprintk(VIDC_DBG, "no memory to allocate bus_prof_entries\n");
		return -ENOMEM;
	}

	of_property_read_u32_array(pdev->dev.of_node, "qcom,bus-table",
			(u32 *)data->bus_table,
			sizeof(*data->bus_table) /
			sizeof(u32) * data->bus_table_size);
	for_each_child_of_node(parent_node, child_node) {

		if (i >= data->count) {
			dprintk(VIDC_ERR,
				"qcom,bus-freq-table: invalid child node %d, max is %d\n",
				i, data->count);
			break;
		}
		entry = &data->bus_prof_entries[i];

		if (of_find_property(child_node, "qcom,codec-mask", NULL)) {
			rc = of_property_read_u32(child_node,
					"qcom,codec-mask", &entry->codec_mask);
			if (rc) {
				dprintk(VIDC_ERR,
					"qcom,codec-mask not found\n");
				break;
			}
		}

		if (of_find_property(child_node, "qcom,low-power-mode", NULL))
			entry->profile = VIDC_BUS_PROFILE_LOW;
		else if (of_find_property(child_node, "qcom,ubwc-mode", NULL))
			entry->profile = VIDC_BUS_PROFILE_UBWC;
		else
			entry->profile = VIDC_BUS_PROFILE_NORMAL;

		if (of_find_property(child_node,
					"qcom,load-busfreq-tbl", NULL)) {
			rc = msm_vidc_load_u32_table(pdev, child_node,
						"qcom,load-busfreq-tbl",
						sizeof(*entry->bus_table),
						(u32 **)&entry->bus_table,
						&entry->bus_table_size);
			if (rc) {
				dprintk(VIDC_ERR,
					"qcom,load-busfreq-tbl failed\n");
				break;
			}
		} else {
			entry->bus_table = NULL;
			entry->bus_table_size = 0;
		}

	dprintk(VIDC_DBG, "%s: bus table:\n", __func__);
	for (c = 0; c < data->bus_table_size; ++c)
		dprintk(VIDC_DBG, "%8d %8d %#x\n", data->bus_table[c].load,
			data->bus_table[c].freq, data->bus_table[c].codecs);
		dprintk(VIDC_DBG,
			"qcom,load-busfreq-tbl: size %d, codec_mask %#x, profile %#x\n",
			entry->bus_table_size, entry->codec_mask,
			entry->profile);
		for (j = 0; j < entry->bus_table_size; j++)
			dprintk(VIDC_DBG, "   load %8d freq %8d\n",
				entry->bus_table[j].load,
				entry->bus_table[j].freq);

		i++;
	}

	return 0;
	return rc;
}

static int msm_vidc_bus_table_probe(struct platform_device *pdev)
@@ -202,6 +332,10 @@ static int msm_vidc_bus_table_remove(struct platform_device *pdev)
	if (IS_ERR_OR_NULL(gov))
		return PTR_ERR(gov);

	rc = msm_vidc_free_bus_table(pdev, gov);
	if (rc)
		dprintk(VIDC_WARN, "%s: free bus table failed\n", __func__);

	rc = devfreq_remove_governor(&gov->devfreq_gov);

	return rc;
+18 −5
Original line number Diff line number Diff line
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -303,11 +303,24 @@ static int msm_vidc_load_imem_ab_table(struct msm_vidc_platform_resources *res)
	return 0;
}

/*
 * this is a generic implementation to load single or
 * multiple array table (array elements should be of u32)
/**
 * msm_vidc_load_u32_table() - load dtsi table entries
 * @pdev: A pointer to the platform device.
 * @of_node:      A pointer to the device node.
 * @table_name:   A pointer to the dtsi table entry name.
 * @struct_size:  The size of the structure which is nothing but
 *                a single entry in the dtsi table.
 * @table:        A pointer to the table pointer which needs to be
 *                filled by the dtsi table entries.
 * @num_elements: Number of elements pointer which needs to be filled
 *                with the number of elements in the table.
 *
 * This is a generic implementation to load single or multiple array
 * table from dtsi. The array elements should be of size equal to u32.
 *
 * Return:        Return '0' for success else appropriate error value.
 */
static int msm_vidc_load_u32_table(struct platform_device *pdev,
int msm_vidc_load_u32_table(struct platform_device *pdev,
		struct device_node *of_node, char *table_name, int struct_size,
		u32 **table, u32 *num_elements)
{
Loading