diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.c b/drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.c index 118073b5c1..9d62e68143 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.c +++ b/drivers/cam_isp/isp_hw_mgr/isp_hw/ppi_hw/cam_csid_ppi_core.c @@ -76,8 +76,7 @@ static int cam_csid_ppi_enable_hw(struct cam_csid_ppi_hw *ppi_hw) } for (i = 0; i < soc_info->num_clk; i++) { - rc = cam_soc_util_clk_enable(soc_info->clk[i], - soc_info->clk_name[i], 0, NULL); + rc = cam_soc_util_clk_enable(soc_info, false, i, -1, NULL); if (rc) goto clk_disable; } @@ -107,8 +106,7 @@ static int cam_csid_ppi_enable_hw(struct cam_csid_ppi_hw *ppi_hw) return 0; clk_disable: for (--i; i >= 0; i--) - cam_soc_util_clk_disable(soc_info->clk[i], - soc_info->clk_name[i]); + cam_soc_util_clk_disable(soc_info, false, i); ppi_hw->hw_info->open_count--; return rc; } @@ -154,8 +152,7 @@ static int cam_csid_ppi_disable_hw(struct cam_csid_ppi_hw *ppi_hw) ppi_hw->device_enabled = 0; for (i = 0; i < soc_info->num_clk; i++) - cam_soc_util_clk_disable(soc_info->clk[i], - soc_info->clk_name[i]); + cam_soc_util_clk_disable(soc_info, false, i); return rc; } diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.c b/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.c index 3976080728..1decd9af46 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.c +++ b/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.c @@ -64,9 +64,8 @@ int cam_tfe_init_soc_resources(struct cam_hw_soc_info *soc_info, clk_option: - rc = cam_soc_util_get_option_clk_by_name(soc_info, - CAM_TFE_DSP_CLK_NAME, &soc_private->dsp_clk, - &soc_private->dsp_clk_index, &soc_private->dsp_clk_rate); + rc = cam_soc_util_get_option_clk_by_name(soc_info, CAM_TFE_DSP_CLK_NAME, + &soc_private->dsp_clk_index); if (rc) CAM_WARN(CAM_ISP, "Option clk get failed with rc %d", rc); @@ -129,10 +128,13 @@ int cam_tfe_deinit_soc_resources(struct cam_hw_soc_info *soc_info) "Error! Release platform resource failed rc=%d", rc); - rc = cam_soc_util_clk_put(&soc_private->dsp_clk); - if (rc < 0) - CAM_ERR(CAM_ISP, - "Error Put dsp clk failed rc=%d", rc); + if (soc_private->dsp_clk_index != -1) { + rc = cam_soc_util_put_optional_clk(soc_info, + soc_private->dsp_clk_index); + if (rc) + CAM_ERR(CAM_ISP, + "Error Put dsp clk failed rc=%d", rc); + } kfree(soc_private); @@ -198,8 +200,8 @@ int cam_tfe_soc_enable_clk(struct cam_hw_soc_info *soc_info, soc_private = soc_info->soc_private; if (strcmp(clk_name, CAM_TFE_DSP_CLK_NAME) == 0) { - rc = cam_soc_util_clk_enable(soc_private->dsp_clk, - CAM_TFE_DSP_CLK_NAME, soc_private->dsp_clk_rate, NULL); + rc = cam_soc_util_clk_enable(soc_info, true, + soc_private->dsp_clk_index, 0, NULL); if (rc) CAM_ERR(CAM_ISP, "Error enable dsp clk failed rc=%d", rc); @@ -222,8 +224,8 @@ int cam_tfe_soc_disable_clk(struct cam_hw_soc_info *soc_info, soc_private = soc_info->soc_private; if (strcmp(clk_name, CAM_TFE_DSP_CLK_NAME) == 0) { - rc = cam_soc_util_clk_disable(soc_private->dsp_clk, - CAM_TFE_DSP_CLK_NAME); + rc = cam_soc_util_clk_disable(soc_info, true, + soc_private->dsp_clk_index); if (rc) CAM_ERR(CAM_ISP, "Error enable dsp clk failed rc=%d", rc); diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.h b/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.h index 5cb137142b..fd5f2ad2b1 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.h +++ b/drivers/cam_isp/isp_hw_mgr/isp_hw/tfe_hw/cam_tfe_soc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #ifndef _CAM_TFE_SOC_H_ @@ -26,15 +26,14 @@ enum cam_cpas_handle_id { * This handle is used for all further interface * with CPAS. * @cpas_version: Has cpas version read from Hardware + * @dsp_clk_index: DSP clk index in optional clocks * @num_pid: number of pids of tfe * @pid: TFE pid value list */ struct cam_tfe_soc_private { uint32_t cpas_handle; uint32_t cpas_version; - struct clk *dsp_clk; int32_t dsp_clk_index; - int32_t dsp_clk_rate; uint32_t num_pid; uint32_t pid[CAM_ISP_HW_MAX_PID_VAL]; }; diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c b/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c index 0138ace0b3..3c3e59050d 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c +++ b/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c @@ -158,9 +158,8 @@ int cam_vfe_init_soc_resources(struct cam_hw_soc_info *soc_info, goto free_soc_private; } - rc = cam_soc_util_get_option_clk_by_name(soc_info, - CAM_VFE_DSP_CLK_NAME, &soc_private->dsp_clk, - &soc_private->dsp_clk_index, &soc_private->dsp_clk_rate); + rc = cam_soc_util_get_option_clk_by_name(soc_info, CAM_VFE_DSP_CLK_NAME, + &soc_private->dsp_clk_index); if (rc) CAM_DBG(CAM_ISP, "Option clk get failed with rc %d", rc); @@ -222,8 +221,9 @@ int cam_vfe_deinit_soc_resources(struct cam_hw_soc_info *soc_info) "Error! Release platform resources failed rc=%d", rc); if (soc_private->dsp_clk_index != -1) { - rc = cam_soc_util_clk_put(&soc_private->dsp_clk); - if (rc < 0) + rc = cam_soc_util_put_optional_clk(soc_info, + soc_private->dsp_clk_index); + if (rc) CAM_ERR(CAM_ISP, "Error Put dsp clk failed rc=%d", rc); } @@ -306,8 +306,8 @@ int cam_vfe_soc_enable_clk(struct cam_hw_soc_info *soc_info, goto end; } - rc = cam_soc_util_clk_enable(soc_private->dsp_clk, - CAM_VFE_DSP_CLK_NAME, soc_private->dsp_clk_rate, NULL); + rc = cam_soc_util_clk_enable(soc_info, true, + soc_private->dsp_clk_index, 0, NULL); if (rc) CAM_ERR(CAM_ISP, "Error enable dsp clk failed rc=%d", rc); @@ -338,8 +338,8 @@ int cam_vfe_soc_disable_clk(struct cam_hw_soc_info *soc_info, goto end; } - rc = cam_soc_util_clk_disable(soc_private->dsp_clk, - CAM_VFE_DSP_CLK_NAME); + rc = cam_soc_util_clk_disable(soc_info, true, + soc_private->dsp_clk_index); if (rc) CAM_ERR(CAM_ISP, "Error disable dsp clk failed rc=%d", rc); diff --git a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h b/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h index cb76b84790..820cd684ad 100644 --- a/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h +++ b/drivers/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ #ifndef _CAM_VFE_SOC_H_ @@ -22,6 +22,7 @@ * This handle is used for all further interface * with CPAS. * @cpas_version: Has cpas version read from Hardware + * @dsp_clk_index: DSP clk index in optional clocks * @ubwc_static_ctrl: UBWC static control configuration * @is_ife_lite: Flag to indicate full vs lite IFE * @ife_clk_src: IFE source clock @@ -31,9 +32,7 @@ struct cam_vfe_soc_private { uint32_t cpas_handle; uint32_t cpas_version; - struct clk *dsp_clk; int32_t dsp_clk_index; - int32_t dsp_clk_rate; uint32_t ubwc_static_ctrl[UBWC_STATIC_CONFIG_MAX]; bool is_ife_lite; uint64_t ife_clk_src; diff --git a/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c b/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c index d4538721ae..9c41de4e97 100644 --- a/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c +++ b/drivers/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c @@ -2096,12 +2096,13 @@ int cam_sensor_core_power_up(struct cam_sensor_power_ctrl_t *ctrl, power_setting->config_val; for (j = 0; j < soc_info->num_clk; j++) { - rc = cam_soc_util_clk_enable(soc_info->clk[j], - soc_info->clk_name[j], - soc_info->clk_rate[0][j], - NULL); - if (rc) + rc = cam_soc_util_clk_enable(soc_info, false, + i, 0, NULL); + if (rc) { + CAM_ERR(CAM_UTIL, + "Failed in clk enable %d", i); break; + } } if (rc < 0) { @@ -2224,8 +2225,7 @@ power_up_failed: switch (power_setting->seq_type) { case SENSOR_MCLK: for (i = soc_info->num_clk - 1; i >= 0; i--) { - cam_soc_util_clk_disable(soc_info->clk[i], - soc_info->clk_name[i]); + cam_soc_util_clk_disable(soc_info, false, i); } ret = cam_config_mclk_reg(ctrl, soc_info, index); if (ret < 0) { @@ -2384,8 +2384,7 @@ int cam_sensor_util_power_down(struct cam_sensor_power_ctrl_t *ctrl, switch (pd->seq_type) { case SENSOR_MCLK: for (i = soc_info->num_clk - 1; i >= 0; i--) { - cam_soc_util_clk_disable(soc_info->clk[i], - soc_info->clk_name[i]); + cam_soc_util_clk_disable(soc_info, false, i); } ret = cam_config_mclk_reg(ctrl, soc_info, index); diff --git a/drivers/cam_utils/cam_soc_util.c b/drivers/cam_utils/cam_soc_util.c index 6e14e7e87c..69e648decd 100644 --- a/drivers/cam_utils/cam_soc_util.c +++ b/drivers/cam_utils/cam_soc_util.c @@ -13,8 +13,274 @@ #include "cam_cx_ipeak.h" #include "cam_mem_mgr.h" +#define CAM_TO_MASK(bitn) (1 << (int)(bitn)) +#define CAM_IS_BIT_SET(mask, bit) ((mask) & CAM_TO_MASK(bit)) +#define CAM_SET_BIT(mask, bit) ((mask) |= CAM_TO_MASK(bit)) +#define CAM_CLEAR_BIT(mask, bit) ((mask) &= ~CAM_TO_MASK(bit)) + +/** + * struct cam_clk_wrapper_clk: This represents an entry corresponding to a + * shared clock in Clk wrapper. Clients that share + * the same clock are registered to this clk entry + * and set rate from them is consolidated before + * setting it to clk driver. + * + * @list: List pointer to point to next shared clk entry + * @clk_id: Clk Id of this clock + * @curr_clk_rate: Current clock rate set for this clock + * @client_list: List of clients registered to this shared clock entry + * @num_clients: Number of clients + **/ +struct cam_clk_wrapper_clk { + struct list_head list; + uint32_t clk_id; + int64_t curr_clk_rate; + struct list_head client_list; + uint32_t num_clients; +}; + +/** + * struct cam_clk_wrapper_client: This represents a client (device) that wants + * to share the clock with some other client. + * + * @list: List pointer to point to next client that share the + * same clock + * @soc_info: soc_info of client. This is used as unique identifier + * for a client + * @clk: Clk handle + * @curr_clk_rate: Current clock rate set for this client + **/ +struct cam_clk_wrapper_client { + struct list_head list; + struct cam_hw_soc_info *soc_info; + struct clk *clk; + int64_t curr_clk_rate; +}; + static char supported_clk_info[256]; +static DEFINE_MUTEX(wrapper_lock); +static LIST_HEAD(wrapper_clk_list); + +static int cam_soc_util_clk_wrapper_register_entry( + uint32_t clk_id, struct clk *clk, struct cam_hw_soc_info *soc_info, + const char *clk_name) +{ + struct cam_clk_wrapper_clk *wrapper_clk; + struct cam_clk_wrapper_client *wrapper_client; + bool clock_found = false; + int rc = 0; + + mutex_lock(&wrapper_lock); + + list_for_each_entry(wrapper_clk, &wrapper_clk_list, list) { + CAM_DBG(CAM_UTIL, "Clk list id %d num clients %d", + wrapper_clk->clk_id, wrapper_clk->num_clients); + + if (wrapper_clk->clk_id == clk_id) { + clock_found = true; + list_for_each_entry(wrapper_client, + &wrapper_clk->client_list, list) { + CAM_DBG(CAM_UTIL, + "Clk id %d entry client %s", + wrapper_clk->clk_id, + wrapper_client->soc_info->dev_name); + if (wrapper_client->soc_info == soc_info) { + CAM_ERR(CAM_UTIL, + "Register with same soc info, clk id %d, client %s", + clk_id, soc_info->dev_name); + rc = -EINVAL; + goto end; + } + } + break; + } + } + + if (!clock_found) { + CAM_DBG(CAM_UTIL, "Adding new entry for clk id %d", clk_id); + wrapper_clk = kzalloc(sizeof(struct cam_clk_wrapper_clk), + GFP_KERNEL); + if (!wrapper_clk) { + CAM_ERR(CAM_UTIL, + "Failed in allocating new clk entry %d", + clk_id); + rc = -ENOMEM; + goto end; + } + + wrapper_clk->clk_id = clk_id; + INIT_LIST_HEAD(&wrapper_clk->list); + INIT_LIST_HEAD(&wrapper_clk->client_list); + list_add_tail(&wrapper_clk->list, &wrapper_clk_list); + } + wrapper_client = kzalloc(sizeof(struct cam_clk_wrapper_client), + GFP_KERNEL); + if (!wrapper_client) { + CAM_ERR(CAM_UTIL, "Failed in allocating new client entry %d", + clk_id); + rc = -ENOMEM; + goto end; + } + + wrapper_client->soc_info = soc_info; + wrapper_client->clk = clk; + + INIT_LIST_HEAD(&wrapper_client->list); + list_add_tail(&wrapper_client->list, &wrapper_clk->client_list); + wrapper_clk->num_clients++; + + CAM_DBG(CAM_UTIL, + "Adding new client %s for clk[%s] id %d, num clients %d", + soc_info->dev_name, clk_name, clk_id, wrapper_clk->num_clients); +end: + mutex_unlock(&wrapper_lock); + return rc; +} + +static int cam_soc_util_clk_wrapper_unregister_entry( + uint32_t clk_id, struct cam_hw_soc_info *soc_info) +{ + struct cam_clk_wrapper_clk *wrapper_clk; + struct cam_clk_wrapper_client *wrapper_client; + bool clock_found = false; + bool client_found = false; + int rc = 0; + + mutex_lock(&wrapper_lock); + + list_for_each_entry(wrapper_clk, &wrapper_clk_list, list) { + CAM_DBG(CAM_UTIL, "Clk list id %d num clients %d", + wrapper_clk->clk_id, wrapper_clk->num_clients); + + if (wrapper_clk->clk_id == clk_id) { + clock_found = true; + list_for_each_entry(wrapper_client, + &wrapper_clk->client_list, list) { + CAM_DBG(CAM_UTIL, "Clk id %d entry client %s", + wrapper_clk->clk_id, + wrapper_client->soc_info->dev_name); + if (wrapper_client->soc_info == soc_info) { + client_found = true; + wrapper_clk->num_clients--; + list_del_init(&wrapper_client->list); + kfree(wrapper_client); + break; + } + } + break; + } + } + + if (!clock_found) { + CAM_ERR(CAM_UTIL, "Shared clk id %d entry not found", clk_id); + rc = -EINVAL; + goto end; + } + + if (!client_found) { + CAM_ERR(CAM_UTIL, + "Client %pK for Shared clk id %d entry not found", + soc_info, clk_id); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_UTIL, "Unregister client %s for clk id %d, num clients %d", + soc_info->dev_name, clk_id, wrapper_clk->num_clients); + + if (!wrapper_clk->num_clients) { + list_del_init(&wrapper_clk->list); + kfree(wrapper_clk); + } +end: + mutex_unlock(&wrapper_lock); + return rc; +} + +static int cam_soc_util_clk_wrapper_set_clk_rate( + uint32_t clk_id, struct cam_hw_soc_info *soc_info, + struct clk *clk, int64_t clk_rate) +{ + struct cam_clk_wrapper_clk *wrapper_clk; + struct cam_clk_wrapper_client *wrapper_client; + bool clk_found = false; + bool client_found = false; + int rc = 0; + int64_t final_clk_rate = 0; + + if (!soc_info || !clk) { + CAM_ERR(CAM_UTIL, "Invalid param soc_info %pK clk %pK", + soc_info, clk); + return -EINVAL; + } + + mutex_lock(&wrapper_lock); + + list_for_each_entry(wrapper_clk, &wrapper_clk_list, list) { + CAM_DBG(CAM_UTIL, "Clk list id %d num clients %d", + wrapper_clk->clk_id, wrapper_clk->num_clients); + if (wrapper_clk->clk_id == clk_id) { + clk_found = true; + break; + } + } + + if (!clk_found) { + CAM_ERR(CAM_UTIL, "Clk entry not found id %d client %s", + clk_id, soc_info->dev_name); + rc = -EINVAL; + goto end; + } + + list_for_each_entry(wrapper_client, &wrapper_clk->client_list, list) { + CAM_DBG(CAM_UTIL, "Clk id %d client %s, clk rate %lld", + wrapper_clk->clk_id, wrapper_client->soc_info->dev_name, + wrapper_client->curr_clk_rate); + if (wrapper_client->soc_info == soc_info) { + client_found = true; + CAM_DBG(CAM_UTIL, + "Clk enable clk id %d, client %s curr %ld new %ld", + clk_id, wrapper_client->soc_info->dev_name, + wrapper_client->curr_clk_rate, clk_rate); + + wrapper_client->curr_clk_rate = clk_rate; + } + + if (final_clk_rate < wrapper_client->curr_clk_rate) + final_clk_rate = wrapper_client->curr_clk_rate; + } + + if (!client_found) { + CAM_ERR(CAM_UTIL, + "Wrapper clk enable without client entry clk id %d client %s", + clk_id, soc_info->dev_name); + rc = -EINVAL; + goto end; + } + + CAM_DBG(CAM_UTIL, + "Clk id %d, client %s, clients rate %ld, curr %ld final %ld", + wrapper_clk->clk_id, soc_info->dev_name, clk_rate, + wrapper_clk->curr_clk_rate, final_clk_rate); + + if (final_clk_rate != wrapper_clk->curr_clk_rate) { + if (final_clk_rate) { + rc = clk_set_rate(clk, final_clk_rate); + if (rc) { + CAM_ERR(CAM_UTIL, "set_rate failed on clk %d", + wrapper_clk->clk_id); + goto end; + } + } + wrapper_clk->curr_clk_rate = final_clk_rate; + } + +end: + mutex_unlock(&wrapper_lock); + return rc; +} + int cam_soc_util_get_clk_level(struct cam_hw_soc_info *soc_info, int64_t clk_rate, int clk_idx, int32_t *clk_lvl) { @@ -366,18 +632,26 @@ long cam_soc_util_get_clk_round_rate(struct cam_hw_soc_info *soc_info, * @clk: Clock structure information for which rate is to be set * @clk_name: Name of the clock for which rate is being set * @clk_rate: Clock rate to be set + * @shared_clk: Whether this is a shared clk + * @clk_id: Clock ID * @applied_clk_rate: Final clock rate set to the clk * * @return: Success or failure */ -static int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name, - int64_t clk_rate, unsigned long *applied_clk_rate) +static int cam_soc_util_set_clk_rate(struct cam_hw_soc_info *soc_info, + struct clk *clk, const char *clk_name, + int64_t clk_rate, bool shared_clk, uint32_t clk_id, + unsigned long *applied_clk_rate) { int rc = 0; long clk_rate_round = -1; + bool set_rate = false; - if (!clk || !clk_name) + if (!clk || !clk_name) { + CAM_ERR(CAM_UTIL, "Invalid input clk %pK clk_name %pK", + clk, clk_name); return -EINVAL; + } CAM_DBG(CAM_UTIL, "set %s, rate %lld", clk_name, clk_rate); if (clk_rate > 0) { @@ -388,11 +662,7 @@ static int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name, clk_name, clk_rate_round); return clk_rate_round; } - rc = clk_set_rate(clk, clk_rate_round); - if (rc) { - CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name); - return rc; - } + set_rate = true; } else if (clk_rate == INIT_RATE) { clk_rate_round = clk_get_rate(clk); CAM_DBG(CAM_UTIL, "init new_rate %ld", clk_rate_round); @@ -404,10 +674,23 @@ static int cam_soc_util_set_clk_rate(struct clk *clk, const char *clk_name, return clk_rate_round; } } - rc = clk_set_rate(clk, clk_rate_round); - if (rc) { - CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name); - return rc; + set_rate = true; + } + + if (set_rate) { + if (shared_clk) { + CAM_DBG(CAM_UTIL, + "Dev %s clk %s id %d Set Shared clk %ld", + soc_info->dev_name, clk_name, clk_id, + clk_rate_round); + cam_soc_util_clk_wrapper_set_clk_rate( + clk_id, soc_info, clk, clk_rate_round); + } else { + rc = clk_set_rate(clk, clk_rate_round); + if (rc) { + CAM_ERR(CAM_UTIL, "set_rate failed on %s", clk_name); + return rc; + } } } @@ -461,8 +744,10 @@ int cam_soc_util_set_src_clk_rate(struct cam_hw_soc_info *soc_info, apply_level); } - rc = cam_soc_util_set_clk_rate(clk, + rc = cam_soc_util_set_clk_rate(soc_info, clk, soc_info->clk_name[src_clk_idx], clk_rate, + CAM_IS_BIT_SET(soc_info->shared_clk_mask, src_clk_idx), + soc_info->clk_id[src_clk_idx], &soc_info->applied_src_clk_rate); if (rc) { CAM_ERR(CAM_UTIL, @@ -481,9 +766,11 @@ int cam_soc_util_set_src_clk_rate(struct cam_hw_soc_info *soc_info, continue; } clk = soc_info->clk[scl_clk_idx]; - rc = cam_soc_util_set_clk_rate(clk, + rc = cam_soc_util_set_clk_rate(soc_info, clk, soc_info->clk_name[scl_clk_idx], soc_info->clk_rate[apply_level][scl_clk_idx], + CAM_IS_BIT_SET(soc_info->shared_clk_mask, scl_clk_idx), + soc_info->clk_id[scl_clk_idx], NULL); if (rc) { CAM_WARN(CAM_UTIL, @@ -497,21 +784,26 @@ int cam_soc_util_set_src_clk_rate(struct cam_hw_soc_info *soc_info, return 0; } -int cam_soc_util_clk_put(struct clk **clk) +int cam_soc_util_put_optional_clk(struct cam_hw_soc_info *soc_info, + int32_t clk_indx) { - if (!(*clk)) { - CAM_ERR(CAM_UTIL, "Invalid params clk"); + if (clk_indx < 0) { + CAM_ERR(CAM_UTIL, "Invalid params clk %d", clk_indx); return -EINVAL; } - clk_put(*clk); - *clk = NULL; + if (CAM_IS_BIT_SET(soc_info->optional_shared_clk_mask, clk_indx)) + cam_soc_util_clk_wrapper_unregister_entry( + soc_info->optional_clk_id[clk_indx], soc_info); + + clk_put(soc_info->optional_clk[clk_indx]); + soc_info->optional_clk[clk_indx] = NULL; return 0; } static struct clk *cam_soc_util_option_clk_get(struct device_node *np, - int index) + int index, uint32_t *clk_id) { struct of_phandle_args clkspec; struct clk *clk; @@ -526,23 +818,25 @@ static struct clk *cam_soc_util_option_clk_get(struct device_node *np, return ERR_PTR(rc); clk = of_clk_get_from_provider(&clkspec); + + *clk_id = clkspec.args[0]; of_node_put(clkspec.np); return clk; } int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info, - const char *clk_name, struct clk **clk, int32_t *clk_index, - int32_t *clk_rate) + const char *clk_name, int32_t *clk_index) { int index = 0; int rc = 0; struct device_node *of_node = NULL; + uint32_t shared_clk_val; - if (!soc_info || !clk_name || !clk) { + if (!soc_info || !clk_name || !clk_index) { CAM_ERR(CAM_UTIL, - "Invalid params soc_info %pK clk_name %s clk %pK", - soc_info, clk_name, clk); + "Invalid params soc_info %pK clk_name %s clk_index %pK", + soc_info, clk_name, clk_index); return -EINVAL; } @@ -553,52 +847,129 @@ int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info, if (index < 0) { CAM_DBG(CAM_UTIL, "No clk data for %s", clk_name); *clk_index = -1; - *clk = ERR_PTR(-EINVAL); return -EINVAL; } - *clk = cam_soc_util_option_clk_get(of_node, index); - if (IS_ERR(*clk)) { + if (index >= CAM_SOC_MAX_OPT_CLK) { + CAM_ERR(CAM_UTIL, "Insufficient optional clk entries %d %d", + index, CAM_SOC_MAX_OPT_CLK); + return -EINVAL; + } + + of_property_read_string_index(of_node, "clock-names-option", + index, &(soc_info->optional_clk_name[index])); + + soc_info->optional_clk[index] = cam_soc_util_option_clk_get(of_node, + index, &soc_info->optional_clk_id[index]); + if (IS_ERR(soc_info->optional_clk[index])) { CAM_ERR(CAM_UTIL, "No clk named %s found. Dev %s", clk_name, soc_info->dev_name); *clk_index = -1; - *clk = NULL; return -EFAULT; } *clk_index = index; rc = of_property_read_u32_index(of_node, "clock-rates-option", - index, clk_rate); + index, &soc_info->optional_clk_rate[index]); if (rc) { CAM_ERR(CAM_UTIL, "Error reading clock-rates clk_name %s index %d", clk_name, index); - cam_soc_util_clk_put(clk); - *clk_rate = 0; - return rc; + goto error; } /* * Option clocks are assumed to be available to single Device here. * Hence use INIT_RATE instead of NO_SET_RATE. */ - *clk_rate = (*clk_rate == 0) ? (int32_t)INIT_RATE : *clk_rate; + soc_info->optional_clk_rate[index] = + (soc_info->optional_clk_rate[index] == 0) ? + (int32_t)INIT_RATE : soc_info->optional_clk_rate[index]; CAM_DBG(CAM_UTIL, "clk_name %s index %d clk_rate %d", - clk_name, *clk_index, *clk_rate); + clk_name, *clk_index, soc_info->optional_clk_rate[index]); + + rc = of_property_read_u32_index(of_node, "shared-clks-option", + index, &shared_clk_val); + if (rc) { + CAM_DBG(CAM_UTIL, "Not shared clk %s index %d", + clk_name, index); + } else if (shared_clk_val > 1) { + CAM_WARN(CAM_UTIL, "Invalid shared clk val %d", shared_clk_val); + } else { + CAM_DBG(CAM_UTIL, + "Dev %s shared clk %s index %d, clk id %d, shared_clk_val %d", + soc_info->dev_name, clk_name, index, + soc_info->optional_clk_id[index], shared_clk_val); + + if (shared_clk_val) { + CAM_SET_BIT(soc_info->optional_shared_clk_mask, index); + + /* Create a wrapper entry if this is a shared clock */ + CAM_DBG(CAM_UTIL, + "Dev %s, clk %s, id %d register wrapper entry for shared clk", + soc_info->dev_name, + soc_info->optional_clk_name[index], + soc_info->optional_clk_id[index]); + + rc = cam_soc_util_clk_wrapper_register_entry( + soc_info->optional_clk_id[index], + soc_info->optional_clk[index], soc_info, + soc_info->optional_clk_name[index]); + if (rc) { + CAM_ERR(CAM_UTIL, + "Failed in registering shared clk Dev %s id %d", + soc_info->dev_name, + soc_info->optional_clk_id[index]); + goto error; + } + } + } return 0; +error: + clk_put(soc_info->optional_clk[index]); + soc_info->optional_clk_rate[index] = 0; + soc_info->optional_clk[index] = NULL; + *clk_index = -1; + + return rc; } -int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name, - int32_t clk_rate, unsigned long *applied_clock_rate) +int cam_soc_util_clk_enable(struct cam_hw_soc_info *soc_info, + bool optional_clk, int32_t clk_idx, int32_t apply_level, + unsigned long *applied_clock_rate) { int rc = 0; + struct clk *clk; + const char *clk_name; + int32_t clk_rate; + uint32_t shared_clk_mask; + uint32_t clk_id; - if (!clk || !clk_name) + if (!soc_info || (clk_idx < 0) || (apply_level >= CAM_MAX_VOTE)) { + CAM_ERR(CAM_UTIL, "Invalid param %d %d", clk_idx, apply_level); return -EINVAL; + } - rc = cam_soc_util_set_clk_rate(clk, clk_name, clk_rate, + if (optional_clk) { + clk = soc_info->optional_clk[clk_idx]; + clk_name = soc_info->optional_clk_name[clk_idx]; + clk_rate = (apply_level == -1) ? + 0 : soc_info->optional_clk_rate[clk_idx]; + shared_clk_mask = soc_info->optional_shared_clk_mask; + clk_id = soc_info->optional_clk_id[clk_idx]; + } else { + clk = soc_info->clk[clk_idx]; + clk_name = soc_info->clk_name[clk_idx]; + clk_rate = (apply_level == -1) ? + 0 : soc_info->clk_rate[apply_level][clk_idx]; + shared_clk_mask = soc_info->shared_clk_mask; + clk_id = soc_info->clk_id[clk_idx]; + } + + rc = cam_soc_util_set_clk_rate(soc_info, clk, clk_name, clk_rate, + CAM_IS_BIT_SET(shared_clk_mask, clk_idx), clk_id, applied_clock_rate); if (rc) return rc; @@ -612,14 +983,42 @@ int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name, return rc; } -int cam_soc_util_clk_disable(struct clk *clk, const char *clk_name) +int cam_soc_util_clk_disable(struct cam_hw_soc_info *soc_info, + bool optional_clk, int32_t clk_idx) { - if (!clk || !clk_name) + + struct clk *clk; + const char *clk_name; + uint32_t shared_clk_mask; + uint32_t clk_id; + + if (!soc_info || (clk_idx < 0)) { + CAM_ERR(CAM_UTIL, "Invalid param %d", clk_idx); return -EINVAL; + } + + if (optional_clk) { + clk = soc_info->optional_clk[clk_idx]; + clk_name = soc_info->optional_clk_name[clk_idx]; + shared_clk_mask = soc_info->optional_shared_clk_mask; + clk_id = soc_info->optional_clk_id[clk_idx]; + } else { + clk = soc_info->clk[clk_idx]; + clk_name = soc_info->clk_name[clk_idx]; + shared_clk_mask = soc_info->shared_clk_mask; + clk_id = soc_info->clk_id[clk_idx]; + } CAM_DBG(CAM_UTIL, "disable %s", clk_name); clk_disable_unprepare(clk); + if (CAM_IS_BIT_SET(shared_clk_mask, clk_idx)) { + CAM_DBG(CAM_UTIL, + "Dev %s clk %s Disabling Shared clk, set 0 rate", + soc_info->dev_name, clk_name); + cam_soc_util_clk_wrapper_set_clk_rate(clk_id, soc_info, clk, 0); + } + return 0; } @@ -657,9 +1056,7 @@ int cam_soc_util_clk_enable_default(struct cam_hw_soc_info *soc_info, cam_cx_ipeak_update_vote_cx_ipeak(soc_info, apply_level); for (i = 0; i < soc_info->num_clk; i++) { - rc = cam_soc_util_clk_enable(soc_info->clk[i], - soc_info->clk_name[i], - soc_info->clk_rate[apply_level][i], + rc = cam_soc_util_clk_enable(soc_info, false, i, apply_level, &applied_clk_rate); if (rc) goto clk_disable; @@ -683,8 +1080,7 @@ clk_disable: if (soc_info->cam_cx_ipeak_enable) cam_cx_ipeak_update_vote_cx_ipeak(soc_info, 0); for (i--; i >= 0; i--) { - cam_soc_util_clk_disable(soc_info->clk[i], - soc_info->clk_name[i]); + cam_soc_util_clk_disable(soc_info, false, i); } return rc; @@ -710,8 +1106,7 @@ void cam_soc_util_clk_disable_default(struct cam_hw_soc_info *soc_info) if (soc_info->cam_cx_ipeak_enable) cam_cx_ipeak_unvote_cx_ipeak(soc_info); for (i = soc_info->num_clk - 1; i >= 0; i--) - cam_soc_util_clk_disable(soc_info->clk[i], - soc_info->clk_name[i]); + cam_soc_util_clk_disable(soc_info, false, i); } /** @@ -736,6 +1131,8 @@ static int cam_soc_util_get_dt_clk_info(struct cam_hw_soc_info *soc_info) const char *clk_control_debugfs = NULL; const char *clk_cntl_lvl_string = NULL; enum cam_vote_level level; + int shared_clk_cnt; + struct of_phandle_args clk_args = {0}; if (!soc_info || !soc_info->dev) return -EINVAL; @@ -856,8 +1253,54 @@ static int cam_soc_util_get_dt_clk_info(struct cam_hw_soc_info *soc_info) soc_info->src_clk_idx = i; CAM_DBG(CAM_UTIL, "src clock = %s, index = %d", src_clk_str, i); - break; } + + rc = of_parse_phandle_with_args(of_node, "clocks", + "#clock-cells", i, &clk_args); + if (rc) { + CAM_ERR(CAM_CPAS, + "failed to clock info rc=%d", rc); + rc = -EINVAL; + goto end; + } + + soc_info->clk_id[i] = clk_args.args[0]; + of_node_put(clk_args.np); + + CAM_DBG(CAM_UTIL, "Dev %s clk %s id %d", + soc_info->dev_name, soc_info->clk_name[i], + soc_info->clk_id[i]); + } + + soc_info->shared_clk_mask = 0; + shared_clk_cnt = of_property_count_u32_elems(of_node, "shared-clks"); + if (shared_clk_cnt <= 0) { + CAM_DBG(CAM_UTIL, "Dev %s, no shared clks", soc_info->dev_name); + } else if (shared_clk_cnt != count) { + CAM_ERR(CAM_UTIL, "Dev %s, incorrect shared clock count %d %d", + soc_info->dev_name, shared_clk_cnt, count); + rc = -EINVAL; + goto end; + } else { + uint32_t shared_clk_val; + + for (i = 0; i < shared_clk_cnt; i++) { + rc = of_property_read_u32_index(of_node, + "shared-clks", i, &shared_clk_val); + if (rc || (shared_clk_val > 1)) { + CAM_ERR(CAM_UTIL, + "Incorrect shared clk info at %d, val=%d, count=%d", + i, shared_clk_val, shared_clk_cnt); + rc = -EINVAL; + goto end; + } + + if (shared_clk_val) + CAM_SET_BIT(soc_info->shared_clk_mask, i); + } + + CAM_DBG(CAM_UTIL, "Dev %s shared clk mask 0x%x", + soc_info->dev_name, soc_info->shared_clk_mask); } /* scalable clk info parsing */ @@ -952,9 +1395,11 @@ int cam_soc_util_set_clk_rate_level(struct cam_hw_soc_info *soc_info, soc_info->clk_name[i], soc_info->clk_rate[apply_level][i]); - rc = cam_soc_util_set_clk_rate(soc_info->clk[i], + rc = cam_soc_util_set_clk_rate(soc_info, soc_info->clk[i], soc_info->clk_name[i], soc_info->clk_rate[apply_level][i], + CAM_IS_BIT_SET(soc_info->shared_clk_mask, i), + soc_info->clk_id[i], &applied_clk_rate); if (rc < 0) { CAM_DBG(CAM_UTIL, @@ -1633,6 +2078,26 @@ int cam_soc_util_request_platform_resource( rc = -ENOENT; goto put_clk; } + + /* Create a wrapper entry if this is a shared clock */ + if (CAM_IS_BIT_SET(soc_info->shared_clk_mask, i)) { + CAM_DBG(CAM_UTIL, + "Dev %s, clk %s, id %d register wrapper entry for shared clk", + soc_info->dev_name, soc_info->clk_name[i], + soc_info->clk_id[i]); + rc = cam_soc_util_clk_wrapper_register_entry( + soc_info->clk_id[i], soc_info->clk[i], soc_info, + soc_info->clk_name[i]); + if (rc) { + CAM_ERR(CAM_UTIL, + "Failed in registering shared clk Dev %s id %d", + soc_info->dev_name, + soc_info->clk_id[i]); + clk_put(soc_info->clk[i]); + soc_info->clk[i] = NULL; + goto put_clk; + } + } } rc = cam_soc_util_request_pinctrl(soc_info); @@ -1655,6 +2120,10 @@ put_clk: i = soc_info->num_clk; for (i = i - 1; i >= 0; i--) { if (soc_info->clk[i]) { + if (CAM_IS_BIT_SET(soc_info->shared_clk_mask, i)) + cam_soc_util_clk_wrapper_unregister_entry( + soc_info->clk_id[i], soc_info); + clk_put(soc_info->clk[i]); soc_info->clk[i] = NULL; } @@ -1702,6 +2171,10 @@ int cam_soc_util_release_platform_resource(struct cam_hw_soc_info *soc_info) } for (i = soc_info->num_clk - 1; i >= 0; i--) { + if (CAM_IS_BIT_SET(soc_info->shared_clk_mask, i)) + cam_soc_util_clk_wrapper_unregister_entry( + soc_info->clk_id[i], soc_info); + clk_put(soc_info->clk[i]); soc_info->clk[i] = NULL; } diff --git a/drivers/cam_utils/cam_soc_util.h b/drivers/cam_utils/cam_soc_util.h index 837faa4a0c..4107b63f86 100644 --- a/drivers/cam_utils/cam_soc_util.h +++ b/drivers/cam_utils/cam_soc_util.h @@ -36,6 +36,9 @@ /* maximum number of device clock */ #define CAM_SOC_MAX_CLK 32 +/* maximum number of optional device clock */ +#define CAM_SOC_MAX_OPT_CLK 2 + /* DDR device types */ #define DDR_TYPE_LPDDR4 6 #define DDR_TYPE_LPDDR4X 7 @@ -155,12 +158,23 @@ struct cam_soc_gpio_data { * @clk: Array of associated clock resources * @clk_rate: 2D array of clock rates representing clock rate * values at different vote levels + * @clk_id Clock IDs + * @shared_clk_mask Mask indicating which of the clocks are shared with + * other devices. Set rate on these clocks needs to go + * through camera clk wrapper for aggregation. * @prev_clk_level Last vote level * @src_clk_idx: Source clock index that is rate-controllable + * @applied_src_clk_rate Current clock rate of the core source clk * @clk_level_valid: Indicates whether corresponding level is valid * @scl_clk_count: Number of scalable clocks present * @scl_clk_idx: Index of scalable clocks - * @applied_src_clk_rate Current clock rate of the core source clk + * @optional_clk_name: Array of clock names + * @optional_clk: Array of associated clock resources + * @optional_clk_rate: Optional clock's clk rate + * @optional_clk_id Clock IDs + * @optional_shared_clk_mask Mask indicating which of the clocks are shared with + * other devices. Set rate on these clocks needs to go + * through camera clk wrapper for aggregation. * @gpio_data: Pointer to gpio info * @pinctrl_info: Pointer to pinctrl info * @dentry: Debugfs entry @@ -205,12 +219,19 @@ struct cam_hw_soc_info { const char *clk_name[CAM_SOC_MAX_CLK]; struct clk *clk[CAM_SOC_MAX_CLK]; int32_t clk_rate[CAM_MAX_VOTE][CAM_SOC_MAX_CLK]; + uint32_t clk_id[CAM_SOC_MAX_CLK]; + uint32_t shared_clk_mask; int32_t prev_clk_level; int32_t src_clk_idx; unsigned long applied_src_clk_rate; bool clk_level_valid[CAM_MAX_VOTE]; int32_t scl_clk_count; int32_t scl_clk_idx[CAM_SOC_MAX_CLK]; + const char *optional_clk_name[CAM_SOC_MAX_OPT_CLK]; + struct clk *optional_clk[CAM_SOC_MAX_OPT_CLK]; + int32_t optional_clk_rate[CAM_SOC_MAX_OPT_CLK]; + uint32_t optional_clk_id[CAM_SOC_MAX_OPT_CLK]; + uint32_t optional_shared_clk_mask; struct cam_soc_gpio_data *gpio_data; struct cam_soc_pinctrl_info pinctrl_info; @@ -427,42 +448,48 @@ int cam_soc_util_set_src_clk_rate(struct cam_hw_soc_info *soc_info, * * @soc_info: Device soc information * @clk_name: Name of clock to find reference for - * @clk: Clock reference pointer to be filled if Success * @clk_index: Clk index in the option clk array to be returned - * @clk_rate: Clk rate in the option clk array * * @return: 0: Success * Negative: Failure */ int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info, - const char *clk_name, struct clk **clk, int32_t *clk_index, - int32_t *clk_rate); + const char *clk_name, int32_t *clk_index); /** - * cam_soc_util_clk_put() + * cam_soc_util_put_optional_clk() * - * @brief: Put clock specified in params + * @brief: Put clock corresponding to index specified in params * - * @clk: Reference to the Clock that needs to be put + * @soc_info: Device soc information + * @clk_idx: Clock index in optional clocks to put * * @return: Success or failure */ -int cam_soc_util_clk_put(struct clk **clk); +int cam_soc_util_put_optional_clk(struct cam_hw_soc_info *soc_info, + int32_t clk_idx); /** * cam_soc_util_clk_enable() * * @brief: Enable clock specified in params * - * @clk: Clock that needs to be turned ON - * @clk_name: Clocks name associated with clk - * @clk_rate: Clocks rate associated with clk + * @soc_info: Device soc information + * @optional_clk: Whether to set optional clk or normal clk with + * the idx given + * @clk_idx: Clock index to set + * @apply_level: Apply level. + * -1 for 0 rate + * any other value indicate level for normal clocks + * For optional clocks any other value means the rate saved + * in soc_info * @applied_clock_rate Final Clock rate applied to the clk * * @return: Success or failure */ -int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name, - int32_t clk_rate, unsigned long *applied_clock_rate); +int cam_soc_util_clk_enable(struct cam_hw_soc_info *soc_info, + bool optional_clk, int32_t clk_idx, int32_t apply_level, + unsigned long *applied_clock_rate); /** * cam_soc_util_set_clk_rate_level() @@ -485,12 +512,15 @@ int cam_soc_util_set_clk_rate_level(struct cam_hw_soc_info *soc_info, * * @brief: Disable clock specified in params * - * @clk: Clock that needs to be turned OFF - * @clk_name: Clocks name associated with clk + * @soc_info: Device soc information + * @optional_clk: Whether to set optional clk or normal clk with + * the idx given + * @clk_idx: Clock index to disable * * @return: Success or failure */ -int cam_soc_util_clk_disable(struct clk *clk, const char *clk_name); +int cam_soc_util_clk_disable(struct cam_hw_soc_info *soc_info, + bool optional_clk, int32_t clk_idx); /** * cam_soc_util_irq_enable()