From 9e05bbac43ebfc2fd1ff95e072730ceed807d149 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 27 May 2020 23:59:40 +0200 Subject: media: smiapp-pll: Rename as ccs-pll MIPI CCS replaces SMIA and SMIA++ as the current standard. CCS brings new features while existing functionality will be supported. Rename the smiapp-pll as ccs-pll accordingly. Also add Intel copyright to the files. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/Makefile | 2 +- drivers/media/i2c/ccs-pll.c | 480 +++++++++++++++++++++++++++++++++++++ drivers/media/i2c/ccs-pll.h | 99 ++++++++ drivers/media/i2c/ccs/Kconfig | 2 +- drivers/media/i2c/ccs/ccs-core.c | 18 +- drivers/media/i2c/ccs/ccs-quirk.c | 2 +- drivers/media/i2c/ccs/ccs.h | 4 +- drivers/media/i2c/smiapp-pll.c | 482 -------------------------------------- drivers/media/i2c/smiapp-pll.h | 99 -------- 10 files changed, 594 insertions(+), 596 deletions(-) create mode 100644 drivers/media/i2c/ccs-pll.c create mode 100644 drivers/media/i2c/ccs-pll.h delete mode 100644 drivers/media/i2c/smiapp-pll.c delete mode 100644 drivers/media/i2c/smiapp-pll.h (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 92ff66c34f93..2b9d81e4794a 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -722,7 +722,7 @@ menu "Camera sensor devices" config VIDEO_APTINA_PLL tristate -config VIDEO_SMIAPP_PLL +config VIDEO_CCS_PLL tristate config VIDEO_HI556 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index bf9fd1bb6bc9..a3149dce21bb 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -106,7 +106,7 @@ obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_LM3560) += lm3560.o obj-$(CONFIG_VIDEO_LM3646) += lm3646.o -obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o +obj-$(CONFIG_VIDEO_CCS_PLL) += ccs-pll.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_I2C) += video-i2c.o diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c new file mode 100644 index 000000000000..d2f0f7375f5c --- /dev/null +++ b/drivers/media/i2c/ccs-pll.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/media/i2c/ccs-pll.c + * + * Generic MIPI CCS/SMIA/SMIA++ PLL calculator + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + */ + +#include +#include +#include +#include + +#include "ccs-pll.h" + +/* Return an even number or one. */ +static inline uint32_t clk_div_even(uint32_t a) +{ + return max_t(uint32_t, 1, a & ~1); +} + +/* Return an even number or one. */ +static inline uint32_t clk_div_even_up(uint32_t a) +{ + if (a == 1) + return 1; + return (a + 1) & ~1; +} + +static inline uint32_t is_one_or_even(uint32_t a) +{ + if (a == 1) + return 1; + if (a & 1) + return 0; + + return 1; +} + +static int bounds_check(struct device *dev, uint32_t val, + uint32_t min, uint32_t max, char *str) +{ + if (val >= min && val <= max) + return 0; + + dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); + + return -EINVAL; +} + +static void print_pll(struct device *dev, struct ccs_pll *pll) +{ + dev_dbg(dev, "pre_pll_clk_div\t%u\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pll_multiplier \t%u\n", pll->pll_multiplier); + if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)) { + dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div); + dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div); + } + dev_dbg(dev, "vt_sys_clk_div \t%u\n", pll->vt.sys_clk_div); + dev_dbg(dev, "vt_pix_clk_div \t%u\n", pll->vt.pix_clk_div); + + dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz); + dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz); + dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz); + if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)) { + dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n", + pll->op.sys_clk_freq_hz); + dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n", + pll->op.pix_clk_freq_hz); + } + dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt.sys_clk_freq_hz); + dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt.pix_clk_freq_hz); +} + +static int check_all_bounds(struct device *dev, + const struct ccs_pll_limits *limits, + const struct ccs_pll_branch_limits *op_limits, + struct ccs_pll *pll, struct ccs_pll_branch *op_pll) +{ + int rval; + + rval = bounds_check(dev, pll->pll_ip_clk_freq_hz, + limits->min_pll_ip_freq_hz, + limits->max_pll_ip_freq_hz, + "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->pll_multiplier, + limits->min_pll_multiplier, limits->max_pll_multiplier, + "pll_multiplier"); + if (!rval) + rval = bounds_check( + dev, pll->pll_op_clk_freq_hz, + limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, + "pll_op_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, op_pll->sys_clk_div, + op_limits->min_sys_clk_div, op_limits->max_sys_clk_div, + "op_sys_clk_div"); + if (!rval) + rval = bounds_check( + dev, op_pll->sys_clk_freq_hz, + op_limits->min_sys_clk_freq_hz, + op_limits->max_sys_clk_freq_hz, + "op_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, op_pll->pix_clk_freq_hz, + op_limits->min_pix_clk_freq_hz, + op_limits->max_pix_clk_freq_hz, + "op_pix_clk_freq_hz"); + + /* + * If there are no OP clocks, the VT clocks are contained in + * the OP clock struct. + */ + if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) + return rval; + + if (!rval) + rval = bounds_check( + dev, pll->vt.sys_clk_freq_hz, + limits->vt.min_sys_clk_freq_hz, + limits->vt.max_sys_clk_freq_hz, + "vt_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt.pix_clk_freq_hz, + limits->vt.min_pix_clk_freq_hz, + limits->vt.max_pix_clk_freq_hz, + "vt_pix_clk_freq_hz"); + + return rval; +} + +/* + * Heuristically guess the PLL tree for a given common multiplier and + * divisor. Begin with the operational timing and continue to video + * timing once operational timing has been verified. + * + * @mul is the PLL multiplier and @div is the common divisor + * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL + * multiplier will be a multiple of @mul. + * + * @return Zero on success, error code on error. + */ +static int +__ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits, + const struct ccs_pll_branch_limits *op_limits, + struct ccs_pll *pll, struct ccs_pll_branch *op_pll, + uint32_t mul, uint32_t div, uint32_t lane_op_clock_ratio) +{ + uint32_t sys_div; + uint32_t best_pix_div = INT_MAX >> 1; + uint32_t vt_op_binning_div; + /* + * Higher multipliers (and divisors) are often required than + * necessitated by the external clock and the output clocks. + * There are limits for all values in the clock tree. These + * are the minimum and maximum multiplier for mul. + */ + uint32_t more_mul_min, more_mul_max; + uint32_t more_mul_factor; + uint32_t min_vt_div, max_vt_div, vt_div; + uint32_t min_sys_div, max_sys_div; + unsigned int i; + + /* + * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be + * too high. + */ + dev_dbg(dev, "pre_pll_clk_div %u\n", pll->pre_pll_clk_div); + + /* Don't go above max pll multiplier. */ + more_mul_max = limits->max_pll_multiplier / mul; + dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %u\n", + more_mul_max); + /* Don't go above max pll op frequency. */ + more_mul_max = + min_t(uint32_t, + more_mul_max, + limits->max_pll_op_freq_hz + / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); + dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %u\n", + more_mul_max); + /* Don't go above the division capability of op sys clock divider. */ + more_mul_max = min(more_mul_max, + op_limits->max_sys_clk_div * pll->pre_pll_clk_div + / div); + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n", + more_mul_max); + /* Ensure we won't go above min_pll_multiplier. */ + more_mul_max = min(more_mul_max, + DIV_ROUND_UP(limits->max_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n", + more_mul_max); + + /* Ensure we won't go below min_pll_op_freq_hz. */ + more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, + pll->ext_clk_freq_hz / pll->pre_pll_clk_div + * mul); + dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %u\n", + more_mul_min); + /* Ensure we won't go below min_pll_multiplier. */ + more_mul_min = max(more_mul_min, + DIV_ROUND_UP(limits->min_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %u\n", + more_mul_min); + + if (more_mul_min > more_mul_max) { + dev_dbg(dev, + "unable to compute more_mul_min and more_mul_max\n"); + return -EINVAL; + } + + more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; + dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div); + dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", + more_mul_factor); + i = roundup(more_mul_min, more_mul_factor); + if (!is_one_or_even(i)) + i <<= 1; + + dev_dbg(dev, "final more_mul: %u\n", i); + if (i > more_mul_max) { + dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); + return -EINVAL; + } + + pll->pll_multiplier = mul * i; + op_pll->sys_clk_div = div * i / pll->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll->sys_clk_div); + + pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz + / pll->pre_pll_clk_div; + + pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz + * pll->pll_multiplier; + + /* Derive pll_op_clk_freq_hz. */ + op_pll->sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / op_pll->sys_clk_div; + + op_pll->pix_clk_div = pll->bits_per_pixel; + dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll->pix_clk_div); + + op_pll->pix_clk_freq_hz = + op_pll->sys_clk_freq_hz / op_pll->pix_clk_div; + + if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) { + /* No OP clocks --- VT clocks are used instead. */ + goto out_skip_vt_calc; + } + + /* + * Some sensors perform analogue binning and some do this + * digitally. The ones doing this digitally can be roughly be + * found out using this formula. The ones doing this digitally + * should run at higher clock rate, so smaller divisor is used + * on video timing side. + */ + if (limits->min_line_length_pck_bin > limits->min_line_length_pck + / pll->binning_horizontal) + vt_op_binning_div = pll->binning_horizontal; + else + vt_op_binning_div = 1; + dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div); + + /* + * Profile 2 supports vt_pix_clk_div E [4, 10] + * + * Horizontal binning can be used as a base for difference in + * divisors. One must make sure that horizontal blanking is + * enough to accommodate the CSI-2 sync codes. + * + * Take scaling factor into account as well. + * + * Find absolute limits for the factor of vt divider. + */ + dev_dbg(dev, "scale_m: %u\n", pll->scale_m); + min_vt_div = DIV_ROUND_UP(op_pll->pix_clk_div * op_pll->sys_clk_div + * pll->scale_n, + lane_op_clock_ratio * vt_op_binning_div + * pll->scale_m); + + /* Find smallest and biggest allowed vt divisor. */ + dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); + min_vt_div = max(min_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->vt.max_pix_clk_freq_hz)); + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", + min_vt_div); + min_vt_div = max_t(uint32_t, min_vt_div, + limits->vt.min_pix_clk_div + * limits->vt.min_sys_clk_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); + + max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div; + dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); + max_vt_div = min(max_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->vt.min_pix_clk_freq_hz)); + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", + max_vt_div); + + /* + * Find limitsits for sys_clk_div. Not all values are possible + * with all values of pix_clk_div. + */ + min_sys_div = limits->vt.min_sys_clk_div; + dev_dbg(dev, "min_sys_div: %u\n", min_sys_div); + min_sys_div = max(min_sys_div, + DIV_ROUND_UP(min_vt_div, + limits->vt.max_pix_clk_div)); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div); + min_sys_div = max(min_sys_div, + pll->pll_op_clk_freq_hz + / limits->vt.max_sys_clk_freq_hz); + dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div); + min_sys_div = clk_div_even_up(min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div); + + max_sys_div = limits->vt.max_sys_clk_div; + dev_dbg(dev, "max_sys_div: %u\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(max_vt_div, + limits->vt.min_pix_clk_div)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->vt.min_pix_clk_freq_hz)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div); + + /* + * Find pix_div such that a legal pix_div * sys_div results + * into a value which is not smaller than div, the desired + * divisor. + */ + for (vt_div = min_vt_div; vt_div <= max_vt_div; + vt_div += 2 - (vt_div & 1)) { + for (sys_div = min_sys_div; + sys_div <= max_sys_div; + sys_div += 2 - (sys_div & 1)) { + uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div); + + if (pix_div < limits->vt.min_pix_clk_div + || pix_div > limits->vt.max_pix_clk_div) { + dev_dbg(dev, + "pix_div %u too small or too big (%u--%u)\n", + pix_div, + limits->vt.min_pix_clk_div, + limits->vt.max_pix_clk_div); + continue; + } + + /* Check if this one is better. */ + if (pix_div * sys_div + <= roundup(min_vt_div, best_pix_div)) + best_pix_div = pix_div; + } + if (best_pix_div < INT_MAX >> 1) + break; + } + + pll->vt.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); + pll->vt.pix_clk_div = best_pix_div; + + pll->vt.sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->vt.sys_clk_div; + pll->vt.pix_clk_freq_hz = + pll->vt.sys_clk_freq_hz / pll->vt.pix_clk_div; + +out_skip_vt_calc: + pll->pixel_rate_csi = + op_pll->pix_clk_freq_hz * lane_op_clock_ratio; + pll->pixel_rate_pixel_array = pll->vt.pix_clk_freq_hz; + + return check_all_bounds(dev, limits, op_limits, pll, op_pll); +} + +int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits, + struct ccs_pll *pll) +{ + const struct ccs_pll_branch_limits *op_limits = &limits->op; + struct ccs_pll_branch *op_pll = &pll->op; + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t lane_op_clock_ratio; + uint32_t mul, div; + unsigned int i; + int rval = -EINVAL; + + if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) { + /* + * If there's no OP PLL at all, use the VT values + * instead. The OP values are ignored for the rest of + * the PLL calculation. + */ + op_limits = &limits->vt; + op_pll = &pll->vt; + } + + if (pll->flags & CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) + lane_op_clock_ratio = pll->csi2.lanes; + else + lane_op_clock_ratio = 1; + dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio); + + dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, + pll->binning_vertical); + + switch (pll->bus_type) { + case CCS_PLL_BUS_TYPE_CSI2: + /* CSI transfers 2 bits per clock per lane; thus times 2 */ + pll->pll_op_clk_freq_hz = pll->link_freq * 2 + * (pll->csi2.lanes / lane_op_clock_ratio); + break; + case CCS_PLL_BUS_TYPE_PARALLEL: + pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel + / DIV_ROUND_UP(pll->bits_per_pixel, + pll->parallel.bus_width); + break; + default: + return -EINVAL; + } + + /* Figure out limits for pre-pll divider based on extclk */ + dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + max_pre_pll_clk_div = + min_t(uint16_t, limits->max_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->min_pll_ip_freq_hz)); + min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(pll->ext_clk_freq_hz, + limits->max_pll_ip_freq_hz))); + dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n", + min_pre_pll_clk_div, max_pre_pll_clk_div); + + i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); + mul = div_u64(pll->pll_op_clk_freq_hz, i); + div = pll->ext_clk_freq_hz / i; + dev_dbg(dev, "mul %u / div %u\n", mul, div); + + min_pre_pll_clk_div = + max_t(uint16_t, min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, + limits->max_pll_op_freq_hz))); + dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n", + min_pre_pll_clk_div, max_pre_pll_clk_div); + + for (pll->pre_pll_clk_div = min_pre_pll_clk_div; + pll->pre_pll_clk_div <= max_pre_pll_clk_div; + pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { + rval = __ccs_pll_calculate(dev, limits, op_limits, pll, op_pll, + mul, div, lane_op_clock_ratio); + if (rval) + continue; + + print_pll(dev, pll); + return 0; + } + + dev_dbg(dev, "unable to compute pre_pll divisor\n"); + + return rval; +} +EXPORT_SYMBOL_GPL(ccs_pll_calculate); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h new file mode 100644 index 000000000000..88d641ee3fa1 --- /dev/null +++ b/drivers/media/i2c/ccs-pll.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drivers/media/i2c/ccs-pll.h + * + * Generic MIPI CCS/SMIA/SMIA++ PLL calculator + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus + */ + +#ifndef CCS_PLL_H +#define CCS_PLL_H + +/* CSI-2 or CCP-2 */ +#define CCS_PLL_BUS_TYPE_CSI2 0x00 +#define CCS_PLL_BUS_TYPE_PARALLEL 0x01 + +/* op pix clock is for all lanes in total normally */ +#define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define CCS_PLL_FLAG_NO_OP_CLOCKS (1 << 1) + +struct ccs_pll_branch { + uint16_t sys_clk_div; + uint16_t pix_clk_div; + uint32_t sys_clk_freq_hz; + uint32_t pix_clk_freq_hz; +}; + +struct ccs_pll { + /* input values */ + uint8_t bus_type; + union { + struct { + uint8_t lanes; + } csi2; + struct { + uint8_t bus_width; + } parallel; + }; + unsigned long flags; + uint8_t binning_horizontal; + uint8_t binning_vertical; + uint8_t scale_m; + uint8_t scale_n; + uint8_t bits_per_pixel; + uint32_t link_freq; + uint32_t ext_clk_freq_hz; + + /* output values */ + uint16_t pre_pll_clk_div; + uint16_t pll_multiplier; + uint32_t pll_ip_clk_freq_hz; + uint32_t pll_op_clk_freq_hz; + struct ccs_pll_branch vt; + struct ccs_pll_branch op; + + uint32_t pixel_rate_csi; + uint32_t pixel_rate_pixel_array; +}; + +struct ccs_pll_branch_limits { + uint16_t min_sys_clk_div; + uint16_t max_sys_clk_div; + uint32_t min_sys_clk_freq_hz; + uint32_t max_sys_clk_freq_hz; + uint16_t min_pix_clk_div; + uint16_t max_pix_clk_div; + uint32_t min_pix_clk_freq_hz; + uint32_t max_pix_clk_freq_hz; +}; + +struct ccs_pll_limits { + /* Strict PLL limits */ + uint32_t min_ext_clk_freq_hz; + uint32_t max_ext_clk_freq_hz; + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t min_pll_ip_freq_hz; + uint32_t max_pll_ip_freq_hz; + uint16_t min_pll_multiplier; + uint16_t max_pll_multiplier; + uint32_t min_pll_op_freq_hz; + uint32_t max_pll_op_freq_hz; + + struct ccs_pll_branch_limits vt; + struct ccs_pll_branch_limits op; + + /* Other relevant limits */ + uint32_t min_line_length_pck_bin; + uint32_t min_line_length_pck; +}; + +struct device; + +int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits, + struct ccs_pll *pll); + +#endif /* CCS_PLL_H */ diff --git a/drivers/media/i2c/ccs/Kconfig b/drivers/media/i2c/ccs/Kconfig index b4f8b10da420..59f35b33ddc1 100644 --- a/drivers/media/i2c/ccs/Kconfig +++ b/drivers/media/i2c/ccs/Kconfig @@ -4,7 +4,7 @@ config VIDEO_CCS depends on I2C && VIDEO_V4L2 && HAVE_CLK select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API - select VIDEO_SMIAPP_PLL + select VIDEO_CCS_PLL select V4L2_FWNODE help This is a generic driver for MIPI CCS, SMIA++ and SMIA compliant diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 074b246538d2..6c8528e6ac96 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -363,7 +363,7 @@ static int ccs_read_frame_fmt(struct ccs_sensor *sensor) static int ccs_pll_configure(struct ccs_sensor *sensor) { - struct smiapp_pll *pll = &sensor->pll; + struct ccs_pll *pll = &sensor->pll; int rval; rval = ccs_write(sensor, VT_PIX_CLK_DIV, pll->vt.pix_clk_div); @@ -386,7 +386,7 @@ static int ccs_pll_configure(struct ccs_sensor *sensor) rval = ccs_write(sensor, REQUESTED_LINK_RATE, DIV_ROUND_UP(pll->op.sys_clk_freq_hz, 1000000 / 256 / 256)); - if (rval < 0 || sensor->pll.flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) + if (rval < 0 || sensor->pll.flags & CCS_PLL_FLAG_NO_OP_CLOCKS) return rval; rval = ccs_write(sensor, OP_PIX_CLK_DIV, pll->op.pix_clk_div); @@ -396,10 +396,10 @@ static int ccs_pll_configure(struct ccs_sensor *sensor) return ccs_write(sensor, OP_SYS_CLK_DIV, pll->op.sys_clk_div); } -static int ccs_pll_try(struct ccs_sensor *sensor, struct smiapp_pll *pll) +static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct smiapp_pll_limits lim = { + struct ccs_pll_limits lim = { .min_pre_pll_clk_div = CCS_LIM(sensor, MIN_PRE_PLL_CLK_DIV), .max_pre_pll_clk_div = CCS_LIM(sensor, MAX_PRE_PLL_CLK_DIV), .min_pll_ip_freq_hz = CCS_LIM(sensor, MIN_PLL_IP_CLK_FREQ_MHZ), @@ -431,12 +431,12 @@ static int ccs_pll_try(struct ccs_sensor *sensor, struct smiapp_pll *pll) .min_line_length_pck = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK), }; - return smiapp_pll_calculate(&client->dev, &lim, pll); + return ccs_pll_calculate(&client->dev, &lim, pll); } static int ccs_pll_update(struct ccs_sensor *sensor) { - struct smiapp_pll *pll = &sensor->pll; + struct ccs_pll *pll = &sensor->pll; int rval; pll->binning_horizontal = sensor->binning_horizontal; @@ -829,7 +829,7 @@ static void ccs_free_controls(struct ccs_sensor *sensor) static int ccs_get_mbus_formats(struct ccs_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct smiapp_pll *pll = &sensor->pll; + struct ccs_pll *pll = &sensor->pll; u8 compressed_max_bpp = 0; unsigned int type, n; unsigned int i, pixel_order; @@ -3155,7 +3155,7 @@ static int ccs_probe(struct i2c_client *client) !CCS_LIM(sensor, MIN_OP_PIX_CLK_DIV) || !CCS_LIM(sensor, MAX_OP_PIX_CLK_DIV)) { /* No OP clock branch */ - sensor->pll.flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; + sensor->pll.flags |= CCS_PLL_FLAG_NO_OP_CLOCKS; } else if (CCS_LIM(sensor, SCALING_CAPABILITY) != CCS_SCALING_CAPABILITY_NONE || CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY) @@ -3172,7 +3172,7 @@ static int ccs_probe(struct i2c_client *client) sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN); /* prepare PLL configuration input values */ - sensor->pll.bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; + sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2; sensor->pll.csi2.lanes = sensor->hwcfg.lanes; sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk; sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN); diff --git a/drivers/media/i2c/ccs/ccs-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c index 07c5733b4244..8b4fa60044b2 100644 --- a/drivers/media/i2c/ccs/ccs-quirk.c +++ b/drivers/media/i2c/ccs/ccs-quirk.c @@ -190,7 +190,7 @@ static int jt8ev1_post_streamoff(struct ccs_sensor *sensor) static int jt8ev1_init(struct ccs_sensor *sensor) { - sensor->pll.flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + sensor->pll.flags |= CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; return 0; } diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index f60d1801c469..c8a9f4ee093e 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -21,7 +21,7 @@ #include "ccs-quirk.h" #include "ccs-regs.h" #include "ccs-reg-access.h" -#include "../smiapp-pll.h" +#include "../ccs-pll.h" #include "smiapp-reg-defs.h" /* @@ -256,7 +256,7 @@ struct ccs_sensor { struct ccs_module_info minfo; - struct smiapp_pll pll; + struct ccs_pll pll; /* Is a default format supported for a given BPP? */ unsigned long *valid_link_freqs; diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c deleted file mode 100644 index 690abe8cbdb2..000000000000 --- a/drivers/media/i2c/smiapp-pll.c +++ /dev/null @@ -1,482 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/media/i2c/smiapp-pll.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - */ - -#include -#include -#include -#include - -#include "smiapp-pll.h" - -/* Return an even number or one. */ -static inline uint32_t clk_div_even(uint32_t a) -{ - return max_t(uint32_t, 1, a & ~1); -} - -/* Return an even number or one. */ -static inline uint32_t clk_div_even_up(uint32_t a) -{ - if (a == 1) - return 1; - return (a + 1) & ~1; -} - -static inline uint32_t is_one_or_even(uint32_t a) -{ - if (a == 1) - return 1; - if (a & 1) - return 0; - - return 1; -} - -static int bounds_check(struct device *dev, uint32_t val, - uint32_t min, uint32_t max, char *str) -{ - if (val >= min && val <= max) - return 0; - - dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); - - return -EINVAL; -} - -static void print_pll(struct device *dev, struct smiapp_pll *pll) -{ - dev_dbg(dev, "pre_pll_clk_div\t%u\n", pll->pre_pll_clk_div); - dev_dbg(dev, "pll_multiplier \t%u\n", pll->pll_multiplier); - if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { - dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div); - dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div); - } - dev_dbg(dev, "vt_sys_clk_div \t%u\n", pll->vt.sys_clk_div); - dev_dbg(dev, "vt_pix_clk_div \t%u\n", pll->vt.pix_clk_div); - - dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz); - dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz); - dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz); - if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { - dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n", - pll->op.sys_clk_freq_hz); - dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n", - pll->op.pix_clk_freq_hz); - } - dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt.sys_clk_freq_hz); - dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt.pix_clk_freq_hz); -} - -static int check_all_bounds(struct device *dev, - const struct smiapp_pll_limits *limits, - const struct smiapp_pll_branch_limits *op_limits, - struct smiapp_pll *pll, - struct smiapp_pll_branch *op_pll) -{ - int rval; - - rval = bounds_check(dev, pll->pll_ip_clk_freq_hz, - limits->min_pll_ip_freq_hz, - limits->max_pll_ip_freq_hz, - "pll_ip_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->pll_multiplier, - limits->min_pll_multiplier, limits->max_pll_multiplier, - "pll_multiplier"); - if (!rval) - rval = bounds_check( - dev, pll->pll_op_clk_freq_hz, - limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, - "pll_op_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, op_pll->sys_clk_div, - op_limits->min_sys_clk_div, op_limits->max_sys_clk_div, - "op_sys_clk_div"); - if (!rval) - rval = bounds_check( - dev, op_pll->sys_clk_freq_hz, - op_limits->min_sys_clk_freq_hz, - op_limits->max_sys_clk_freq_hz, - "op_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, op_pll->pix_clk_freq_hz, - op_limits->min_pix_clk_freq_hz, - op_limits->max_pix_clk_freq_hz, - "op_pix_clk_freq_hz"); - - /* - * If there are no OP clocks, the VT clocks are contained in - * the OP clock struct. - */ - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) - return rval; - - if (!rval) - rval = bounds_check( - dev, pll->vt.sys_clk_freq_hz, - limits->vt.min_sys_clk_freq_hz, - limits->vt.max_sys_clk_freq_hz, - "vt_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt.pix_clk_freq_hz, - limits->vt.min_pix_clk_freq_hz, - limits->vt.max_pix_clk_freq_hz, - "vt_pix_clk_freq_hz"); - - return rval; -} - -/* - * Heuristically guess the PLL tree for a given common multiplier and - * divisor. Begin with the operational timing and continue to video - * timing once operational timing has been verified. - * - * @mul is the PLL multiplier and @div is the common divisor - * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL - * multiplier will be a multiple of @mul. - * - * @return Zero on success, error code on error. - */ -static int __smiapp_pll_calculate( - struct device *dev, const struct smiapp_pll_limits *limits, - const struct smiapp_pll_branch_limits *op_limits, - struct smiapp_pll *pll, struct smiapp_pll_branch *op_pll, uint32_t mul, - uint32_t div, uint32_t lane_op_clock_ratio) -{ - uint32_t sys_div; - uint32_t best_pix_div = INT_MAX >> 1; - uint32_t vt_op_binning_div; - /* - * Higher multipliers (and divisors) are often required than - * necessitated by the external clock and the output clocks. - * There are limits for all values in the clock tree. These - * are the minimum and maximum multiplier for mul. - */ - uint32_t more_mul_min, more_mul_max; - uint32_t more_mul_factor; - uint32_t min_vt_div, max_vt_div, vt_div; - uint32_t min_sys_div, max_sys_div; - unsigned int i; - - /* - * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be - * too high. - */ - dev_dbg(dev, "pre_pll_clk_div %u\n", pll->pre_pll_clk_div); - - /* Don't go above max pll multiplier. */ - more_mul_max = limits->max_pll_multiplier / mul; - dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %u\n", - more_mul_max); - /* Don't go above max pll op frequency. */ - more_mul_max = - min_t(uint32_t, - more_mul_max, - limits->max_pll_op_freq_hz - / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); - dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %u\n", - more_mul_max); - /* Don't go above the division capability of op sys clock divider. */ - more_mul_max = min(more_mul_max, - op_limits->max_sys_clk_div * pll->pre_pll_clk_div - / div); - dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n", - more_mul_max); - /* Ensure we won't go above min_pll_multiplier. */ - more_mul_max = min(more_mul_max, - DIV_ROUND_UP(limits->max_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n", - more_mul_max); - - /* Ensure we won't go below min_pll_op_freq_hz. */ - more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, - pll->ext_clk_freq_hz / pll->pre_pll_clk_div - * mul); - dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %u\n", - more_mul_min); - /* Ensure we won't go below min_pll_multiplier. */ - more_mul_min = max(more_mul_min, - DIV_ROUND_UP(limits->min_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %u\n", - more_mul_min); - - if (more_mul_min > more_mul_max) { - dev_dbg(dev, - "unable to compute more_mul_min and more_mul_max\n"); - return -EINVAL; - } - - more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; - dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor); - more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div); - dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", - more_mul_factor); - i = roundup(more_mul_min, more_mul_factor); - if (!is_one_or_even(i)) - i <<= 1; - - dev_dbg(dev, "final more_mul: %u\n", i); - if (i > more_mul_max) { - dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); - return -EINVAL; - } - - pll->pll_multiplier = mul * i; - op_pll->sys_clk_div = div * i / pll->pre_pll_clk_div; - dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll->sys_clk_div); - - pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz - / pll->pre_pll_clk_div; - - pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz - * pll->pll_multiplier; - - /* Derive pll_op_clk_freq_hz. */ - op_pll->sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / op_pll->sys_clk_div; - - op_pll->pix_clk_div = pll->bits_per_pixel; - dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll->pix_clk_div); - - op_pll->pix_clk_freq_hz = - op_pll->sys_clk_freq_hz / op_pll->pix_clk_div; - - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - /* No OP clocks --- VT clocks are used instead. */ - goto out_skip_vt_calc; - } - - /* - * Some sensors perform analogue binning and some do this - * digitally. The ones doing this digitally can be roughly be - * found out using this formula. The ones doing this digitally - * should run at higher clock rate, so smaller divisor is used - * on video timing side. - */ - if (limits->min_line_length_pck_bin > limits->min_line_length_pck - / pll->binning_horizontal) - vt_op_binning_div = pll->binning_horizontal; - else - vt_op_binning_div = 1; - dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div); - - /* - * Profile 2 supports vt_pix_clk_div E [4, 10] - * - * Horizontal binning can be used as a base for difference in - * divisors. One must make sure that horizontal blanking is - * enough to accommodate the CSI-2 sync codes. - * - * Take scaling factor into account as well. - * - * Find absolute limits for the factor of vt divider. - */ - dev_dbg(dev, "scale_m: %u\n", pll->scale_m); - min_vt_div = DIV_ROUND_UP(op_pll->pix_clk_div * op_pll->sys_clk_div - * pll->scale_n, - lane_op_clock_ratio * vt_op_binning_div - * pll->scale_m); - - /* Find smallest and biggest allowed vt divisor. */ - dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); - min_vt_div = max(min_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->vt.max_pix_clk_freq_hz)); - dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", - min_vt_div); - min_vt_div = max_t(uint32_t, min_vt_div, - limits->vt.min_pix_clk_div - * limits->vt.min_sys_clk_div); - dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); - - max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div; - dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); - max_vt_div = min(max_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->vt.min_pix_clk_freq_hz)); - dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", - max_vt_div); - - /* - * Find limitsits for sys_clk_div. Not all values are possible - * with all values of pix_clk_div. - */ - min_sys_div = limits->vt.min_sys_clk_div; - dev_dbg(dev, "min_sys_div: %u\n", min_sys_div); - min_sys_div = max(min_sys_div, - DIV_ROUND_UP(min_vt_div, - limits->vt.max_pix_clk_div)); - dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div); - min_sys_div = max(min_sys_div, - pll->pll_op_clk_freq_hz - / limits->vt.max_sys_clk_freq_hz); - dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div); - min_sys_div = clk_div_even_up(min_sys_div); - dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div); - - max_sys_div = limits->vt.max_sys_clk_div; - dev_dbg(dev, "max_sys_div: %u\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(max_vt_div, - limits->vt.min_pix_clk_div)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->vt.min_pix_clk_freq_hz)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div); - - /* - * Find pix_div such that a legal pix_div * sys_div results - * into a value which is not smaller than div, the desired - * divisor. - */ - for (vt_div = min_vt_div; vt_div <= max_vt_div; - vt_div += 2 - (vt_div & 1)) { - for (sys_div = min_sys_div; - sys_div <= max_sys_div; - sys_div += 2 - (sys_div & 1)) { - uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div); - - if (pix_div < limits->vt.min_pix_clk_div - || pix_div > limits->vt.max_pix_clk_div) { - dev_dbg(dev, - "pix_div %u too small or too big (%u--%u)\n", - pix_div, - limits->vt.min_pix_clk_div, - limits->vt.max_pix_clk_div); - continue; - } - - /* Check if this one is better. */ - if (pix_div * sys_div - <= roundup(min_vt_div, best_pix_div)) - best_pix_div = pix_div; - } - if (best_pix_div < INT_MAX >> 1) - break; - } - - pll->vt.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); - pll->vt.pix_clk_div = best_pix_div; - - pll->vt.sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->vt.sys_clk_div; - pll->vt.pix_clk_freq_hz = - pll->vt.sys_clk_freq_hz / pll->vt.pix_clk_div; - -out_skip_vt_calc: - pll->pixel_rate_csi = - op_pll->pix_clk_freq_hz * lane_op_clock_ratio; - pll->pixel_rate_pixel_array = pll->vt.pix_clk_freq_hz; - - return check_all_bounds(dev, limits, op_limits, pll, op_pll); -} - -int smiapp_pll_calculate(struct device *dev, - const struct smiapp_pll_limits *limits, - struct smiapp_pll *pll) -{ - const struct smiapp_pll_branch_limits *op_limits = &limits->op; - struct smiapp_pll_branch *op_pll = &pll->op; - uint16_t min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div; - uint32_t lane_op_clock_ratio; - uint32_t mul, div; - unsigned int i; - int rval = -EINVAL; - - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - /* - * If there's no OP PLL at all, use the VT values - * instead. The OP values are ignored for the rest of - * the PLL calculation. - */ - op_limits = &limits->vt; - op_pll = &pll->vt; - } - - if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) - lane_op_clock_ratio = pll->csi2.lanes; - else - lane_op_clock_ratio = 1; - dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio); - - dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, - pll->binning_vertical); - - switch (pll->bus_type) { - case SMIAPP_PLL_BUS_TYPE_CSI2: - /* CSI transfers 2 bits per clock per lane; thus times 2 */ - pll->pll_op_clk_freq_hz = pll->link_freq * 2 - * (pll->csi2.lanes / lane_op_clock_ratio); - break; - case SMIAPP_PLL_BUS_TYPE_PARALLEL: - pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel - / DIV_ROUND_UP(pll->bits_per_pixel, - pll->parallel.bus_width); - break; - default: - return -EINVAL; - } - - /* Figure out limits for pre-pll divider based on extclk */ - dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - max_pre_pll_clk_div = - min_t(uint16_t, limits->max_pre_pll_clk_div, - clk_div_even(pll->ext_clk_freq_hz / - limits->min_pll_ip_freq_hz)); - min_pre_pll_clk_div = - max_t(uint16_t, limits->min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(pll->ext_clk_freq_hz, - limits->max_pll_ip_freq_hz))); - dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n", - min_pre_pll_clk_div, max_pre_pll_clk_div); - - i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); - mul = div_u64(pll->pll_op_clk_freq_hz, i); - div = pll->ext_clk_freq_hz / i; - dev_dbg(dev, "mul %u / div %u\n", mul, div); - - min_pre_pll_clk_div = - max_t(uint16_t, min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, - limits->max_pll_op_freq_hz))); - dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n", - min_pre_pll_clk_div, max_pre_pll_clk_div); - - for (pll->pre_pll_clk_div = min_pre_pll_clk_div; - pll->pre_pll_clk_div <= max_pre_pll_clk_div; - pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { - rval = __smiapp_pll_calculate(dev, limits, op_limits, pll, - op_pll, mul, div, - lane_op_clock_ratio); - if (rval) - continue; - - print_pll(dev, pll); - return 0; - } - - dev_dbg(dev, "unable to compute pre_pll divisor\n"); - - return rval; -} -EXPORT_SYMBOL_GPL(smiapp_pll_calculate); - -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h deleted file mode 100644 index bd6902f54539..000000000000 --- a/drivers/media/i2c/smiapp-pll.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * drivers/media/i2c/smiapp-pll.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2012 Nokia Corporation - * Contact: Sakari Ailus - */ - -#ifndef SMIAPP_PLL_H -#define SMIAPP_PLL_H - -/* CSI-2 or CCP-2 */ -#define SMIAPP_PLL_BUS_TYPE_CSI2 0x00 -#define SMIAPP_PLL_BUS_TYPE_PARALLEL 0x01 - -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) -#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) - -struct smiapp_pll_branch { - uint16_t sys_clk_div; - uint16_t pix_clk_div; - uint32_t sys_clk_freq_hz; - uint32_t pix_clk_freq_hz; -}; - -struct smiapp_pll { - /* input values */ - uint8_t bus_type; - union { - struct { - uint8_t lanes; - } csi2; - struct { - uint8_t bus_width; - } parallel; - }; - unsigned long flags; - uint8_t binning_horizontal; - uint8_t binning_vertical; - uint8_t scale_m; - uint8_t scale_n; - uint8_t bits_per_pixel; - uint32_t link_freq; - uint32_t ext_clk_freq_hz; - - /* output values */ - uint16_t pre_pll_clk_div; - uint16_t pll_multiplier; - uint32_t pll_ip_clk_freq_hz; - uint32_t pll_op_clk_freq_hz; - struct smiapp_pll_branch vt; - struct smiapp_pll_branch op; - - uint32_t pixel_rate_csi; - uint32_t pixel_rate_pixel_array; -}; - -struct smiapp_pll_branch_limits { - uint16_t min_sys_clk_div; - uint16_t max_sys_clk_div; - uint32_t min_sys_clk_freq_hz; - uint32_t max_sys_clk_freq_hz; - uint16_t min_pix_clk_div; - uint16_t max_pix_clk_div; - uint32_t min_pix_clk_freq_hz; - uint32_t max_pix_clk_freq_hz; -}; - -struct smiapp_pll_limits { - /* Strict PLL limits */ - uint32_t min_ext_clk_freq_hz; - uint32_t max_ext_clk_freq_hz; - uint16_t min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div; - uint32_t min_pll_ip_freq_hz; - uint32_t max_pll_ip_freq_hz; - uint16_t min_pll_multiplier; - uint16_t max_pll_multiplier; - uint32_t min_pll_op_freq_hz; - uint32_t max_pll_op_freq_hz; - - struct smiapp_pll_branch_limits vt; - struct smiapp_pll_branch_limits op; - - /* Other relevant limits */ - uint32_t min_line_length_pck_bin; - uint32_t min_line_length_pck; -}; - -struct device; - -int smiapp_pll_calculate(struct device *dev, - const struct smiapp_pll_limits *limits, - struct smiapp_pll *pll); - -#endif /* SMIAPP_PLL_H */ -- cgit v1.2.3