Loading Documentation/devicetree/bindings/media/video/msm-vpu.txt +3 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ Required properties: - interrupt-names : Names corresponding to the defined interrupts list. - "vpu_wdog" : Watchdog interrupt - "vpu_hfi" : Firmware to Host interrupt - clock-names: Array of clocks that the driver requires for the device. The names here correspond to the clock names used in clk_get(<name>). - qcom,maple-clk-load-freq-tbl: Table of <load, freq> entries. An entry specifies a given VPU processing load (in bits per second), and a Loading @@ -41,6 +43,7 @@ Example: reg-names = "vpu_csr", "vpu_smem"; interrupts = <0 44 0>, <0 45 0>; interrupt-names = "vpu_wdog", "vpu_hfi"; clock-names = "core_clk", "bus_clock", "iface_clk"; qcom,maple-clk-load-freq-tbl = <100000 50000000>, <500000 400000000>; qcom,vdp-clk-load-freq-tbl = <200000 100000000>, Loading drivers/media/platform/msm/vpu/vpu_bus_clock.c +91 −81 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -156,33 +156,15 @@ int vpu_bus_scale(u32 load) * vpu_vdp_clk between 200MHz and 400MHz during runtime to optimize for * power consumption */ #define VPU_CLK_GATE_LEVEL VPU_VDP_CLK static const char *clock_names[VPU_MAX_CLKS] = { [VPU_BUS_CLK] = "vdp_bus_clk", [VPU_MAPLE_CLK] = "core_clk", [VPU_VDP_CLK] = "vdp_clk", [VPU_AHB_CLK] = "iface_clk", [VPU_AXI_CLK] = "bus_clk", [VPU_SLEEP_CLK] = "sleep_clk", [VPU_CXO_CLK] = "cxo_clk", [VPU_MAPLE_AXI_CLK] = "maple_bus_clk", [VPU_PRNG_CLK] = "prng_clk", }; struct vpu_core_clock { struct clk *clk; u32 status; u32 current_freq; struct load_freq_table *load_freq_tbl; const char *name; }; #define NOT_USED(clk_ctrl, i) (!( \ ((clk_ctrl)->clock[i]->flag & CLOCK_PRESENT) && \ ((clk_ctrl)->clock[i]->flag & (clk_ctrl)->mask))) #define NOT_IN_GROUP(clk_ctrl, i, clk_group) (!( \ ((clk_ctrl)->clock[i]->flag & (clk_group)))) static const u32 clock_freqs[VPU_MAX_CLKS][VPU_POWER_MAX] = { [VPU_BUS_CLK] = { 40000000, 80000000, 80000000}, [VPU_MAPLE_CLK] = {200000000, 400000000, 400000000}, [VPU_VDP_CLK] = {200000000, 200000000, 400000000}, }; #define CLOCK_IS_SCALABLE(clk_ctrl, i) \ ((clk_ctrl)->clock[i]->flag & CLOCK_SCALABLE) /* * Note: there is no lock in this block Loading @@ -194,14 +176,15 @@ struct vpu_clk_control { /* svs, nominal, turbo, dynamic(default) */ u32 mode; struct vpu_core_clock clock[VPU_MAX_CLKS]; struct vpu_clock *clock[VPU_MAX_CLKS]; u32 mask; }; void *vpu_clock_init(struct vpu_platform_resources *res) { int i; int rc = -1; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctrl; if (!res) Loading @@ -214,14 +197,21 @@ void *vpu_clock_init(struct vpu_platform_resources *res) return NULL; } for (i = 0; i < VPU_MAX_CLKS; i++) clk_ctrl->clock[i] = &res->clock[i]; /* mask allowing to only enable clocks from certain groups of clocks */ clk_ctrl->mask = CLOCK_CORE; /* setup the clock handles */ for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctrl->clock[i]; if (NOT_USED(clk_ctrl, i)) continue; cl->load_freq_tbl = &res->clock_tables[i]; cl->name = clock_names[i]; cl = clk_ctrl->clock[i]; if (i <= VPU_CLK_GATE_LEVEL && cl->load_freq_tbl->count == 0) { if (CLOCK_IS_SCALABLE(clk_ctrl, i) && !cl->load_freq_tbl.count) { pr_err("%s freq table size is 0\n", cl->name); goto fail_init_clocks; } Loading @@ -229,27 +219,16 @@ void *vpu_clock_init(struct vpu_platform_resources *res) cl->clk = devm_clk_get(&res->pdev->dev, cl->name); if (IS_ERR_OR_NULL(cl->clk)) { pr_err("Failed to get clock: %s\n", cl->name); rc = PTR_ERR(cl->clk); break; } cl->status = 0; } /* free the clock if not all successful */ if (i < VPU_MAX_CLKS) { for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctrl->clock[i]; if (cl->clk) { clk_put(cl->clk); cl->clk = NULL; } } goto fail_init_clocks; } cl->status = 0; } return clk_ctrl; fail_init_clocks: vpu_clock_deinit((void *)clk_ctrl); kfree(clk_ctrl); return NULL; } Loading @@ -257,7 +236,7 @@ fail_init_clocks: void vpu_clock_deinit(void *clkh) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; if (!clk_ctr) { Loading @@ -266,21 +245,32 @@ void vpu_clock_deinit(void *clkh) } for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (NOT_USED(clk_ctr, i)) continue; cl = clk_ctr->clock[i]; if (cl->status) { clk_disable_unprepare(cl->clk); cl->status = 0; } if (cl->clk) { clk_put(cl->clk); cl->clk = NULL; } } kfree(clk_ctr); } int vpu_clock_enable(void *clkh) /* * vpu_clock_enable() - enable a group of clocks * * @clkh: clock handler * @clk_group: see vpu_clock_flag (group section) * */ int vpu_clock_enable(void *clkh, u32 clk_group) { struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; int i = 0; int rc = 0; Loading @@ -293,14 +283,18 @@ int vpu_clock_enable(void *clkh) clk_ctr->mode = VPU_POWER_DYNAMIC; for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (NOT_USED(clk_ctr, i) || NOT_IN_GROUP(clk_ctr, i, clk_group)) continue; cl = clk_ctr->clock[i]; if (cl->status == 0) { /* set rate if it's a gated clock */ if (i <= VPU_CLK_GATE_LEVEL && cl->load_freq_tbl->entry) { if (CLOCK_IS_SCALABLE(clk_ctr, i) && cl->load_freq_tbl.entry) { cl->current_freq = cl->load_freq_tbl->entry[0].freq; cl->load_freq_tbl.entry[0].freq; rc = clk_set_rate(cl->clk, cl->current_freq); if (rc) { Loading @@ -325,21 +319,21 @@ int vpu_clock_enable(void *clkh) return rc; fail_clk_enable: for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (cl->status) { clk_disable_unprepare(cl->clk); cl->status = 0; } } vpu_clock_disable(clkh, clk_group); return rc; } void vpu_clock_disable(void *clkh) /* * vpu_clock_disable() - disable a group of clocks * * @clkh: clock handler * @clk_group: see vpu_clock_flag (group section) * */ void vpu_clock_disable(void *clkh, u32 clk_group) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; if (!clk_ctr) { Loading @@ -348,7 +342,10 @@ void vpu_clock_disable(void *clkh) } for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (NOT_USED(clk_ctr, i) || NOT_IN_GROUP(clk_ctr, i, clk_group)) continue; cl = clk_ctr->clock[i]; if (cl->status) { clk_disable_unprepare(cl->clk); cl->status = 0; Loading @@ -356,10 +353,10 @@ void vpu_clock_disable(void *clkh) } } static unsigned long __clock_get_rate(struct vpu_core_clock *clock, static unsigned long __clock_get_rate(struct vpu_clock *clock, u32 num_bits_per_sec) { struct load_freq_table *table = clock->load_freq_tbl; struct load_freq_table *table = &clock->load_freq_tbl; unsigned long ret = 0; int i; Loading @@ -385,10 +382,13 @@ int vpu_clock_scale(void *clkh, u32 load) clk_ctr->load = load; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { struct vpu_core_clock *cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { struct vpu_clock *cl = clk_ctr->clock[i]; unsigned long freq; if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; freq = __clock_get_rate(cl, load); if (clk_ctr->mode == VPU_POWER_DYNAMIC) { rc = clk_set_rate(cl->clk, freq); Loading @@ -407,7 +407,7 @@ int vpu_clock_scale(void *clkh, u32 load) int vpu_clock_gating_off(void *clkh) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; int rc = 0; Loading @@ -420,8 +420,11 @@ int vpu_clock_gating_off(void *clkh) if (clk_ctr->mode != VPU_POWER_DYNAMIC) return 0; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; cl = clk_ctr->clock[i]; if (cl->status == 0) { rc = clk_enable(cl->clk); if (rc) { Loading @@ -440,7 +443,7 @@ int vpu_clock_gating_off(void *clkh) void vpu_clock_gating_on(void *clkh) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; if (!clk_ctr) { Loading @@ -452,8 +455,11 @@ void vpu_clock_gating_on(void *clkh) if (clk_ctr->mode != VPU_POWER_DYNAMIC) return; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; cl = clk_ctr->clock[i]; if (cl->status) { clk_disable(cl->clk); cl->status = 0; Loading @@ -477,12 +483,16 @@ void vpu_clock_mode_set(void *clkh, enum vpu_power_mode mode) if (mode <= VPU_POWER_DYNAMIC) { clk_ctr->mode = mode; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { struct vpu_core_clock *cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { struct vpu_clock *cl = clk_ctr->clock[i]; unsigned long freq; if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; if (mode < VPU_POWER_DYNAMIC) freq = clock_freqs[i][mode]; freq = cl->pwr_frequencies[mode]; else freq = cl->current_freq; Loading drivers/media/platform/msm/vpu/vpu_bus_clock.h +3 −11 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -26,21 +26,13 @@ int vpu_bus_scale(u32 load); void *vpu_clock_init(struct vpu_platform_resources *res); void vpu_clock_deinit(void *clk_handle); int vpu_clock_enable(void *clk_handle); void vpu_clock_disable(void *clk_handle); int vpu_clock_enable(void *clk_handle, u32 clk_group); void vpu_clock_disable(void *clk_handle, u32 clk_group); int vpu_clock_scale(void *clk_handle, u32 load); int vpu_clock_gating_off(void *clkh); void vpu_clock_gating_on(void *clkh); enum vpu_power_mode { VPU_POWER_SVS, VPU_POWER_NOMINAL, VPU_POWER_TURBO, VPU_POWER_DYNAMIC, VPU_POWER_MAX = VPU_POWER_DYNAMIC }; void vpu_clock_mode_set(void *clkh, enum vpu_power_mode mode); enum vpu_power_mode vpu_clock_mode_get(void *clkh); #endif /* __H_VPU_BUS_CLOCK_H__ */ drivers/media/platform/msm/vpu/vpu_channel.c +4 −4 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -1947,7 +1947,7 @@ static int vpu_hw_power_on(struct vpu_channel_hal *hal) } /* enable the VPU clocks */ rc = vpu_clock_enable(hal->clk_handle); rc = vpu_clock_enable(hal->clk_handle, CLOCK_BOOT); if (unlikely(rc)) { pr_err("failed to enable clock\n"); goto err_clock; Loading @@ -1970,7 +1970,7 @@ err_power: */ static void vpu_hw_power_off(struct vpu_channel_hal *hal) { vpu_clock_disable(hal->clk_handle); vpu_clock_disable(hal->clk_handle, CLOCK_ALL_GROUPS); vpu_bus_unvote(); if (hal->vdd_enabled) { regulator_disable(hal->vdd); Loading @@ -1993,7 +1993,7 @@ int vpu_hw_sys_suspend(void) /* TODO: Send suspend IPC command once it is defined */ /* make sure clock are on */ rc = vpu_clock_enable(ch_hal->clk_handle); rc = vpu_clock_enable(ch_hal->clk_handle, CLOCK_BOOT); if (rc) { pr_err("clock off when trying to suspend\n"); goto suspend_exit; Loading drivers/media/platform/msm/vpu/vpu_resources.c +165 −11 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -78,10 +78,110 @@ static struct vpu_iommu_map iommus_array[VPU_MAX_IOMMU_DOMAIN] = { }, }; static const char * const clk_table_dt_entries[] = { [VPU_BUS_CLK] = "qcom,bus-clk-load-freq-tbl", [VPU_MAPLE_CLK] = "qcom,maple-clk-load-freq-tbl", [VPU_VDP_CLK] = "qcom,vdp-clk-load-freq-tbl", struct vpu_clock_descr { char *name; u32 flag; /* enum vpu_clock_flag */ u32 pwr_frequencies[VPU_POWER_MAX]; char *load_freq_dt_entry; }; const struct vpu_clock_descr vpu_clock_set[VPU_MAX_CLKS] = { [VPU_BUS_CLK] = { .name = "vdp_bus_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = { 40000000, 80000000, 80000000}, .load_freq_dt_entry = "qcom,bus-clk-load-freq-tbl", }, [VPU_MAPLE_CLK] = { .name = "core_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = {200000000, 400000000, 400000000}, .load_freq_dt_entry = "qcom,maple-clk-load-freq-tbl", }, [VPU_VDP_CLK] = { .name = "vdp_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = {200000000, 200000000, 400000000}, .load_freq_dt_entry = "qcom,vdp-clk-load-freq-tbl", }, [VPU_VDP_XIN] = { .name = "vdp_xin_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = {200000000, 467000000, 467000000}, .load_freq_dt_entry = "qcom,vdp-xin-clk-load-freq-tbl", }, [VPU_AHB_CLK] = { .name = "iface_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_AXI_CLK] = { .name = "bus_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_SLEEP_CLK] = { .name = "sleep_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_CXO_CLK] = { .name = "cxo_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_MAPLE_AXI_CLK] = { .name = "maple_bus_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_PRNG_CLK] = { .name = "prng_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_FRC_GPROC] { .name = "gproc_clk", .flag = CLOCK_FRC | CLOCK_BOOT, }, [VPU_FRC_KPROC] { .name = "kproc_clk", .flag = CLOCK_FRC | CLOCK_BOOT, }, [VPU_FRC_SDMC_FRCS] { .name = "sdmc_frcs_clk", .flag = CLOCK_FRC, }, [VPU_FRC_SDME_FRCF] { .name = "sdme_frcf_clk", .flag = CLOCK_FRC, }, [VPU_FRC_SDME_FRCS] { .name = "sdme_frcs_clk", .flag = CLOCK_FRC, }, [VPU_FRC_SDME_VPRO] { .name = "sdme_vproc_clk", .flag = CLOCK_FRC, }, [VPU_FRC_HDMC_FRCF] { .name = "hdmc_frcf_clk", .flag = CLOCK_FRC, }, [VPU_FRC_PREPROC] { .name = "preproc_clk", .flag = CLOCK_FRC, }, [VPU_FRC_FRC_XIN] { .name = "frc_xin_clk", .flag = CLOCK_FRC, }, [VPU_FRC_MAPLE_AXI] { .name = "maple_axi_clk", .flag = CLOCK_FRC, }, [VPU_QDSS_AT] { .name = "qdss_at_clk", .flag = CLOCK_QDSS, }, [VPU_QDSS_TSCTR_DIV8] { .name = "qdss_tsctr_div8_clk", .flag = CLOCK_QDSS, }, }; struct bus_pdata_config { Loading Loading @@ -125,7 +225,7 @@ static int __vpu_load_freq_table(struct vpu_platform_resources *res, clk_table->entry = devm_kzalloc(&pdev->dev, num_elements * sizeof(*clk_table->entry), GFP_KERNEL); if (!clk_table->entry) { pr_err("Failed alloc load_freq_tabll\n"); pr_err("Failed alloc load_freq_table\n"); return -ENOMEM; } Loading @@ -148,12 +248,17 @@ static int __vpu_load_freq_tables(struct vpu_platform_resources *res) { int ret = 0, i; for (i = 0; i < ARRAY_SIZE(clk_table_dt_entries); i++) { ret = __vpu_load_freq_table(res, clk_table_dt_entries[i], &res->clock_tables[i]); for (i = 0; i < VPU_MAX_CLKS; i++) { struct vpu_clock *cl = &res->clock[i]; if ((cl->flag & CLOCK_PRESENT) && (cl->flag & CLOCK_SCALABLE)) { ret = __vpu_load_freq_table(res, vpu_clock_set[i].load_freq_dt_entry, &cl->load_freq_tbl); if (ret) return ret; } } return ret; } Loading Loading @@ -259,6 +364,49 @@ static int __vpu_load_bus_vectors(struct vpu_platform_resources *res) return 0; } static int __vpu_load_clk_names(struct vpu_platform_resources *res) { int ret = 0, i, j, num_elements; struct platform_device *pdev = res->pdev; const char *name; num_elements = of_property_count_strings(pdev->dev.of_node, "qcom,clock-names"); if (num_elements <= 0) { pr_err("No valid clock list in device tree.\n"); return -EINVAL; } else if (num_elements > VPU_MAX_CLKS) { pr_err("List of clocks to enable is too large\n"); return -EINVAL; } for (i = 0; i < num_elements; i++) { bool found = false; ret = of_property_read_string_index(pdev->dev.of_node, "qcom,clock-names", i, &name); if (ret) return ret; for (j = 0; j < VPU_MAX_CLKS; j++) { if (strcmp(name, vpu_clock_set[j].name) == 0) { res->clock[j].name = vpu_clock_set[j].name; res->clock[j].flag = vpu_clock_set[j].flag | CLOCK_PRESENT; res->clock[j].pwr_frequencies = vpu_clock_set[j].pwr_frequencies; found = true; break; } } if (!found) { pr_err("clock %s not found\n", name); return -EINVAL; } } return 0; } static int __vpu_load_iommu_maps(struct vpu_platform_resources *res) { int ret = 0, i, j, num_elements; Loading Loading @@ -351,6 +499,12 @@ int read_vpu_platform_resources(struct vpu_platform_resources *res, GFP_KERNEL)) return -ENOMEM; ret = __vpu_load_clk_names(res); if (ret) { pr_err("Failed to load clock names: %d\n", ret); goto err_read_dt_resources; } ret = __vpu_load_freq_tables(res); if (ret) { pr_err("Failed to load freq tables: %d\n", ret); Loading Loading
Documentation/devicetree/bindings/media/video/msm-vpu.txt +3 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ Required properties: - interrupt-names : Names corresponding to the defined interrupts list. - "vpu_wdog" : Watchdog interrupt - "vpu_hfi" : Firmware to Host interrupt - clock-names: Array of clocks that the driver requires for the device. The names here correspond to the clock names used in clk_get(<name>). - qcom,maple-clk-load-freq-tbl: Table of <load, freq> entries. An entry specifies a given VPU processing load (in bits per second), and a Loading @@ -41,6 +43,7 @@ Example: reg-names = "vpu_csr", "vpu_smem"; interrupts = <0 44 0>, <0 45 0>; interrupt-names = "vpu_wdog", "vpu_hfi"; clock-names = "core_clk", "bus_clock", "iface_clk"; qcom,maple-clk-load-freq-tbl = <100000 50000000>, <500000 400000000>; qcom,vdp-clk-load-freq-tbl = <200000 100000000>, Loading
drivers/media/platform/msm/vpu/vpu_bus_clock.c +91 −81 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -156,33 +156,15 @@ int vpu_bus_scale(u32 load) * vpu_vdp_clk between 200MHz and 400MHz during runtime to optimize for * power consumption */ #define VPU_CLK_GATE_LEVEL VPU_VDP_CLK static const char *clock_names[VPU_MAX_CLKS] = { [VPU_BUS_CLK] = "vdp_bus_clk", [VPU_MAPLE_CLK] = "core_clk", [VPU_VDP_CLK] = "vdp_clk", [VPU_AHB_CLK] = "iface_clk", [VPU_AXI_CLK] = "bus_clk", [VPU_SLEEP_CLK] = "sleep_clk", [VPU_CXO_CLK] = "cxo_clk", [VPU_MAPLE_AXI_CLK] = "maple_bus_clk", [VPU_PRNG_CLK] = "prng_clk", }; struct vpu_core_clock { struct clk *clk; u32 status; u32 current_freq; struct load_freq_table *load_freq_tbl; const char *name; }; #define NOT_USED(clk_ctrl, i) (!( \ ((clk_ctrl)->clock[i]->flag & CLOCK_PRESENT) && \ ((clk_ctrl)->clock[i]->flag & (clk_ctrl)->mask))) #define NOT_IN_GROUP(clk_ctrl, i, clk_group) (!( \ ((clk_ctrl)->clock[i]->flag & (clk_group)))) static const u32 clock_freqs[VPU_MAX_CLKS][VPU_POWER_MAX] = { [VPU_BUS_CLK] = { 40000000, 80000000, 80000000}, [VPU_MAPLE_CLK] = {200000000, 400000000, 400000000}, [VPU_VDP_CLK] = {200000000, 200000000, 400000000}, }; #define CLOCK_IS_SCALABLE(clk_ctrl, i) \ ((clk_ctrl)->clock[i]->flag & CLOCK_SCALABLE) /* * Note: there is no lock in this block Loading @@ -194,14 +176,15 @@ struct vpu_clk_control { /* svs, nominal, turbo, dynamic(default) */ u32 mode; struct vpu_core_clock clock[VPU_MAX_CLKS]; struct vpu_clock *clock[VPU_MAX_CLKS]; u32 mask; }; void *vpu_clock_init(struct vpu_platform_resources *res) { int i; int rc = -1; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctrl; if (!res) Loading @@ -214,14 +197,21 @@ void *vpu_clock_init(struct vpu_platform_resources *res) return NULL; } for (i = 0; i < VPU_MAX_CLKS; i++) clk_ctrl->clock[i] = &res->clock[i]; /* mask allowing to only enable clocks from certain groups of clocks */ clk_ctrl->mask = CLOCK_CORE; /* setup the clock handles */ for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctrl->clock[i]; if (NOT_USED(clk_ctrl, i)) continue; cl->load_freq_tbl = &res->clock_tables[i]; cl->name = clock_names[i]; cl = clk_ctrl->clock[i]; if (i <= VPU_CLK_GATE_LEVEL && cl->load_freq_tbl->count == 0) { if (CLOCK_IS_SCALABLE(clk_ctrl, i) && !cl->load_freq_tbl.count) { pr_err("%s freq table size is 0\n", cl->name); goto fail_init_clocks; } Loading @@ -229,27 +219,16 @@ void *vpu_clock_init(struct vpu_platform_resources *res) cl->clk = devm_clk_get(&res->pdev->dev, cl->name); if (IS_ERR_OR_NULL(cl->clk)) { pr_err("Failed to get clock: %s\n", cl->name); rc = PTR_ERR(cl->clk); break; } cl->status = 0; } /* free the clock if not all successful */ if (i < VPU_MAX_CLKS) { for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctrl->clock[i]; if (cl->clk) { clk_put(cl->clk); cl->clk = NULL; } } goto fail_init_clocks; } cl->status = 0; } return clk_ctrl; fail_init_clocks: vpu_clock_deinit((void *)clk_ctrl); kfree(clk_ctrl); return NULL; } Loading @@ -257,7 +236,7 @@ fail_init_clocks: void vpu_clock_deinit(void *clkh) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; if (!clk_ctr) { Loading @@ -266,21 +245,32 @@ void vpu_clock_deinit(void *clkh) } for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (NOT_USED(clk_ctr, i)) continue; cl = clk_ctr->clock[i]; if (cl->status) { clk_disable_unprepare(cl->clk); cl->status = 0; } if (cl->clk) { clk_put(cl->clk); cl->clk = NULL; } } kfree(clk_ctr); } int vpu_clock_enable(void *clkh) /* * vpu_clock_enable() - enable a group of clocks * * @clkh: clock handler * @clk_group: see vpu_clock_flag (group section) * */ int vpu_clock_enable(void *clkh, u32 clk_group) { struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; int i = 0; int rc = 0; Loading @@ -293,14 +283,18 @@ int vpu_clock_enable(void *clkh) clk_ctr->mode = VPU_POWER_DYNAMIC; for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (NOT_USED(clk_ctr, i) || NOT_IN_GROUP(clk_ctr, i, clk_group)) continue; cl = clk_ctr->clock[i]; if (cl->status == 0) { /* set rate if it's a gated clock */ if (i <= VPU_CLK_GATE_LEVEL && cl->load_freq_tbl->entry) { if (CLOCK_IS_SCALABLE(clk_ctr, i) && cl->load_freq_tbl.entry) { cl->current_freq = cl->load_freq_tbl->entry[0].freq; cl->load_freq_tbl.entry[0].freq; rc = clk_set_rate(cl->clk, cl->current_freq); if (rc) { Loading @@ -325,21 +319,21 @@ int vpu_clock_enable(void *clkh) return rc; fail_clk_enable: for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (cl->status) { clk_disable_unprepare(cl->clk); cl->status = 0; } } vpu_clock_disable(clkh, clk_group); return rc; } void vpu_clock_disable(void *clkh) /* * vpu_clock_disable() - disable a group of clocks * * @clkh: clock handler * @clk_group: see vpu_clock_flag (group section) * */ void vpu_clock_disable(void *clkh, u32 clk_group) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; if (!clk_ctr) { Loading @@ -348,7 +342,10 @@ void vpu_clock_disable(void *clkh) } for (i = 0; i < VPU_MAX_CLKS; i++) { cl = &clk_ctr->clock[i]; if (NOT_USED(clk_ctr, i) || NOT_IN_GROUP(clk_ctr, i, clk_group)) continue; cl = clk_ctr->clock[i]; if (cl->status) { clk_disable_unprepare(cl->clk); cl->status = 0; Loading @@ -356,10 +353,10 @@ void vpu_clock_disable(void *clkh) } } static unsigned long __clock_get_rate(struct vpu_core_clock *clock, static unsigned long __clock_get_rate(struct vpu_clock *clock, u32 num_bits_per_sec) { struct load_freq_table *table = clock->load_freq_tbl; struct load_freq_table *table = &clock->load_freq_tbl; unsigned long ret = 0; int i; Loading @@ -385,10 +382,13 @@ int vpu_clock_scale(void *clkh, u32 load) clk_ctr->load = load; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { struct vpu_core_clock *cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { struct vpu_clock *cl = clk_ctr->clock[i]; unsigned long freq; if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; freq = __clock_get_rate(cl, load); if (clk_ctr->mode == VPU_POWER_DYNAMIC) { rc = clk_set_rate(cl->clk, freq); Loading @@ -407,7 +407,7 @@ int vpu_clock_scale(void *clkh, u32 load) int vpu_clock_gating_off(void *clkh) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; int rc = 0; Loading @@ -420,8 +420,11 @@ int vpu_clock_gating_off(void *clkh) if (clk_ctr->mode != VPU_POWER_DYNAMIC) return 0; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; cl = clk_ctr->clock[i]; if (cl->status == 0) { rc = clk_enable(cl->clk); if (rc) { Loading @@ -440,7 +443,7 @@ int vpu_clock_gating_off(void *clkh) void vpu_clock_gating_on(void *clkh) { int i; struct vpu_core_clock *cl; struct vpu_clock *cl; struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh; if (!clk_ctr) { Loading @@ -452,8 +455,11 @@ void vpu_clock_gating_on(void *clkh) if (clk_ctr->mode != VPU_POWER_DYNAMIC) return; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; cl = clk_ctr->clock[i]; if (cl->status) { clk_disable(cl->clk); cl->status = 0; Loading @@ -477,12 +483,16 @@ void vpu_clock_mode_set(void *clkh, enum vpu_power_mode mode) if (mode <= VPU_POWER_DYNAMIC) { clk_ctr->mode = mode; for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) { struct vpu_core_clock *cl = &clk_ctr->clock[i]; for (i = 0; i < VPU_MAX_CLKS; i++) { struct vpu_clock *cl = clk_ctr->clock[i]; unsigned long freq; if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i)) continue; if (mode < VPU_POWER_DYNAMIC) freq = clock_freqs[i][mode]; freq = cl->pwr_frequencies[mode]; else freq = cl->current_freq; Loading
drivers/media/platform/msm/vpu/vpu_bus_clock.h +3 −11 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -26,21 +26,13 @@ int vpu_bus_scale(u32 load); void *vpu_clock_init(struct vpu_platform_resources *res); void vpu_clock_deinit(void *clk_handle); int vpu_clock_enable(void *clk_handle); void vpu_clock_disable(void *clk_handle); int vpu_clock_enable(void *clk_handle, u32 clk_group); void vpu_clock_disable(void *clk_handle, u32 clk_group); int vpu_clock_scale(void *clk_handle, u32 load); int vpu_clock_gating_off(void *clkh); void vpu_clock_gating_on(void *clkh); enum vpu_power_mode { VPU_POWER_SVS, VPU_POWER_NOMINAL, VPU_POWER_TURBO, VPU_POWER_DYNAMIC, VPU_POWER_MAX = VPU_POWER_DYNAMIC }; void vpu_clock_mode_set(void *clkh, enum vpu_power_mode mode); enum vpu_power_mode vpu_clock_mode_get(void *clkh); #endif /* __H_VPU_BUS_CLOCK_H__ */
drivers/media/platform/msm/vpu/vpu_channel.c +4 −4 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -1947,7 +1947,7 @@ static int vpu_hw_power_on(struct vpu_channel_hal *hal) } /* enable the VPU clocks */ rc = vpu_clock_enable(hal->clk_handle); rc = vpu_clock_enable(hal->clk_handle, CLOCK_BOOT); if (unlikely(rc)) { pr_err("failed to enable clock\n"); goto err_clock; Loading @@ -1970,7 +1970,7 @@ err_power: */ static void vpu_hw_power_off(struct vpu_channel_hal *hal) { vpu_clock_disable(hal->clk_handle); vpu_clock_disable(hal->clk_handle, CLOCK_ALL_GROUPS); vpu_bus_unvote(); if (hal->vdd_enabled) { regulator_disable(hal->vdd); Loading @@ -1993,7 +1993,7 @@ int vpu_hw_sys_suspend(void) /* TODO: Send suspend IPC command once it is defined */ /* make sure clock are on */ rc = vpu_clock_enable(ch_hal->clk_handle); rc = vpu_clock_enable(ch_hal->clk_handle, CLOCK_BOOT); if (rc) { pr_err("clock off when trying to suspend\n"); goto suspend_exit; Loading
drivers/media/platform/msm/vpu/vpu_resources.c +165 −11 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, 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 @@ -78,10 +78,110 @@ static struct vpu_iommu_map iommus_array[VPU_MAX_IOMMU_DOMAIN] = { }, }; static const char * const clk_table_dt_entries[] = { [VPU_BUS_CLK] = "qcom,bus-clk-load-freq-tbl", [VPU_MAPLE_CLK] = "qcom,maple-clk-load-freq-tbl", [VPU_VDP_CLK] = "qcom,vdp-clk-load-freq-tbl", struct vpu_clock_descr { char *name; u32 flag; /* enum vpu_clock_flag */ u32 pwr_frequencies[VPU_POWER_MAX]; char *load_freq_dt_entry; }; const struct vpu_clock_descr vpu_clock_set[VPU_MAX_CLKS] = { [VPU_BUS_CLK] = { .name = "vdp_bus_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = { 40000000, 80000000, 80000000}, .load_freq_dt_entry = "qcom,bus-clk-load-freq-tbl", }, [VPU_MAPLE_CLK] = { .name = "core_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = {200000000, 400000000, 400000000}, .load_freq_dt_entry = "qcom,maple-clk-load-freq-tbl", }, [VPU_VDP_CLK] = { .name = "vdp_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = {200000000, 200000000, 400000000}, .load_freq_dt_entry = "qcom,vdp-clk-load-freq-tbl", }, [VPU_VDP_XIN] = { .name = "vdp_xin_clk", .flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE, .pwr_frequencies = {200000000, 467000000, 467000000}, .load_freq_dt_entry = "qcom,vdp-xin-clk-load-freq-tbl", }, [VPU_AHB_CLK] = { .name = "iface_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_AXI_CLK] = { .name = "bus_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_SLEEP_CLK] = { .name = "sleep_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_CXO_CLK] = { .name = "cxo_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_MAPLE_AXI_CLK] = { .name = "maple_bus_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_PRNG_CLK] = { .name = "prng_clk", .flag = CLOCK_CORE | CLOCK_BOOT, }, [VPU_FRC_GPROC] { .name = "gproc_clk", .flag = CLOCK_FRC | CLOCK_BOOT, }, [VPU_FRC_KPROC] { .name = "kproc_clk", .flag = CLOCK_FRC | CLOCK_BOOT, }, [VPU_FRC_SDMC_FRCS] { .name = "sdmc_frcs_clk", .flag = CLOCK_FRC, }, [VPU_FRC_SDME_FRCF] { .name = "sdme_frcf_clk", .flag = CLOCK_FRC, }, [VPU_FRC_SDME_FRCS] { .name = "sdme_frcs_clk", .flag = CLOCK_FRC, }, [VPU_FRC_SDME_VPRO] { .name = "sdme_vproc_clk", .flag = CLOCK_FRC, }, [VPU_FRC_HDMC_FRCF] { .name = "hdmc_frcf_clk", .flag = CLOCK_FRC, }, [VPU_FRC_PREPROC] { .name = "preproc_clk", .flag = CLOCK_FRC, }, [VPU_FRC_FRC_XIN] { .name = "frc_xin_clk", .flag = CLOCK_FRC, }, [VPU_FRC_MAPLE_AXI] { .name = "maple_axi_clk", .flag = CLOCK_FRC, }, [VPU_QDSS_AT] { .name = "qdss_at_clk", .flag = CLOCK_QDSS, }, [VPU_QDSS_TSCTR_DIV8] { .name = "qdss_tsctr_div8_clk", .flag = CLOCK_QDSS, }, }; struct bus_pdata_config { Loading Loading @@ -125,7 +225,7 @@ static int __vpu_load_freq_table(struct vpu_platform_resources *res, clk_table->entry = devm_kzalloc(&pdev->dev, num_elements * sizeof(*clk_table->entry), GFP_KERNEL); if (!clk_table->entry) { pr_err("Failed alloc load_freq_tabll\n"); pr_err("Failed alloc load_freq_table\n"); return -ENOMEM; } Loading @@ -148,12 +248,17 @@ static int __vpu_load_freq_tables(struct vpu_platform_resources *res) { int ret = 0, i; for (i = 0; i < ARRAY_SIZE(clk_table_dt_entries); i++) { ret = __vpu_load_freq_table(res, clk_table_dt_entries[i], &res->clock_tables[i]); for (i = 0; i < VPU_MAX_CLKS; i++) { struct vpu_clock *cl = &res->clock[i]; if ((cl->flag & CLOCK_PRESENT) && (cl->flag & CLOCK_SCALABLE)) { ret = __vpu_load_freq_table(res, vpu_clock_set[i].load_freq_dt_entry, &cl->load_freq_tbl); if (ret) return ret; } } return ret; } Loading Loading @@ -259,6 +364,49 @@ static int __vpu_load_bus_vectors(struct vpu_platform_resources *res) return 0; } static int __vpu_load_clk_names(struct vpu_platform_resources *res) { int ret = 0, i, j, num_elements; struct platform_device *pdev = res->pdev; const char *name; num_elements = of_property_count_strings(pdev->dev.of_node, "qcom,clock-names"); if (num_elements <= 0) { pr_err("No valid clock list in device tree.\n"); return -EINVAL; } else if (num_elements > VPU_MAX_CLKS) { pr_err("List of clocks to enable is too large\n"); return -EINVAL; } for (i = 0; i < num_elements; i++) { bool found = false; ret = of_property_read_string_index(pdev->dev.of_node, "qcom,clock-names", i, &name); if (ret) return ret; for (j = 0; j < VPU_MAX_CLKS; j++) { if (strcmp(name, vpu_clock_set[j].name) == 0) { res->clock[j].name = vpu_clock_set[j].name; res->clock[j].flag = vpu_clock_set[j].flag | CLOCK_PRESENT; res->clock[j].pwr_frequencies = vpu_clock_set[j].pwr_frequencies; found = true; break; } } if (!found) { pr_err("clock %s not found\n", name); return -EINVAL; } } return 0; } static int __vpu_load_iommu_maps(struct vpu_platform_resources *res) { int ret = 0, i, j, num_elements; Loading Loading @@ -351,6 +499,12 @@ int read_vpu_platform_resources(struct vpu_platform_resources *res, GFP_KERNEL)) return -ENOMEM; ret = __vpu_load_clk_names(res); if (ret) { pr_err("Failed to load clock names: %d\n", ret); goto err_read_dt_resources; } ret = __vpu_load_freq_tables(res); if (ret) { pr_err("Failed to load freq tables: %d\n", ret); Loading