diff options
Diffstat (limited to 'drivers/video/tegra/camera/camera_clk.c')
-rw-r--r-- | drivers/video/tegra/camera/camera_clk.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/drivers/video/tegra/camera/camera_clk.c b/drivers/video/tegra/camera/camera_clk.c new file mode 100644 index 000000000000..d3668c9a8356 --- /dev/null +++ b/drivers/video/tegra/camera/camera_clk.c @@ -0,0 +1,170 @@ +/* + * drivers/video/tegra/camera/camera_clk.c + * + * Copyright (C) 2013 Nvidia Corp + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "camera_clk.h" + +int tegra_camera_enable_clk(struct tegra_camera *camera) +{ + int i; + for (i = 0; i < CAMERA_CLK_MAX; i++) + clk_prepare_enable(camera->clk[i]); + return 0; +} + +int tegra_camera_disable_clk(struct tegra_camera *camera) +{ + int i; + for (i = CAMERA_CLK_MAX; i > 0; i--) + clk_prepare_enable(camera->clk[i-1]); + return 0; +} + +int tegra_camera_clk_set_rate(struct tegra_camera *camera) +{ + struct clk *clk, *clk_parent; + struct tegra_camera_clk_info *info = &camera->info; + unsigned long parent_rate, parent_div_rate, parent_div_rate_pre; + + if (!info) { + dev_err(camera->dev, + "%s: no clock info %d\n", + __func__, info->id); + return -EINVAL; + } + + if (info->id != TEGRA_CAMERA_MODULE_VI && + info->id != TEGRA_CAMERA_MODULE_EMC) { + dev_err(camera->dev, + "%s: set rate only aplies to vi module %d\n", + __func__, info->id); + return -EINVAL; + } + + switch (info->clk_id) { + case TEGRA_CAMERA_VI_CLK: + clk = camera->clk[CAMERA_VI_CLK]; + break; + case TEGRA_CAMERA_VI_SENSOR_CLK: + clk = camera->clk[CAMERA_VI_SENSOR_CLK]; + break; + case TEGRA_CAMERA_EMC_CLK: + clk = camera->clk[CAMERA_EMC_CLK]; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + { + /* + * User space assumes that HW emc controller is 4 + * byte-wide DDR controller. + * Emc bandwidth needs to be calcaluated using input emc + * freq first, and then real emc freq will + * be calculated using tegra_emc API. + * tegra_emc_bw_to_freq_req takes HW difference + * into consideration. + * bw param in tegra_emc_bw_to_freq_req() is in KHz. + */ + unsigned long bw = (info->rate * 8) >> 10; +#ifdef CONFIG_ARCH_TEGRA_11x_SOC + int ret = 0; +#endif + dev_dbg(camera->dev, "%s: emc_clk rate=%lu\n", + __func__, info->rate); + clk_set_rate(clk, + tegra_emc_bw_to_freq_req(bw) << 10); +#ifdef CONFIG_ARCH_TEGRA_11x_SOC + /* + * There is no way to figure out what latency + * can be tolerated in VI without reading VI + * registers for now. 3 usec is minimum time + * to switch PLL source. Let's put 4 usec as + * latency for now. + */ + ret = tegra_isomgr_reserve(camera->isomgr_handle, + bw, /* KB/sec */ + 4); /* usec */ + if (!ret) + return -ENOMEM; + + ret = tegra_isomgr_realize(camera->isomgr_handle); + if (!ret) + return -ENOMEM; +#endif + } +#endif + goto set_rate_end; + default: + dev_err(camera->dev, + "%s: invalid clk id for set rate %d\n", + __func__, info->clk_id); + return -EINVAL; + } + + clk_parent = clk_get_parent(clk); + parent_rate = clk_get_rate(clk_parent); + dev_dbg(camera->dev, "%s: clk_id=%d, parent_rate=%lu, clk_rate=%lu\n", + __func__, info->clk_id, parent_rate, info->rate); + parent_div_rate = parent_rate; + parent_div_rate_pre = parent_rate; + + /* + * The requested clock rate from user space should be respected. + * This loop is to search the clock rate that is higher than requested + * clock. + */ + while (parent_div_rate >= info->rate) { + parent_div_rate_pre = parent_div_rate; + parent_div_rate = clk_round_rate(clk, parent_div_rate-1); + } + + dev_dbg(camera->dev, "%s: set_rate=%lu", + __func__, parent_div_rate_pre); + + clk_set_rate(clk, parent_div_rate_pre); + + if (info->clk_id == TEGRA_CAMERA_VI_CLK) { +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + { + u32 val; + void __iomem *apb_misc = + IO_ADDRESS(TEGRA_APB_MISC_BASE); + val = readl(apb_misc + 0x42c); + writel(val | 0x1, apb_misc + 0x42c); + } +#endif + if (info->flag == TEGRA_CAMERA_ENABLE_PD2VI_CLK) { +#ifdef CONFIG_ARCH_TEGRA_11x_SOC + tegra_clk_cfg_ex(camera->clk[CAMERA_PLL_D2_CLK], + TEGRA_CLK_PLLD_CSI_OUT_ENB, 1); +#else + /* + * bit 25: 0 = pd2vi_Clk, 1 = vi_sensor_clk + * bit 24: 0 = internal clock, 1 = external clock(pd2vi_clk) + */ + tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2); +#endif + } +#ifdef CONFIG_ARCH_TEGRA_11x_SOC + else { + tegra_clk_cfg_ex(camera->clk[CAMERA_PLL_D2_CLK], + TEGRA_CLK_PLLD_CSI_OUT_ENB, 0); + } +#endif + } + +set_rate_end: + info->rate = clk_get_rate(clk); + dev_dbg(camera->dev, "%s: get_rate=%lu", + __func__, info->rate); + return 0; +} |