xref: /openbmc/linux/drivers/media/i2c/ccs-pll.c (revision 38c94eb8d7aa60e32ed6da9e4ecd4b5a1597760e)
19e05bbacSSakari Ailus // SPDX-License-Identifier: GPL-2.0-only
29e05bbacSSakari Ailus /*
39e05bbacSSakari Ailus  * drivers/media/i2c/ccs-pll.c
49e05bbacSSakari Ailus  *
59e05bbacSSakari Ailus  * Generic MIPI CCS/SMIA/SMIA++ PLL calculator
69e05bbacSSakari Ailus  *
79e05bbacSSakari Ailus  * Copyright (C) 2020 Intel Corporation
89e05bbacSSakari Ailus  * Copyright (C) 2011--2012 Nokia Corporation
97389d01cSSakari Ailus  * Contact: Sakari Ailus <sakari.ailus@linux.intel.com>
109e05bbacSSakari Ailus  */
119e05bbacSSakari Ailus 
129e05bbacSSakari Ailus #include <linux/device.h>
139e05bbacSSakari Ailus #include <linux/gcd.h>
149e05bbacSSakari Ailus #include <linux/lcm.h>
159e05bbacSSakari Ailus #include <linux/module.h>
169e05bbacSSakari Ailus 
179e05bbacSSakari Ailus #include "ccs-pll.h"
189e05bbacSSakari Ailus 
199e05bbacSSakari Ailus /* Return an even number or one. */
209e05bbacSSakari Ailus static inline uint32_t clk_div_even(uint32_t a)
219e05bbacSSakari Ailus {
229e05bbacSSakari Ailus 	return max_t(uint32_t, 1, a & ~1);
239e05bbacSSakari Ailus }
249e05bbacSSakari Ailus 
259e05bbacSSakari Ailus /* Return an even number or one. */
269e05bbacSSakari Ailus static inline uint32_t clk_div_even_up(uint32_t a)
279e05bbacSSakari Ailus {
289e05bbacSSakari Ailus 	if (a == 1)
299e05bbacSSakari Ailus 		return 1;
309e05bbacSSakari Ailus 	return (a + 1) & ~1;
319e05bbacSSakari Ailus }
329e05bbacSSakari Ailus 
339e05bbacSSakari Ailus static inline uint32_t is_one_or_even(uint32_t a)
349e05bbacSSakari Ailus {
359e05bbacSSakari Ailus 	if (a == 1)
369e05bbacSSakari Ailus 		return 1;
379e05bbacSSakari Ailus 	if (a & 1)
389e05bbacSSakari Ailus 		return 0;
399e05bbacSSakari Ailus 
409e05bbacSSakari Ailus 	return 1;
419e05bbacSSakari Ailus }
429e05bbacSSakari Ailus 
43482e75e7SSakari Ailus static inline uint32_t one_or_more(uint32_t a)
44482e75e7SSakari Ailus {
45482e75e7SSakari Ailus 	return a ?: 1;
46482e75e7SSakari Ailus }
47482e75e7SSakari Ailus 
489e05bbacSSakari Ailus static int bounds_check(struct device *dev, uint32_t val,
499e05bbacSSakari Ailus 			uint32_t min, uint32_t max, char *str)
509e05bbacSSakari Ailus {
519e05bbacSSakari Ailus 	if (val >= min && val <= max)
529e05bbacSSakari Ailus 		return 0;
539e05bbacSSakari Ailus 
549e05bbacSSakari Ailus 	dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
559e05bbacSSakari Ailus 
569e05bbacSSakari Ailus 	return -EINVAL;
579e05bbacSSakari Ailus }
589e05bbacSSakari Ailus 
599e05bbacSSakari Ailus static void print_pll(struct device *dev, struct ccs_pll *pll)
609e05bbacSSakari Ailus {
61415ddd99SSakari Ailus 	dev_dbg(dev, "pre_pll_clk_div\t%u\n",  pll->vt_fr.pre_pll_clk_div);
62415ddd99SSakari Ailus 	dev_dbg(dev, "pll_multiplier \t%u\n",  pll->vt_fr.pll_multiplier);
639e05bbacSSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)) {
64415ddd99SSakari Ailus 		dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op_bk.sys_clk_div);
65415ddd99SSakari Ailus 		dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op_bk.pix_clk_div);
669e05bbacSSakari Ailus 	}
67415ddd99SSakari Ailus 	dev_dbg(dev, "vt_sys_clk_div \t%u\n",  pll->vt_bk.sys_clk_div);
68415ddd99SSakari Ailus 	dev_dbg(dev, "vt_pix_clk_div \t%u\n",  pll->vt_bk.pix_clk_div);
699e05bbacSSakari Ailus 
709e05bbacSSakari Ailus 	dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz);
71415ddd99SSakari Ailus 	dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->vt_fr.pll_ip_clk_freq_hz);
72415ddd99SSakari Ailus 	dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->vt_fr.pll_op_clk_freq_hz);
739e05bbacSSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)) {
749e05bbacSSakari Ailus 		dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n",
75415ddd99SSakari Ailus 			pll->op_bk.sys_clk_freq_hz);
769e05bbacSSakari Ailus 		dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n",
77415ddd99SSakari Ailus 			pll->op_bk.pix_clk_freq_hz);
789e05bbacSSakari Ailus 	}
79415ddd99SSakari Ailus 	dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt_bk.sys_clk_freq_hz);
80415ddd99SSakari Ailus 	dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt_bk.pix_clk_freq_hz);
819e05bbacSSakari Ailus }
829e05bbacSSakari Ailus 
839e05bbacSSakari Ailus static int check_all_bounds(struct device *dev,
84415ddd99SSakari Ailus 			    const struct ccs_pll_limits *lim,
85415ddd99SSakari Ailus 			    const struct ccs_pll_branch_limits_fr *op_lim_fr,
86415ddd99SSakari Ailus 			    const struct ccs_pll_branch_limits_bk *op_lim_bk,
87415ddd99SSakari Ailus 			    struct ccs_pll *pll,
88415ddd99SSakari Ailus 			    struct ccs_pll_branch_fr *op_pll_fr,
89415ddd99SSakari Ailus 			    struct ccs_pll_branch_bk *op_pll_bk)
909e05bbacSSakari Ailus {
919e05bbacSSakari Ailus 	int rval;
929e05bbacSSakari Ailus 
93415ddd99SSakari Ailus 	rval = bounds_check(dev, op_pll_fr->pll_ip_clk_freq_hz,
94415ddd99SSakari Ailus 			    op_lim_fr->min_pll_ip_clk_freq_hz,
95415ddd99SSakari Ailus 			    op_lim_fr->max_pll_ip_clk_freq_hz,
969e05bbacSSakari Ailus 			    "pll_ip_clk_freq_hz");
979e05bbacSSakari Ailus 	if (!rval)
989e05bbacSSakari Ailus 		rval = bounds_check(
99415ddd99SSakari Ailus 			dev, op_pll_fr->pll_multiplier,
100415ddd99SSakari Ailus 			op_lim_fr->min_pll_multiplier,
101415ddd99SSakari Ailus 			op_lim_fr->max_pll_multiplier, "pll_multiplier");
1029e05bbacSSakari Ailus 	if (!rval)
1039e05bbacSSakari Ailus 		rval = bounds_check(
104415ddd99SSakari Ailus 			dev, op_pll_fr->pll_op_clk_freq_hz,
105415ddd99SSakari Ailus 			op_lim_fr->min_pll_op_clk_freq_hz,
106415ddd99SSakari Ailus 			op_lim_fr->max_pll_op_clk_freq_hz, "pll_op_clk_freq_hz");
1079e05bbacSSakari Ailus 	if (!rval)
1089e05bbacSSakari Ailus 		rval = bounds_check(
109415ddd99SSakari Ailus 			dev, op_pll_bk->sys_clk_div,
110415ddd99SSakari Ailus 			op_lim_bk->min_sys_clk_div, op_lim_bk->max_sys_clk_div,
1119e05bbacSSakari Ailus 			"op_sys_clk_div");
1129e05bbacSSakari Ailus 	if (!rval)
1139e05bbacSSakari Ailus 		rval = bounds_check(
114415ddd99SSakari Ailus 			dev, op_pll_bk->sys_clk_freq_hz,
115415ddd99SSakari Ailus 			op_lim_bk->min_sys_clk_freq_hz,
116415ddd99SSakari Ailus 			op_lim_bk->max_sys_clk_freq_hz,
1179e05bbacSSakari Ailus 			"op_sys_clk_freq_hz");
1189e05bbacSSakari Ailus 	if (!rval)
1199e05bbacSSakari Ailus 		rval = bounds_check(
120415ddd99SSakari Ailus 			dev, op_pll_bk->pix_clk_freq_hz,
121415ddd99SSakari Ailus 			op_lim_bk->min_pix_clk_freq_hz,
122415ddd99SSakari Ailus 			op_lim_bk->max_pix_clk_freq_hz,
1239e05bbacSSakari Ailus 			"op_pix_clk_freq_hz");
1249e05bbacSSakari Ailus 
1259e05bbacSSakari Ailus 	/*
1269e05bbacSSakari Ailus 	 * If there are no OP clocks, the VT clocks are contained in
1279e05bbacSSakari Ailus 	 * the OP clock struct.
1289e05bbacSSakari Ailus 	 */
1299e05bbacSSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
1309e05bbacSSakari Ailus 		return rval;
1319e05bbacSSakari Ailus 
1329e05bbacSSakari Ailus 	if (!rval)
1339e05bbacSSakari Ailus 		rval = bounds_check(
134415ddd99SSakari Ailus 			dev, pll->vt_bk.sys_clk_freq_hz,
135415ddd99SSakari Ailus 			lim->vt_bk.min_sys_clk_freq_hz,
136415ddd99SSakari Ailus 			lim->vt_bk.max_sys_clk_freq_hz,
1379e05bbacSSakari Ailus 			"vt_sys_clk_freq_hz");
1389e05bbacSSakari Ailus 	if (!rval)
1399e05bbacSSakari Ailus 		rval = bounds_check(
140415ddd99SSakari Ailus 			dev, pll->vt_bk.pix_clk_freq_hz,
141415ddd99SSakari Ailus 			lim->vt_bk.min_pix_clk_freq_hz,
142415ddd99SSakari Ailus 			lim->vt_bk.max_pix_clk_freq_hz,
1439e05bbacSSakari Ailus 			"vt_pix_clk_freq_hz");
1449e05bbacSSakari Ailus 
145*38c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) &&
146*38c94eb8SSakari Ailus 	    pll->pixel_rate_pixel_array > pll->pixel_rate_csi) {
147*38c94eb8SSakari Ailus 		dev_dbg(dev, "device does not support derating\n");
148*38c94eb8SSakari Ailus 		return -EINVAL;
149*38c94eb8SSakari Ailus 	}
150*38c94eb8SSakari Ailus 
151*38c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_OVERRATING) &&
152*38c94eb8SSakari Ailus 	    pll->pixel_rate_pixel_array < pll->pixel_rate_csi) {
153*38c94eb8SSakari Ailus 		dev_dbg(dev, "device does not support overrating\n");
154*38c94eb8SSakari Ailus 		return -EINVAL;
155*38c94eb8SSakari Ailus 	}
156*38c94eb8SSakari Ailus 
1579e05bbacSSakari Ailus 	return rval;
1589e05bbacSSakari Ailus }
1599e05bbacSSakari Ailus 
1608030aa4fSSakari Ailus #define CPHY_CONST		7
1618030aa4fSSakari Ailus #define DPHY_CONST		16
1628030aa4fSSakari Ailus #define PHY_CONST_DIV		16
1638030aa4fSSakari Ailus 
1643e2db036SSakari Ailus static void
1653e2db036SSakari Ailus __ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
1663e2db036SSakari Ailus 		       const struct ccs_pll_branch_limits_bk *op_lim_bk,
1673e2db036SSakari Ailus 		       struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
1683e2db036SSakari Ailus 		       struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
1693e2db036SSakari Ailus 		       uint32_t phy_const)
1703e2db036SSakari Ailus {
1713e2db036SSakari Ailus 	uint32_t sys_div;
1723e2db036SSakari Ailus 	uint32_t best_pix_div = INT_MAX >> 1;
1733e2db036SSakari Ailus 	uint32_t vt_op_binning_div;
1743e2db036SSakari Ailus 	uint32_t min_vt_div, max_vt_div, vt_div;
1753e2db036SSakari Ailus 	uint32_t min_sys_div, max_sys_div;
1763e2db036SSakari Ailus 
1773e2db036SSakari Ailus 	/*
178*38c94eb8SSakari Ailus 	 * Find out whether a sensor supports derating. If it does not, VT and
179*38c94eb8SSakari Ailus 	 * OP domains are required to run at the same pixel rate.
180*38c94eb8SSakari Ailus 	 */
181*38c94eb8SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) {
182*38c94eb8SSakari Ailus 		min_vt_div =
183*38c94eb8SSakari Ailus 			op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div
184*38c94eb8SSakari Ailus 			* pll->vt_lanes * phy_const
185*38c94eb8SSakari Ailus 			/ pll->op_lanes / PHY_CONST_DIV;
186*38c94eb8SSakari Ailus 	} else {
187*38c94eb8SSakari Ailus 		/*
1883e2db036SSakari Ailus 		 * Some sensors perform analogue binning and some do this
1893e2db036SSakari Ailus 		 * digitally. The ones doing this digitally can be roughly be
1903e2db036SSakari Ailus 		 * found out using this formula. The ones doing this digitally
1913e2db036SSakari Ailus 		 * should run at higher clock rate, so smaller divisor is used
1923e2db036SSakari Ailus 		 * on video timing side.
1933e2db036SSakari Ailus 		 */
1943e2db036SSakari Ailus 		if (lim->min_line_length_pck_bin > lim->min_line_length_pck
1953e2db036SSakari Ailus 		    / pll->binning_horizontal)
1963e2db036SSakari Ailus 			vt_op_binning_div = pll->binning_horizontal;
1973e2db036SSakari Ailus 		else
1983e2db036SSakari Ailus 			vt_op_binning_div = 1;
1993e2db036SSakari Ailus 		dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div);
2003e2db036SSakari Ailus 
2013e2db036SSakari Ailus 		/*
2023e2db036SSakari Ailus 		 * Profile 2 supports vt_pix_clk_div E [4, 10]
2033e2db036SSakari Ailus 		 *
2043e2db036SSakari Ailus 		 * Horizontal binning can be used as a base for difference in
2053e2db036SSakari Ailus 		 * divisors. One must make sure that horizontal blanking is
2063e2db036SSakari Ailus 		 * enough to accommodate the CSI-2 sync codes.
2073e2db036SSakari Ailus 		 *
2083e2db036SSakari Ailus 		 * Take scaling factor and number of VT lanes into account as well.
2093e2db036SSakari Ailus 		 *
2103e2db036SSakari Ailus 		 * Find absolute limits for the factor of vt divider.
2113e2db036SSakari Ailus 		 */
2123e2db036SSakari Ailus 		dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
213*38c94eb8SSakari Ailus 		min_vt_div =
214*38c94eb8SSakari Ailus 			DIV_ROUND_UP(pll->bits_per_pixel
215*38c94eb8SSakari Ailus 				     * op_pll_bk->sys_clk_div * pll->scale_n
216*38c94eb8SSakari Ailus 				     * pll->vt_lanes * phy_const,
217*38c94eb8SSakari Ailus 				     (pll->flags &
218*38c94eb8SSakari Ailus 				      CCS_PLL_FLAG_LANE_SPEED_MODEL ?
2193e2db036SSakari Ailus 				      pll->csi2.lanes : 1)
2203e2db036SSakari Ailus 				     * vt_op_binning_div * pll->scale_m
2213e2db036SSakari Ailus 				     * PHY_CONST_DIV);
222*38c94eb8SSakari Ailus 	}
2233e2db036SSakari Ailus 
2243e2db036SSakari Ailus 	/* Find smallest and biggest allowed vt divisor. */
2253e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
2263e2db036SSakari Ailus 	min_vt_div = max(min_vt_div,
2273e2db036SSakari Ailus 			 DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
2283e2db036SSakari Ailus 				      lim->vt_bk.max_pix_clk_freq_hz));
2293e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n",
2303e2db036SSakari Ailus 		min_vt_div);
2313e2db036SSakari Ailus 	min_vt_div = max_t(uint32_t, min_vt_div,
2323e2db036SSakari Ailus 			   lim->vt_bk.min_pix_clk_div
2333e2db036SSakari Ailus 			   * lim->vt_bk.min_sys_clk_div);
2343e2db036SSakari Ailus 	dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div);
2353e2db036SSakari Ailus 
2363e2db036SSakari Ailus 	max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.max_pix_clk_div;
2373e2db036SSakari Ailus 	dev_dbg(dev, "max_vt_div: %u\n", max_vt_div);
2383e2db036SSakari Ailus 	max_vt_div = min(max_vt_div,
2393e2db036SSakari Ailus 			 DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
2403e2db036SSakari Ailus 				      lim->vt_bk.min_pix_clk_freq_hz));
2413e2db036SSakari Ailus 	dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
2423e2db036SSakari Ailus 		max_vt_div);
2433e2db036SSakari Ailus 
2443e2db036SSakari Ailus 	/*
2453e2db036SSakari Ailus 	 * Find limitsits for sys_clk_div. Not all values are possible
2463e2db036SSakari Ailus 	 * with all values of pix_clk_div.
2473e2db036SSakari Ailus 	 */
2483e2db036SSakari Ailus 	min_sys_div = lim->vt_bk.min_sys_clk_div;
2493e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: %u\n", min_sys_div);
2503e2db036SSakari Ailus 	min_sys_div = max(min_sys_div,
2513e2db036SSakari Ailus 			  DIV_ROUND_UP(min_vt_div,
2523e2db036SSakari Ailus 				       lim->vt_bk.max_pix_clk_div));
2533e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div);
2543e2db036SSakari Ailus 	min_sys_div = max(min_sys_div,
2553e2db036SSakari Ailus 			  pll_fr->pll_op_clk_freq_hz
2563e2db036SSakari Ailus 			  / lim->vt_bk.max_sys_clk_freq_hz);
2573e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div);
2583e2db036SSakari Ailus 	min_sys_div = clk_div_even_up(min_sys_div);
2593e2db036SSakari Ailus 	dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div);
2603e2db036SSakari Ailus 
2613e2db036SSakari Ailus 	max_sys_div = lim->vt_bk.max_sys_clk_div;
2623e2db036SSakari Ailus 	dev_dbg(dev, "max_sys_div: %u\n", max_sys_div);
2633e2db036SSakari Ailus 	max_sys_div = min(max_sys_div,
2643e2db036SSakari Ailus 			  DIV_ROUND_UP(max_vt_div,
2653e2db036SSakari Ailus 				       lim->vt_bk.min_pix_clk_div));
2663e2db036SSakari Ailus 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div);
2673e2db036SSakari Ailus 	max_sys_div = min(max_sys_div,
2683e2db036SSakari Ailus 			  DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
2693e2db036SSakari Ailus 				       lim->vt_bk.min_pix_clk_freq_hz));
2703e2db036SSakari Ailus 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
2713e2db036SSakari Ailus 
2723e2db036SSakari Ailus 	/*
2733e2db036SSakari Ailus 	 * Find pix_div such that a legal pix_div * sys_div results
2743e2db036SSakari Ailus 	 * into a value which is not smaller than div, the desired
2753e2db036SSakari Ailus 	 * divisor.
2763e2db036SSakari Ailus 	 */
2773e2db036SSakari Ailus 	for (vt_div = min_vt_div; vt_div <= max_vt_div;
2783e2db036SSakari Ailus 	     vt_div += 2 - (vt_div & 1)) {
2793e2db036SSakari Ailus 		for (sys_div = min_sys_div;
2803e2db036SSakari Ailus 		     sys_div <= max_sys_div;
2813e2db036SSakari Ailus 		     sys_div += 2 - (sys_div & 1)) {
2823e2db036SSakari Ailus 			uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div);
2833e2db036SSakari Ailus 			uint16_t rounded_div;
2843e2db036SSakari Ailus 
2853e2db036SSakari Ailus 			if (pix_div < lim->vt_bk.min_pix_clk_div
2863e2db036SSakari Ailus 			    || pix_div > lim->vt_bk.max_pix_clk_div) {
2873e2db036SSakari Ailus 				dev_dbg(dev,
2883e2db036SSakari Ailus 					"pix_div %u too small or too big (%u--%u)\n",
2893e2db036SSakari Ailus 					pix_div,
2903e2db036SSakari Ailus 					lim->vt_bk.min_pix_clk_div,
2913e2db036SSakari Ailus 					lim->vt_bk.max_pix_clk_div);
2923e2db036SSakari Ailus 				continue;
2933e2db036SSakari Ailus 			}
2943e2db036SSakari Ailus 
2953e2db036SSakari Ailus 			rounded_div = roundup(vt_div, best_pix_div);
2963e2db036SSakari Ailus 
2973e2db036SSakari Ailus 			/* Check if this one is better. */
2983e2db036SSakari Ailus 			if (pix_div * sys_div <= rounded_div)
2993e2db036SSakari Ailus 				best_pix_div = pix_div;
3003e2db036SSakari Ailus 
3013e2db036SSakari Ailus 			/* Bail out if we've already found the best value. */
3023e2db036SSakari Ailus 			if (vt_div == rounded_div)
3033e2db036SSakari Ailus 				break;
3043e2db036SSakari Ailus 		}
3053e2db036SSakari Ailus 		if (best_pix_div < INT_MAX >> 1)
3063e2db036SSakari Ailus 			break;
3073e2db036SSakari Ailus 	}
3083e2db036SSakari Ailus 
3093e2db036SSakari Ailus 	pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div);
3103e2db036SSakari Ailus 	pll->vt_bk.pix_clk_div = best_pix_div;
3113e2db036SSakari Ailus 
3123e2db036SSakari Ailus 	pll->vt_bk.sys_clk_freq_hz =
3133e2db036SSakari Ailus 		pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
3143e2db036SSakari Ailus 	pll->vt_bk.pix_clk_freq_hz =
3153e2db036SSakari Ailus 		pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div;
3163e2db036SSakari Ailus }
3173e2db036SSakari Ailus 
3189e05bbacSSakari Ailus /*
3199e05bbacSSakari Ailus  * Heuristically guess the PLL tree for a given common multiplier and
3209e05bbacSSakari Ailus  * divisor. Begin with the operational timing and continue to video
3219e05bbacSSakari Ailus  * timing once operational timing has been verified.
3229e05bbacSSakari Ailus  *
3239e05bbacSSakari Ailus  * @mul is the PLL multiplier and @div is the common divisor
3249e05bbacSSakari Ailus  * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
3259e05bbacSSakari Ailus  * multiplier will be a multiple of @mul.
3269e05bbacSSakari Ailus  *
3279e05bbacSSakari Ailus  * @return Zero on success, error code on error.
3289e05bbacSSakari Ailus  */
3299e05bbacSSakari Ailus static int
330415ddd99SSakari Ailus __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
331415ddd99SSakari Ailus 		    const struct ccs_pll_branch_limits_fr *op_lim_fr,
332415ddd99SSakari Ailus 		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
333415ddd99SSakari Ailus 		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
334415ddd99SSakari Ailus 		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
3358030aa4fSSakari Ailus 		    uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
3369e05bbacSSakari Ailus {
3379e05bbacSSakari Ailus 	/*
3389e05bbacSSakari Ailus 	 * Higher multipliers (and divisors) are often required than
3399e05bbacSSakari Ailus 	 * necessitated by the external clock and the output clocks.
3409e05bbacSSakari Ailus 	 * There are limits for all values in the clock tree. These
3419e05bbacSSakari Ailus 	 * are the minimum and maximum multiplier for mul.
3429e05bbacSSakari Ailus 	 */
3439e05bbacSSakari Ailus 	uint32_t more_mul_min, more_mul_max;
3449e05bbacSSakari Ailus 	uint32_t more_mul_factor;
345e583e654SSakari Ailus 	uint32_t i;
3469e05bbacSSakari Ailus 
3479e05bbacSSakari Ailus 	/*
3489e05bbacSSakari Ailus 	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
3499e05bbacSSakari Ailus 	 * too high.
3509e05bbacSSakari Ailus 	 */
351415ddd99SSakari Ailus 	dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div);
3529e05bbacSSakari Ailus 
3539e05bbacSSakari Ailus 	/* Don't go above max pll multiplier. */
354415ddd99SSakari Ailus 	more_mul_max = op_lim_fr->max_pll_multiplier / mul;
355415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_max: max_op_pll_multiplier check: %u\n",
3569e05bbacSSakari Ailus 		more_mul_max);
3579e05bbacSSakari Ailus 	/* Don't go above max pll op frequency. */
3589e05bbacSSakari Ailus 	more_mul_max =
3599e05bbacSSakari Ailus 		min_t(uint32_t,
3609e05bbacSSakari Ailus 		      more_mul_max,
361415ddd99SSakari Ailus 		      op_lim_fr->max_pll_op_clk_freq_hz
362ae502e08SSakari Ailus 		      / (pll->ext_clk_freq_hz /
363ae502e08SSakari Ailus 			 op_pll_fr->pre_pll_clk_div * mul));
364415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_max: max_pll_op_clk_freq_hz check: %u\n",
3659e05bbacSSakari Ailus 		more_mul_max);
3669e05bbacSSakari Ailus 	/* Don't go above the division capability of op sys clock divider. */
3679e05bbacSSakari Ailus 	more_mul_max = min(more_mul_max,
368415ddd99SSakari Ailus 			   op_lim_bk->max_sys_clk_div * op_pll_fr->pre_pll_clk_div
3699e05bbacSSakari Ailus 			   / div);
3709e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n",
3719e05bbacSSakari Ailus 		more_mul_max);
372c64cf71dSSakari Ailus 	/* Ensure we won't go above max_pll_multiplier. */
37382ab97c8SSakari Ailus 	more_mul_max = min(more_mul_max, op_lim_fr->max_pll_multiplier / mul);
3749e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
3759e05bbacSSakari Ailus 		more_mul_max);
3769e05bbacSSakari Ailus 
377415ddd99SSakari Ailus 	/* Ensure we won't go below min_pll_op_clk_freq_hz. */
378415ddd99SSakari Ailus 	more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz,
379415ddd99SSakari Ailus 				    pll->ext_clk_freq_hz /
380415ddd99SSakari Ailus 				    op_pll_fr->pre_pll_clk_div * mul);
381415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_freq_hz check: %u\n",
3829e05bbacSSakari Ailus 		more_mul_min);
3839e05bbacSSakari Ailus 	/* Ensure we won't go below min_pll_multiplier. */
3849e05bbacSSakari Ailus 	more_mul_min = max(more_mul_min,
385415ddd99SSakari Ailus 			   DIV_ROUND_UP(op_lim_fr->min_pll_multiplier, mul));
386415ddd99SSakari Ailus 	dev_dbg(dev, "more_mul_min: min_op_pll_multiplier check: %u\n",
3879e05bbacSSakari Ailus 		more_mul_min);
3889e05bbacSSakari Ailus 
3899e05bbacSSakari Ailus 	if (more_mul_min > more_mul_max) {
3909e05bbacSSakari Ailus 		dev_dbg(dev,
3919e05bbacSSakari Ailus 			"unable to compute more_mul_min and more_mul_max\n");
3929e05bbacSSakari Ailus 		return -EINVAL;
3939e05bbacSSakari Ailus 	}
3949e05bbacSSakari Ailus 
395415ddd99SSakari Ailus 	more_mul_factor = lcm(div, op_pll_fr->pre_pll_clk_div) / div;
3969e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
397415ddd99SSakari Ailus 	more_mul_factor = lcm(more_mul_factor, op_lim_bk->min_sys_clk_div);
3989e05bbacSSakari Ailus 	dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
3999e05bbacSSakari Ailus 		more_mul_factor);
4009e05bbacSSakari Ailus 	i = roundup(more_mul_min, more_mul_factor);
4019e05bbacSSakari Ailus 	if (!is_one_or_even(i))
4029e05bbacSSakari Ailus 		i <<= 1;
4039e05bbacSSakari Ailus 
4049e05bbacSSakari Ailus 	dev_dbg(dev, "final more_mul: %u\n", i);
4059e05bbacSSakari Ailus 	if (i > more_mul_max) {
4069e05bbacSSakari Ailus 		dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max);
4079e05bbacSSakari Ailus 		return -EINVAL;
4089e05bbacSSakari Ailus 	}
4099e05bbacSSakari Ailus 
410415ddd99SSakari Ailus 	op_pll_fr->pll_multiplier = mul * i;
411415ddd99SSakari Ailus 	op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div;
412415ddd99SSakari Ailus 	dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div);
4139e05bbacSSakari Ailus 
414415ddd99SSakari Ailus 	op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
415415ddd99SSakari Ailus 		/ op_pll_fr->pre_pll_clk_div;
4169e05bbacSSakari Ailus 
417415ddd99SSakari Ailus 	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
418415ddd99SSakari Ailus 		* op_pll_fr->pll_multiplier;
4199e05bbacSSakari Ailus 
420c4c0b222SSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
421cac8f5d2SSakari Ailus 		op_pll_bk->pix_clk_div = pll->bits_per_pixel
4228030aa4fSSakari Ailus 			* pll->op_lanes * phy_const
4238030aa4fSSakari Ailus 			/ PHY_CONST_DIV / pll->csi2.lanes / l;
424c4c0b222SSakari Ailus 	else
4258030aa4fSSakari Ailus 		op_pll_bk->pix_clk_div =
4268030aa4fSSakari Ailus 			pll->bits_per_pixel * phy_const / PHY_CONST_DIV / l;
427c4c0b222SSakari Ailus 
428415ddd99SSakari Ailus 	op_pll_bk->pix_clk_freq_hz =
429415ddd99SSakari Ailus 		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
430c4c0b222SSakari Ailus 
431cac8f5d2SSakari Ailus 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
432cac8f5d2SSakari Ailus 
4333e2db036SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS))
4343e2db036SSakari Ailus 		__ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
4353e2db036SSakari Ailus 				       op_pll_bk, cphy, phy_const);
4369e05bbacSSakari Ailus 
437cac8f5d2SSakari Ailus 	pll->pixel_rate_pixel_array =
438cac8f5d2SSakari Ailus 		pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
4399e05bbacSSakari Ailus 
440415ddd99SSakari Ailus 	return check_all_bounds(dev, lim, op_lim_fr, op_lim_bk, pll, op_pll_fr,
441415ddd99SSakari Ailus 				op_pll_bk);
4429e05bbacSSakari Ailus }
4439e05bbacSSakari Ailus 
444415ddd99SSakari Ailus int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
4459e05bbacSSakari Ailus 		      struct ccs_pll *pll)
4469e05bbacSSakari Ailus {
447415ddd99SSakari Ailus 	const struct ccs_pll_branch_limits_fr *op_lim_fr = &lim->vt_fr;
448415ddd99SSakari Ailus 	const struct ccs_pll_branch_limits_bk *op_lim_bk = &lim->op_bk;
449415ddd99SSakari Ailus 	struct ccs_pll_branch_fr *op_pll_fr = &pll->vt_fr;
450415ddd99SSakari Ailus 	struct ccs_pll_branch_bk *op_pll_bk = &pll->op_bk;
4518030aa4fSSakari Ailus 	bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY;
4528030aa4fSSakari Ailus 	uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST;
453415ddd99SSakari Ailus 	uint16_t min_op_pre_pll_clk_div;
454415ddd99SSakari Ailus 	uint16_t max_op_pre_pll_clk_div;
4559e05bbacSSakari Ailus 	uint32_t mul, div;
456c4c0b222SSakari Ailus 	uint32_t l = (!pll->op_bits_per_lane ||
457c4c0b222SSakari Ailus 		      pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2;
458e583e654SSakari Ailus 	uint32_t i;
4599e05bbacSSakari Ailus 	int rval = -EINVAL;
4609e05bbacSSakari Ailus 
461cac8f5d2SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) {
462cac8f5d2SSakari Ailus 		pll->op_lanes = 1;
463cac8f5d2SSakari Ailus 		pll->vt_lanes = 1;
464cac8f5d2SSakari Ailus 	}
4659490a227SSakari Ailus 
466d7172c0eSSakari Ailus 	if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel ||
467d7172c0eSSakari Ailus 	    !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m ||
468d7172c0eSSakari Ailus 	    !op_lim_fr->min_pll_ip_clk_freq_hz ||
469d7172c0eSSakari Ailus 	    !op_lim_fr->max_pll_ip_clk_freq_hz ||
470d7172c0eSSakari Ailus 	    !op_lim_fr->min_pll_op_clk_freq_hz ||
471d7172c0eSSakari Ailus 	    !op_lim_fr->max_pll_op_clk_freq_hz ||
472d7172c0eSSakari Ailus 	    !op_lim_bk->max_sys_clk_div || !op_lim_fr->max_pll_multiplier)
473d7172c0eSSakari Ailus 		return -EINVAL;
474d7172c0eSSakari Ailus 
4759490a227SSakari Ailus 	/*
4769490a227SSakari Ailus 	 * Make sure op_pix_clk_div will be integer --- unless flexible
4779490a227SSakari Ailus 	 * op_pix_clk_div is supported
4789490a227SSakari Ailus 	 */
4799490a227SSakari Ailus 	if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) &&
4809490a227SSakari Ailus 	    (pll->bits_per_pixel * pll->op_lanes) % (pll->csi2.lanes * l)) {
4819490a227SSakari Ailus 		dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n",
4829490a227SSakari Ailus 			pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l);
4839490a227SSakari Ailus 		return -EINVAL;
4849490a227SSakari Ailus 	}
4859490a227SSakari Ailus 
486cac8f5d2SSakari Ailus 	dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes);
487cac8f5d2SSakari Ailus 	dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes);
488cac8f5d2SSakari Ailus 
4899e05bbacSSakari Ailus 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
4909e05bbacSSakari Ailus 		/*
4919e05bbacSSakari Ailus 		 * If there's no OP PLL at all, use the VT values
4929e05bbacSSakari Ailus 		 * instead. The OP values are ignored for the rest of
4939e05bbacSSakari Ailus 		 * the PLL calculation.
4949e05bbacSSakari Ailus 		 */
495415ddd99SSakari Ailus 		op_lim_fr = &lim->vt_fr;
496415ddd99SSakari Ailus 		op_lim_bk = &lim->vt_bk;
497415ddd99SSakari Ailus 		op_pll_bk = &pll->vt_bk;
4989e05bbacSSakari Ailus 	}
4999e05bbacSSakari Ailus 
5009e05bbacSSakari Ailus 	dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal,
5019e05bbacSSakari Ailus 		pll->binning_vertical);
5029e05bbacSSakari Ailus 
5039e05bbacSSakari Ailus 	switch (pll->bus_type) {
50447b6eaf3SSakari Ailus 	case CCS_PLL_BUS_TYPE_CSI2_DPHY:
5059e05bbacSSakari Ailus 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
506cab27256SSakari Ailus 		op_pll_bk->sys_clk_freq_hz = pll->link_freq * 2
507cac8f5d2SSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
508ae502e08SSakari Ailus 			   1 : pll->csi2.lanes);
5099e05bbacSSakari Ailus 		break;
5108030aa4fSSakari Ailus 	case CCS_PLL_BUS_TYPE_CSI2_CPHY:
5118030aa4fSSakari Ailus 		op_pll_bk->sys_clk_freq_hz =
5128030aa4fSSakari Ailus 			pll->link_freq
5138030aa4fSSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
5148030aa4fSSakari Ailus 			   1 : pll->csi2.lanes);
5158030aa4fSSakari Ailus 		break;
5169e05bbacSSakari Ailus 	default:
5179e05bbacSSakari Ailus 		return -EINVAL;
5189e05bbacSSakari Ailus 	}
5199e05bbacSSakari Ailus 
520cac8f5d2SSakari Ailus 	pll->pixel_rate_csi =
5218030aa4fSSakari Ailus 		div_u64((uint64_t)op_pll_bk->sys_clk_freq_hz
522cac8f5d2SSakari Ailus 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
5238030aa4fSSakari Ailus 			   pll->csi2.lanes : 1) * PHY_CONST_DIV,
5248030aa4fSSakari Ailus 			phy_const * pll->bits_per_pixel * l);
525cac8f5d2SSakari Ailus 
526415ddd99SSakari Ailus 	/* Figure out limits for OP pre-pll divider based on extclk */
527415ddd99SSakari Ailus 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
528415ddd99SSakari Ailus 		op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div);
529415ddd99SSakari Ailus 	max_op_pre_pll_clk_div =
530415ddd99SSakari Ailus 		min_t(uint16_t, op_lim_fr->max_pre_pll_clk_div,
5319e05bbacSSakari Ailus 		      clk_div_even(pll->ext_clk_freq_hz /
532415ddd99SSakari Ailus 				   op_lim_fr->min_pll_ip_clk_freq_hz));
533415ddd99SSakari Ailus 	min_op_pre_pll_clk_div =
534415ddd99SSakari Ailus 		max_t(uint16_t, op_lim_fr->min_pre_pll_clk_div,
5359e05bbacSSakari Ailus 		      clk_div_even_up(
5369e05bbacSSakari Ailus 			      DIV_ROUND_UP(pll->ext_clk_freq_hz,
537415ddd99SSakari Ailus 					   op_lim_fr->max_pll_ip_clk_freq_hz)));
538415ddd99SSakari Ailus 	dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
539415ddd99SSakari Ailus 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
5409e05bbacSSakari Ailus 
541cab27256SSakari Ailus 	i = gcd(op_pll_bk->sys_clk_freq_hz, pll->ext_clk_freq_hz);
542cab27256SSakari Ailus 	mul = op_pll_bk->sys_clk_freq_hz / i;
5439e05bbacSSakari Ailus 	div = pll->ext_clk_freq_hz / i;
5449e05bbacSSakari Ailus 	dev_dbg(dev, "mul %u / div %u\n", mul, div);
5459e05bbacSSakari Ailus 
546415ddd99SSakari Ailus 	min_op_pre_pll_clk_div =
547415ddd99SSakari Ailus 		max_t(uint16_t, min_op_pre_pll_clk_div,
5489e05bbacSSakari Ailus 		      clk_div_even_up(
549482e75e7SSakari Ailus 			      mul /
550482e75e7SSakari Ailus 			      one_or_more(
551482e75e7SSakari Ailus 				      DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz,
552482e75e7SSakari Ailus 						   pll->ext_clk_freq_hz))));
553415ddd99SSakari Ailus 	dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
554415ddd99SSakari Ailus 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
5559e05bbacSSakari Ailus 
556415ddd99SSakari Ailus 	for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div;
557415ddd99SSakari Ailus 	     op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div;
5584e1e8d24SSakari Ailus 	     op_pll_fr->pre_pll_clk_div +=
5594e1e8d24SSakari Ailus 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
5604e1e8d24SSakari Ailus 		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
561415ddd99SSakari Ailus 		rval = __ccs_pll_calculate(dev, lim, op_lim_fr, op_lim_bk, pll,
5628030aa4fSSakari Ailus 					   op_pll_fr, op_pll_bk, mul, div, l,
5638030aa4fSSakari Ailus 					   cphy, phy_const);
5649e05bbacSSakari Ailus 		if (rval)
5659e05bbacSSakari Ailus 			continue;
5669e05bbacSSakari Ailus 
5679e05bbacSSakari Ailus 		print_pll(dev, pll);
5689e05bbacSSakari Ailus 		return 0;
5699e05bbacSSakari Ailus 	}
5709e05bbacSSakari Ailus 
5719e05bbacSSakari Ailus 	dev_dbg(dev, "unable to compute pre_pll divisor\n");
5729e05bbacSSakari Ailus 
5739e05bbacSSakari Ailus 	return rval;
5749e05bbacSSakari Ailus }
5759e05bbacSSakari Ailus EXPORT_SYMBOL_GPL(ccs_pll_calculate);
5769e05bbacSSakari Ailus 
5777389d01cSSakari Ailus MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
5789e05bbacSSakari Ailus MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator");
579b3c0115eSSakari Ailus MODULE_LICENSE("GPL v2");
580