18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/media/i2c/smiapp-pll.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Generic driver for SMIA/SMIA++ compliant camera modules 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2011--2012 Nokia Corporation 88c2ecf20Sopenharmony_ci * Contact: Sakari Ailus <sakari.ailus@iki.fi> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/gcd.h> 138c2ecf20Sopenharmony_ci#include <linux/lcm.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "smiapp-pll.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Return an even number or one. */ 198c2ecf20Sopenharmony_cistatic inline uint32_t clk_div_even(uint32_t a) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci return max_t(uint32_t, 1, a & ~1); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Return an even number or one. */ 258c2ecf20Sopenharmony_cistatic inline uint32_t clk_div_even_up(uint32_t a) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci if (a == 1) 288c2ecf20Sopenharmony_ci return 1; 298c2ecf20Sopenharmony_ci return (a + 1) & ~1; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic inline uint32_t is_one_or_even(uint32_t a) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci if (a == 1) 358c2ecf20Sopenharmony_ci return 1; 368c2ecf20Sopenharmony_ci if (a & 1) 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return 1; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int bounds_check(struct device *dev, uint32_t val, 438c2ecf20Sopenharmony_ci uint32_t min, uint32_t max, char *str) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci if (val >= min && val <= max) 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return -EINVAL; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void print_pll(struct device *dev, struct smiapp_pll *pll) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci dev_dbg(dev, "pre_pll_clk_div\t%u\n", pll->pre_pll_clk_div); 568c2ecf20Sopenharmony_ci dev_dbg(dev, "pll_multiplier \t%u\n", pll->pll_multiplier); 578c2ecf20Sopenharmony_ci if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { 588c2ecf20Sopenharmony_ci dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div); 598c2ecf20Sopenharmony_ci dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div); 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci dev_dbg(dev, "vt_sys_clk_div \t%u\n", pll->vt.sys_clk_div); 628c2ecf20Sopenharmony_ci dev_dbg(dev, "vt_pix_clk_div \t%u\n", pll->vt.pix_clk_div); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz); 658c2ecf20Sopenharmony_ci dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz); 668c2ecf20Sopenharmony_ci dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz); 678c2ecf20Sopenharmony_ci if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { 688c2ecf20Sopenharmony_ci dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n", 698c2ecf20Sopenharmony_ci pll->op.sys_clk_freq_hz); 708c2ecf20Sopenharmony_ci dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n", 718c2ecf20Sopenharmony_ci pll->op.pix_clk_freq_hz); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt.sys_clk_freq_hz); 748c2ecf20Sopenharmony_ci dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt.pix_clk_freq_hz); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int check_all_bounds(struct device *dev, 788c2ecf20Sopenharmony_ci const struct smiapp_pll_limits *limits, 798c2ecf20Sopenharmony_ci const struct smiapp_pll_branch_limits *op_limits, 808c2ecf20Sopenharmony_ci struct smiapp_pll *pll, 818c2ecf20Sopenharmony_ci struct smiapp_pll_branch *op_pll) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int rval; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci rval = bounds_check(dev, pll->pll_ip_clk_freq_hz, 868c2ecf20Sopenharmony_ci limits->min_pll_ip_freq_hz, 878c2ecf20Sopenharmony_ci limits->max_pll_ip_freq_hz, 888c2ecf20Sopenharmony_ci "pll_ip_clk_freq_hz"); 898c2ecf20Sopenharmony_ci if (!rval) 908c2ecf20Sopenharmony_ci rval = bounds_check( 918c2ecf20Sopenharmony_ci dev, pll->pll_multiplier, 928c2ecf20Sopenharmony_ci limits->min_pll_multiplier, limits->max_pll_multiplier, 938c2ecf20Sopenharmony_ci "pll_multiplier"); 948c2ecf20Sopenharmony_ci if (!rval) 958c2ecf20Sopenharmony_ci rval = bounds_check( 968c2ecf20Sopenharmony_ci dev, pll->pll_op_clk_freq_hz, 978c2ecf20Sopenharmony_ci limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, 988c2ecf20Sopenharmony_ci "pll_op_clk_freq_hz"); 998c2ecf20Sopenharmony_ci if (!rval) 1008c2ecf20Sopenharmony_ci rval = bounds_check( 1018c2ecf20Sopenharmony_ci dev, op_pll->sys_clk_div, 1028c2ecf20Sopenharmony_ci op_limits->min_sys_clk_div, op_limits->max_sys_clk_div, 1038c2ecf20Sopenharmony_ci "op_sys_clk_div"); 1048c2ecf20Sopenharmony_ci if (!rval) 1058c2ecf20Sopenharmony_ci rval = bounds_check( 1068c2ecf20Sopenharmony_ci dev, op_pll->sys_clk_freq_hz, 1078c2ecf20Sopenharmony_ci op_limits->min_sys_clk_freq_hz, 1088c2ecf20Sopenharmony_ci op_limits->max_sys_clk_freq_hz, 1098c2ecf20Sopenharmony_ci "op_sys_clk_freq_hz"); 1108c2ecf20Sopenharmony_ci if (!rval) 1118c2ecf20Sopenharmony_ci rval = bounds_check( 1128c2ecf20Sopenharmony_ci dev, op_pll->pix_clk_freq_hz, 1138c2ecf20Sopenharmony_ci op_limits->min_pix_clk_freq_hz, 1148c2ecf20Sopenharmony_ci op_limits->max_pix_clk_freq_hz, 1158c2ecf20Sopenharmony_ci "op_pix_clk_freq_hz"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* 1188c2ecf20Sopenharmony_ci * If there are no OP clocks, the VT clocks are contained in 1198c2ecf20Sopenharmony_ci * the OP clock struct. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) 1228c2ecf20Sopenharmony_ci return rval; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (!rval) 1258c2ecf20Sopenharmony_ci rval = bounds_check( 1268c2ecf20Sopenharmony_ci dev, pll->vt.sys_clk_freq_hz, 1278c2ecf20Sopenharmony_ci limits->vt.min_sys_clk_freq_hz, 1288c2ecf20Sopenharmony_ci limits->vt.max_sys_clk_freq_hz, 1298c2ecf20Sopenharmony_ci "vt_sys_clk_freq_hz"); 1308c2ecf20Sopenharmony_ci if (!rval) 1318c2ecf20Sopenharmony_ci rval = bounds_check( 1328c2ecf20Sopenharmony_ci dev, pll->vt.pix_clk_freq_hz, 1338c2ecf20Sopenharmony_ci limits->vt.min_pix_clk_freq_hz, 1348c2ecf20Sopenharmony_ci limits->vt.max_pix_clk_freq_hz, 1358c2ecf20Sopenharmony_ci "vt_pix_clk_freq_hz"); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return rval; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * Heuristically guess the PLL tree for a given common multiplier and 1428c2ecf20Sopenharmony_ci * divisor. Begin with the operational timing and continue to video 1438c2ecf20Sopenharmony_ci * timing once operational timing has been verified. 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * @mul is the PLL multiplier and @div is the common divisor 1468c2ecf20Sopenharmony_ci * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL 1478c2ecf20Sopenharmony_ci * multiplier will be a multiple of @mul. 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * @return Zero on success, error code on error. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int __smiapp_pll_calculate( 1528c2ecf20Sopenharmony_ci struct device *dev, const struct smiapp_pll_limits *limits, 1538c2ecf20Sopenharmony_ci const struct smiapp_pll_branch_limits *op_limits, 1548c2ecf20Sopenharmony_ci struct smiapp_pll *pll, struct smiapp_pll_branch *op_pll, uint32_t mul, 1558c2ecf20Sopenharmony_ci uint32_t div, uint32_t lane_op_clock_ratio) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci uint32_t sys_div; 1588c2ecf20Sopenharmony_ci uint32_t best_pix_div = INT_MAX >> 1; 1598c2ecf20Sopenharmony_ci uint32_t vt_op_binning_div; 1608c2ecf20Sopenharmony_ci /* 1618c2ecf20Sopenharmony_ci * Higher multipliers (and divisors) are often required than 1628c2ecf20Sopenharmony_ci * necessitated by the external clock and the output clocks. 1638c2ecf20Sopenharmony_ci * There are limits for all values in the clock tree. These 1648c2ecf20Sopenharmony_ci * are the minimum and maximum multiplier for mul. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci uint32_t more_mul_min, more_mul_max; 1678c2ecf20Sopenharmony_ci uint32_t more_mul_factor; 1688c2ecf20Sopenharmony_ci uint32_t min_vt_div, max_vt_div, vt_div; 1698c2ecf20Sopenharmony_ci uint32_t min_sys_div, max_sys_div; 1708c2ecf20Sopenharmony_ci unsigned int i; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be 1748c2ecf20Sopenharmony_ci * too high. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci dev_dbg(dev, "pre_pll_clk_div %u\n", pll->pre_pll_clk_div); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Don't go above max pll multiplier. */ 1798c2ecf20Sopenharmony_ci more_mul_max = limits->max_pll_multiplier / mul; 1808c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %u\n", 1818c2ecf20Sopenharmony_ci more_mul_max); 1828c2ecf20Sopenharmony_ci /* Don't go above max pll op frequency. */ 1838c2ecf20Sopenharmony_ci more_mul_max = 1848c2ecf20Sopenharmony_ci min_t(uint32_t, 1858c2ecf20Sopenharmony_ci more_mul_max, 1868c2ecf20Sopenharmony_ci limits->max_pll_op_freq_hz 1878c2ecf20Sopenharmony_ci / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); 1888c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %u\n", 1898c2ecf20Sopenharmony_ci more_mul_max); 1908c2ecf20Sopenharmony_ci /* Don't go above the division capability of op sys clock divider. */ 1918c2ecf20Sopenharmony_ci more_mul_max = min(more_mul_max, 1928c2ecf20Sopenharmony_ci op_limits->max_sys_clk_div * pll->pre_pll_clk_div 1938c2ecf20Sopenharmony_ci / div); 1948c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n", 1958c2ecf20Sopenharmony_ci more_mul_max); 1968c2ecf20Sopenharmony_ci /* Ensure we won't go above min_pll_multiplier. */ 1978c2ecf20Sopenharmony_ci more_mul_max = min(more_mul_max, 1988c2ecf20Sopenharmony_ci DIV_ROUND_UP(limits->max_pll_multiplier, mul)); 1998c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n", 2008c2ecf20Sopenharmony_ci more_mul_max); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Ensure we won't go below min_pll_op_freq_hz. */ 2038c2ecf20Sopenharmony_ci more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, 2048c2ecf20Sopenharmony_ci pll->ext_clk_freq_hz / pll->pre_pll_clk_div 2058c2ecf20Sopenharmony_ci * mul); 2068c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %u\n", 2078c2ecf20Sopenharmony_ci more_mul_min); 2088c2ecf20Sopenharmony_ci /* Ensure we won't go below min_pll_multiplier. */ 2098c2ecf20Sopenharmony_ci more_mul_min = max(more_mul_min, 2108c2ecf20Sopenharmony_ci DIV_ROUND_UP(limits->min_pll_multiplier, mul)); 2118c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %u\n", 2128c2ecf20Sopenharmony_ci more_mul_min); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (more_mul_min > more_mul_max) { 2158c2ecf20Sopenharmony_ci dev_dbg(dev, 2168c2ecf20Sopenharmony_ci "unable to compute more_mul_min and more_mul_max\n"); 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; 2218c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor); 2228c2ecf20Sopenharmony_ci more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div); 2238c2ecf20Sopenharmony_ci dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", 2248c2ecf20Sopenharmony_ci more_mul_factor); 2258c2ecf20Sopenharmony_ci i = roundup(more_mul_min, more_mul_factor); 2268c2ecf20Sopenharmony_ci if (!is_one_or_even(i)) 2278c2ecf20Sopenharmony_ci i <<= 1; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci dev_dbg(dev, "final more_mul: %u\n", i); 2308c2ecf20Sopenharmony_ci if (i > more_mul_max) { 2318c2ecf20Sopenharmony_ci dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); 2328c2ecf20Sopenharmony_ci return -EINVAL; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci pll->pll_multiplier = mul * i; 2368c2ecf20Sopenharmony_ci op_pll->sys_clk_div = div * i / pll->pre_pll_clk_div; 2378c2ecf20Sopenharmony_ci dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll->sys_clk_div); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz 2408c2ecf20Sopenharmony_ci / pll->pre_pll_clk_div; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz 2438c2ecf20Sopenharmony_ci * pll->pll_multiplier; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Derive pll_op_clk_freq_hz. */ 2468c2ecf20Sopenharmony_ci op_pll->sys_clk_freq_hz = 2478c2ecf20Sopenharmony_ci pll->pll_op_clk_freq_hz / op_pll->sys_clk_div; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci op_pll->pix_clk_div = pll->bits_per_pixel; 2508c2ecf20Sopenharmony_ci dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll->pix_clk_div); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci op_pll->pix_clk_freq_hz = 2538c2ecf20Sopenharmony_ci op_pll->sys_clk_freq_hz / op_pll->pix_clk_div; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { 2568c2ecf20Sopenharmony_ci /* No OP clocks --- VT clocks are used instead. */ 2578c2ecf20Sopenharmony_ci goto out_skip_vt_calc; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * Some sensors perform analogue binning and some do this 2628c2ecf20Sopenharmony_ci * digitally. The ones doing this digitally can be roughly be 2638c2ecf20Sopenharmony_ci * found out using this formula. The ones doing this digitally 2648c2ecf20Sopenharmony_ci * should run at higher clock rate, so smaller divisor is used 2658c2ecf20Sopenharmony_ci * on video timing side. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci if (limits->min_line_length_pck_bin > limits->min_line_length_pck 2688c2ecf20Sopenharmony_ci / pll->binning_horizontal) 2698c2ecf20Sopenharmony_ci vt_op_binning_div = pll->binning_horizontal; 2708c2ecf20Sopenharmony_ci else 2718c2ecf20Sopenharmony_ci vt_op_binning_div = 1; 2728c2ecf20Sopenharmony_ci dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Profile 2 supports vt_pix_clk_div E [4, 10] 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * Horizontal binning can be used as a base for difference in 2788c2ecf20Sopenharmony_ci * divisors. One must make sure that horizontal blanking is 2798c2ecf20Sopenharmony_ci * enough to accommodate the CSI-2 sync codes. 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * Take scaling factor into account as well. 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * Find absolute limits for the factor of vt divider. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci dev_dbg(dev, "scale_m: %u\n", pll->scale_m); 2868c2ecf20Sopenharmony_ci min_vt_div = DIV_ROUND_UP(op_pll->pix_clk_div * op_pll->sys_clk_div 2878c2ecf20Sopenharmony_ci * pll->scale_n, 2888c2ecf20Sopenharmony_ci lane_op_clock_ratio * vt_op_binning_div 2898c2ecf20Sopenharmony_ci * pll->scale_m); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Find smallest and biggest allowed vt divisor. */ 2928c2ecf20Sopenharmony_ci dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); 2938c2ecf20Sopenharmony_ci min_vt_div = max(min_vt_div, 2948c2ecf20Sopenharmony_ci DIV_ROUND_UP(pll->pll_op_clk_freq_hz, 2958c2ecf20Sopenharmony_ci limits->vt.max_pix_clk_freq_hz)); 2968c2ecf20Sopenharmony_ci dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", 2978c2ecf20Sopenharmony_ci min_vt_div); 2988c2ecf20Sopenharmony_ci min_vt_div = max_t(uint32_t, min_vt_div, 2998c2ecf20Sopenharmony_ci limits->vt.min_pix_clk_div 3008c2ecf20Sopenharmony_ci * limits->vt.min_sys_clk_div); 3018c2ecf20Sopenharmony_ci dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div; 3048c2ecf20Sopenharmony_ci dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); 3058c2ecf20Sopenharmony_ci max_vt_div = min(max_vt_div, 3068c2ecf20Sopenharmony_ci DIV_ROUND_UP(pll->pll_op_clk_freq_hz, 3078c2ecf20Sopenharmony_ci limits->vt.min_pix_clk_freq_hz)); 3088c2ecf20Sopenharmony_ci dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", 3098c2ecf20Sopenharmony_ci max_vt_div); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * Find limitsits for sys_clk_div. Not all values are possible 3138c2ecf20Sopenharmony_ci * with all values of pix_clk_div. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci min_sys_div = limits->vt.min_sys_clk_div; 3168c2ecf20Sopenharmony_ci dev_dbg(dev, "min_sys_div: %u\n", min_sys_div); 3178c2ecf20Sopenharmony_ci min_sys_div = max(min_sys_div, 3188c2ecf20Sopenharmony_ci DIV_ROUND_UP(min_vt_div, 3198c2ecf20Sopenharmony_ci limits->vt.max_pix_clk_div)); 3208c2ecf20Sopenharmony_ci dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div); 3218c2ecf20Sopenharmony_ci min_sys_div = max(min_sys_div, 3228c2ecf20Sopenharmony_ci pll->pll_op_clk_freq_hz 3238c2ecf20Sopenharmony_ci / limits->vt.max_sys_clk_freq_hz); 3248c2ecf20Sopenharmony_ci dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div); 3258c2ecf20Sopenharmony_ci min_sys_div = clk_div_even_up(min_sys_div); 3268c2ecf20Sopenharmony_ci dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci max_sys_div = limits->vt.max_sys_clk_div; 3298c2ecf20Sopenharmony_ci dev_dbg(dev, "max_sys_div: %u\n", max_sys_div); 3308c2ecf20Sopenharmony_ci max_sys_div = min(max_sys_div, 3318c2ecf20Sopenharmony_ci DIV_ROUND_UP(max_vt_div, 3328c2ecf20Sopenharmony_ci limits->vt.min_pix_clk_div)); 3338c2ecf20Sopenharmony_ci dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div); 3348c2ecf20Sopenharmony_ci max_sys_div = min(max_sys_div, 3358c2ecf20Sopenharmony_ci DIV_ROUND_UP(pll->pll_op_clk_freq_hz, 3368c2ecf20Sopenharmony_ci limits->vt.min_pix_clk_freq_hz)); 3378c2ecf20Sopenharmony_ci dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * Find pix_div such that a legal pix_div * sys_div results 3418c2ecf20Sopenharmony_ci * into a value which is not smaller than div, the desired 3428c2ecf20Sopenharmony_ci * divisor. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci for (vt_div = min_vt_div; vt_div <= max_vt_div; 3458c2ecf20Sopenharmony_ci vt_div += 2 - (vt_div & 1)) { 3468c2ecf20Sopenharmony_ci for (sys_div = min_sys_div; 3478c2ecf20Sopenharmony_ci sys_div <= max_sys_div; 3488c2ecf20Sopenharmony_ci sys_div += 2 - (sys_div & 1)) { 3498c2ecf20Sopenharmony_ci uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (pix_div < limits->vt.min_pix_clk_div 3528c2ecf20Sopenharmony_ci || pix_div > limits->vt.max_pix_clk_div) { 3538c2ecf20Sopenharmony_ci dev_dbg(dev, 3548c2ecf20Sopenharmony_ci "pix_div %u too small or too big (%u--%u)\n", 3558c2ecf20Sopenharmony_ci pix_div, 3568c2ecf20Sopenharmony_ci limits->vt.min_pix_clk_div, 3578c2ecf20Sopenharmony_ci limits->vt.max_pix_clk_div); 3588c2ecf20Sopenharmony_ci continue; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Check if this one is better. */ 3628c2ecf20Sopenharmony_ci if (pix_div * sys_div 3638c2ecf20Sopenharmony_ci <= roundup(min_vt_div, best_pix_div)) 3648c2ecf20Sopenharmony_ci best_pix_div = pix_div; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci if (best_pix_div < INT_MAX >> 1) 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci pll->vt.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); 3718c2ecf20Sopenharmony_ci pll->vt.pix_clk_div = best_pix_div; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci pll->vt.sys_clk_freq_hz = 3748c2ecf20Sopenharmony_ci pll->pll_op_clk_freq_hz / pll->vt.sys_clk_div; 3758c2ecf20Sopenharmony_ci pll->vt.pix_clk_freq_hz = 3768c2ecf20Sopenharmony_ci pll->vt.sys_clk_freq_hz / pll->vt.pix_clk_div; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ciout_skip_vt_calc: 3798c2ecf20Sopenharmony_ci pll->pixel_rate_csi = 3808c2ecf20Sopenharmony_ci op_pll->pix_clk_freq_hz * lane_op_clock_ratio; 3818c2ecf20Sopenharmony_ci pll->pixel_rate_pixel_array = pll->vt.pix_clk_freq_hz; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return check_all_bounds(dev, limits, op_limits, pll, op_pll); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciint smiapp_pll_calculate(struct device *dev, 3878c2ecf20Sopenharmony_ci const struct smiapp_pll_limits *limits, 3888c2ecf20Sopenharmony_ci struct smiapp_pll *pll) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci const struct smiapp_pll_branch_limits *op_limits = &limits->op; 3918c2ecf20Sopenharmony_ci struct smiapp_pll_branch *op_pll = &pll->op; 3928c2ecf20Sopenharmony_ci uint16_t min_pre_pll_clk_div; 3938c2ecf20Sopenharmony_ci uint16_t max_pre_pll_clk_div; 3948c2ecf20Sopenharmony_ci uint32_t lane_op_clock_ratio; 3958c2ecf20Sopenharmony_ci uint32_t mul, div; 3968c2ecf20Sopenharmony_ci unsigned int i; 3978c2ecf20Sopenharmony_ci int rval = -EINVAL; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * If there's no OP PLL at all, use the VT values 4028c2ecf20Sopenharmony_ci * instead. The OP values are ignored for the rest of 4038c2ecf20Sopenharmony_ci * the PLL calculation. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci op_limits = &limits->vt; 4068c2ecf20Sopenharmony_ci op_pll = &pll->vt; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) 4108c2ecf20Sopenharmony_ci lane_op_clock_ratio = pll->csi2.lanes; 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci lane_op_clock_ratio = 1; 4138c2ecf20Sopenharmony_ci dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, 4168c2ecf20Sopenharmony_ci pll->binning_vertical); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci switch (pll->bus_type) { 4198c2ecf20Sopenharmony_ci case SMIAPP_PLL_BUS_TYPE_CSI2: 4208c2ecf20Sopenharmony_ci /* CSI transfers 2 bits per clock per lane; thus times 2 */ 4218c2ecf20Sopenharmony_ci pll->pll_op_clk_freq_hz = pll->link_freq * 2 4228c2ecf20Sopenharmony_ci * (pll->csi2.lanes / lane_op_clock_ratio); 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci case SMIAPP_PLL_BUS_TYPE_PARALLEL: 4258c2ecf20Sopenharmony_ci pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel 4268c2ecf20Sopenharmony_ci / DIV_ROUND_UP(pll->bits_per_pixel, 4278c2ecf20Sopenharmony_ci pll->parallel.bus_width); 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci default: 4308c2ecf20Sopenharmony_ci return -EINVAL; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* Figure out limits for pre-pll divider based on extclk */ 4348c2ecf20Sopenharmony_ci dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n", 4358c2ecf20Sopenharmony_ci limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); 4368c2ecf20Sopenharmony_ci max_pre_pll_clk_div = 4378c2ecf20Sopenharmony_ci min_t(uint16_t, limits->max_pre_pll_clk_div, 4388c2ecf20Sopenharmony_ci clk_div_even(pll->ext_clk_freq_hz / 4398c2ecf20Sopenharmony_ci limits->min_pll_ip_freq_hz)); 4408c2ecf20Sopenharmony_ci min_pre_pll_clk_div = 4418c2ecf20Sopenharmony_ci max_t(uint16_t, limits->min_pre_pll_clk_div, 4428c2ecf20Sopenharmony_ci clk_div_even_up( 4438c2ecf20Sopenharmony_ci DIV_ROUND_UP(pll->ext_clk_freq_hz, 4448c2ecf20Sopenharmony_ci limits->max_pll_ip_freq_hz))); 4458c2ecf20Sopenharmony_ci dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n", 4468c2ecf20Sopenharmony_ci min_pre_pll_clk_div, max_pre_pll_clk_div); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); 4498c2ecf20Sopenharmony_ci mul = div_u64(pll->pll_op_clk_freq_hz, i); 4508c2ecf20Sopenharmony_ci div = pll->ext_clk_freq_hz / i; 4518c2ecf20Sopenharmony_ci dev_dbg(dev, "mul %u / div %u\n", mul, div); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci min_pre_pll_clk_div = 4548c2ecf20Sopenharmony_ci max_t(uint16_t, min_pre_pll_clk_div, 4558c2ecf20Sopenharmony_ci clk_div_even_up( 4568c2ecf20Sopenharmony_ci DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, 4578c2ecf20Sopenharmony_ci limits->max_pll_op_freq_hz))); 4588c2ecf20Sopenharmony_ci dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n", 4598c2ecf20Sopenharmony_ci min_pre_pll_clk_div, max_pre_pll_clk_div); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci for (pll->pre_pll_clk_div = min_pre_pll_clk_div; 4628c2ecf20Sopenharmony_ci pll->pre_pll_clk_div <= max_pre_pll_clk_div; 4638c2ecf20Sopenharmony_ci pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { 4648c2ecf20Sopenharmony_ci rval = __smiapp_pll_calculate(dev, limits, op_limits, pll, 4658c2ecf20Sopenharmony_ci op_pll, mul, div, 4668c2ecf20Sopenharmony_ci lane_op_clock_ratio); 4678c2ecf20Sopenharmony_ci if (rval) 4688c2ecf20Sopenharmony_ci continue; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci print_pll(dev, pll); 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci dev_dbg(dev, "unable to compute pre_pll divisor\n"); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return rval; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smiapp_pll_calculate); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>"); 4818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); 4828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 483