Loading Documentation/devicetree/bindings/media/video/msm-vidc-bus.txt +37 −13 Original line number Diff line number Diff line Loading @@ -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>; }; }; }; arch/arm/boot/dts/qcom/msm8937-vidc.dtsi +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 Loading Loading @@ -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>; }; }; }; }; arch/arm/boot/dts/qcom/msmtitanium-vidc.dtsi +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 Loading Loading @@ -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>; }; Loading @@ -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>; }; }; }; }; drivers/media/platform/msm/vidc/governors/msm_vidc_table_gov.c +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 Loading @@ -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; Loading Loading @@ -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", Loading @@ -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) Loading Loading @@ -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; Loading drivers/media/platform/msm/vidc/msm_vidc_res_parse.c +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 Loading Loading @@ -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 Loading
Documentation/devicetree/bindings/media/video/msm-vidc-bus.txt +37 −13 Original line number Diff line number Diff line Loading @@ -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>; }; }; };
arch/arm/boot/dts/qcom/msm8937-vidc.dtsi +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 Loading Loading @@ -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>; }; }; }; };
arch/arm/boot/dts/qcom/msmtitanium-vidc.dtsi +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 Loading Loading @@ -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>; }; Loading @@ -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>; }; }; }; };
drivers/media/platform/msm/vidc/governors/msm_vidc_table_gov.c +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 Loading @@ -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; Loading Loading @@ -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", Loading @@ -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) Loading Loading @@ -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; Loading
drivers/media/platform/msm/vidc/msm_vidc_res_parse.c +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 Loading Loading @@ -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