linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/38] Support additional CCS PLL features, C-PHY
@ 2020-12-02 18:06 Sakari Ailus
  2020-12-02 18:06 ` [PATCH 01/38] ccs-pll: Don't use div_u64 to divide a 32-bit number Sakari Ailus
                   ` (37 more replies)
  0 siblings, 38 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Hello everyone,

Here's a set of patches that turn the existing SMIA driver into a MIPI CCS
driver while maintaining SMIA support. A number of bugs in the existing
code are fixed in this set, too.

The changes at this point are primarily focused on dealing with new
mandatory driver features related to PLL configuration (as CCS allows for
much more variation there) and things such as integer conversion from
U16.U16 format instead of float. There are some other new features as well
such as digital gain and support for getting device specific analogue gain
coefficients.

A new feature in CCS is CCS static data which makes it possible to obtain
sensor's capabilities and limits from a file chosen based on sensor
identification. CCS static data is used also for storing MSR registers so
supporting new, CCS compliant devices requires no driver changes.

Also DT bindings are updated accordingly and converted to YAML format.

More information on MIPI CCS can be found here:

<URL:https://www.mipi.org/specifications/camera-command-set>

Comments are welcome.

since the big, big patchset (v2):

- Split into more easily reviewable chunks (this is the first of maybe
  three). The cover page describes the entire big set. This set contains
  support for additional CCS PLL features as well as making the CCS driver
  support them, including trivial dual PLL and C-PHY support --- as the
  first upstream sensor driver supporting C-PHY.

- Fix kerneldoc comments in CCS PLL documentation.

Sakari Ailus (38):
  ccs-pll: Don't use div_u64 to divide a 32-bit number
  ccs-pll: Split limits and PLL configuration into front and back parts
  ccs-pll: Use correct VT divisor for calculating VT SYS divisor
  ccs-pll: End search if there are no better values available
  ccs-pll: Remove parallel bus support
  ccs-pll: Differentiate between CSI-2 D-PHY and C-PHY
  ccs-pll: Move the flags field down, away from 8-bit fields
  ccs-pll: Document the structs in the header as well as the function
  ccs-pll: Use the BIT macro
  ccs-pll: Begin calculation from OP system clock frequency
  ccs-pll: Fix condition for pre-PLL divider lower bound
  ccs-pll: Avoid overflow in pre-PLL divisor lower bound search
  ccs-pll: Fix comment on check against maximum PLL multiplier
  ccs-pll: Fix check for PLL multiplier upper bound
  ccs-pll: Use explicit 32-bit unsigned type
  ccs-pll: Add support for lane speed model
  ccs: Add support for lane speed model
  ccs-pll: Add support for decoupled OP domain calculation
  ccs-pll: Add support for extended input PLL clock divider
  ccs-pll: Support two cycles per pixel on OP domain
  ccs-pll: Add support flexible OP PLL pixel clock divider
  ccs-pll: Add sanity checks
  ccs-pll: Add C-PHY support
  ccs-pll: Split off VT subtree calculation
  ccs-pll: Check for derating and overrating, support non-derating
    sensors
  ccs-pll: Better separate OP and VT sub-tree calculation
  ccs-pll: Print relevant information on PLL tree
  ccs-pll: Rework bounds checks
  ccs-pll: Make VT divisors 16-bit
  ccs-pll: Fix VT post-PLL divisor calculation
  ccs-pll: Separate VT divisor limit calculation from the rest
  ccs-pll: Add trivial dual PLL support
  ccs: Dual PLL support
  ccs-pll: Add support for DDR OP system and pixel clocks
  ccs: Add support for DDR OP SYS and OP PIX clocks
  ccs: Print written register values
  ccs-pll: Print pixel rates
  ccs: Add support for obtaining C-PHY configuration from firmware

 drivers/media/i2c/ccs-pll.c            | 986 +++++++++++++++++--------
 drivers/media/i2c/ccs-pll.h            | 177 ++++-
 drivers/media/i2c/ccs/ccs-core.c       | 161 +++-
 drivers/media/i2c/ccs/ccs-quirk.c      |   5 +-
 drivers/media/i2c/ccs/ccs-reg-access.c |   4 +
 5 files changed, 974 insertions(+), 359 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH 01/38] ccs-pll: Don't use div_u64 to divide a 32-bit number
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 02/38] ccs-pll: Split limits and PLL configuration into front and back parts Sakari Ailus
                   ` (36 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

pll->pll_op_clk_freq is a 32-bit number. It does not need div_u64 to
divide it.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 0d57bac1599a..1cfe6cf7e51c 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -445,7 +445,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 		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);
+	mul = pll->pll_op_clk_freq_hz / i;
 	div = pll->ext_clk_freq_hz / i;
 	dev_dbg(dev, "mul %u / div %u\n", mul, div);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 02/38] ccs-pll: Split limits and PLL configuration into front and back parts
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
  2020-12-02 18:06 ` [PATCH 01/38] ccs-pll: Don't use div_u64 to divide a 32-bit number Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 03/38] ccs-pll: Use correct VT divisor for calculating VT SYS divisor Sakari Ailus
                   ` (35 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The CCS spec supports a lot of variation in the PLL. Split the PLL in
front and back parts to better prepare for supporting it.

Also use CCS compliant naming for IP and OP PLL frequencies (i.e. include
"clk" in the name).

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c      | 282 ++++++++++++++++---------------
 drivers/media/i2c/ccs-pll.h      |  44 +++--
 drivers/media/i2c/ccs/ccs-core.c |  71 ++++----
 3 files changed, 209 insertions(+), 188 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 1cfe6cf7e51c..b2f0fa14ff92 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -53,65 +53,68 @@ static int bounds_check(struct device *dev, uint32_t val,
 
 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);
+	dev_dbg(dev, "pre_pll_clk_div\t%u\n",  pll->vt_fr.pre_pll_clk_div);
+	dev_dbg(dev, "pll_multiplier \t%u\n",  pll->vt_fr.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, "op_sys_clk_div \t%u\n", pll->op_bk.sys_clk_div);
+		dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op_bk.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, "vt_sys_clk_div \t%u\n",  pll->vt_bk.sys_clk_div);
+	dev_dbg(dev, "vt_pix_clk_div \t%u\n",  pll->vt_bk.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);
+	dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->vt_fr.pll_ip_clk_freq_hz);
+	dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->vt_fr.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);
+			pll->op_bk.sys_clk_freq_hz);
 		dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n",
-			pll->op.pix_clk_freq_hz);
+			pll->op_bk.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);
+	dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt_bk.sys_clk_freq_hz);
+	dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt_bk.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)
+			    const struct ccs_pll_limits *lim,
+			    const struct ccs_pll_branch_limits_fr *op_lim_fr,
+			    const struct ccs_pll_branch_limits_bk *op_lim_bk,
+			    struct ccs_pll *pll,
+			    struct ccs_pll_branch_fr *op_pll_fr,
+			    struct ccs_pll_branch_bk *op_pll_bk)
 {
 	int rval;
 
-	rval = bounds_check(dev, pll->pll_ip_clk_freq_hz,
-			    limits->min_pll_ip_freq_hz,
-			    limits->max_pll_ip_freq_hz,
+	rval = bounds_check(dev, op_pll_fr->pll_ip_clk_freq_hz,
+			    op_lim_fr->min_pll_ip_clk_freq_hz,
+			    op_lim_fr->max_pll_ip_clk_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");
+			dev, op_pll_fr->pll_multiplier,
+			op_lim_fr->min_pll_multiplier,
+			op_lim_fr->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");
+			dev, op_pll_fr->pll_op_clk_freq_hz,
+			op_lim_fr->min_pll_op_clk_freq_hz,
+			op_lim_fr->max_pll_op_clk_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,
+			dev, op_pll_bk->sys_clk_div,
+			op_lim_bk->min_sys_clk_div, op_lim_bk->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,
+			dev, op_pll_bk->sys_clk_freq_hz,
+			op_lim_bk->min_sys_clk_freq_hz,
+			op_lim_bk->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,
+			dev, op_pll_bk->pix_clk_freq_hz,
+			op_lim_bk->min_pix_clk_freq_hz,
+			op_lim_bk->max_pix_clk_freq_hz,
 			"op_pix_clk_freq_hz");
 
 	/*
@@ -123,15 +126,15 @@ static int check_all_bounds(struct device *dev,
 
 	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,
+			dev, pll->vt_bk.sys_clk_freq_hz,
+			lim->vt_bk.min_sys_clk_freq_hz,
+			lim->vt_bk.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,
+			dev, pll->vt_bk.pix_clk_freq_hz,
+			lim->vt_bk.min_pix_clk_freq_hz,
+			lim->vt_bk.max_pix_clk_freq_hz,
 			"vt_pix_clk_freq_hz");
 
 	return rval;
@@ -149,10 +152,12 @@ static int check_all_bounds(struct device *dev,
  * @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)
+__ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
+		    const struct ccs_pll_branch_limits_fr *op_lim_fr,
+		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
+		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
+		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
+		    uint32_t div, uint32_t lane_op_clock_ratio)
 {
 	uint32_t sys_div;
 	uint32_t best_pix_div = INT_MAX >> 1;
@@ -173,42 +178,42 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 	 * 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);
+	dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->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 = op_lim_fr->max_pll_multiplier / mul;
+	dev_dbg(dev, "more_mul_max: max_op_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",
+		      op_lim_fr->max_pll_op_clk_freq_hz
+		      / (pll->ext_clk_freq_hz / op_pll_fr->pre_pll_clk_div * mul));
+	dev_dbg(dev, "more_mul_max: max_pll_op_clk_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
+			   op_lim_bk->max_sys_clk_div * op_pll_fr->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));
+			   DIV_ROUND_UP(op_lim_fr->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",
+	/* Ensure we won't go below min_pll_op_clk_freq_hz. */
+	more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz,
+				    pll->ext_clk_freq_hz /
+				    op_pll_fr->pre_pll_clk_div * mul);
+	dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_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",
+			   DIV_ROUND_UP(op_lim_fr->min_pll_multiplier, mul));
+	dev_dbg(dev, "more_mul_min: min_op_pll_multiplier check: %u\n",
 		more_mul_min);
 
 	if (more_mul_min > more_mul_max) {
@@ -217,9 +222,9 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 		return -EINVAL;
 	}
 
-	more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
+	more_mul_factor = lcm(div, op_pll_fr->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);
+	more_mul_factor = lcm(more_mul_factor, op_lim_bk->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);
@@ -232,25 +237,25 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 		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);
+	op_pll_fr->pll_multiplier = mul * i;
+	op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div;
+	dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div);
 
-	pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
-		/ pll->pre_pll_clk_div;
+	op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
+		/ op_pll_fr->pre_pll_clk_div;
 
-	pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz
-		* pll->pll_multiplier;
+	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
+		* op_pll_fr->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_bk->sys_clk_freq_hz =
+		op_pll_fr->pll_op_clk_freq_hz / op_pll_bk->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_bk->pix_clk_div = pll->bits_per_pixel;
+	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
 
-	op_pll->pix_clk_freq_hz =
-		op_pll->sys_clk_freq_hz / op_pll->pix_clk_div;
+	op_pll_bk->pix_clk_freq_hz =
+		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
 
 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
 		/* No OP clocks --- VT clocks are used instead. */
@@ -264,7 +269,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 	 * 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
+	if (lim->min_line_length_pck_bin > lim->min_line_length_pck
 	    / pll->binning_horizontal)
 		vt_op_binning_div = pll->binning_horizontal;
 	else
@@ -283,28 +288,28 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 	 * 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,
+	min_vt_div = DIV_ROUND_UP(op_pll_bk->pix_clk_div
+				  * op_pll_bk->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));
+			 DIV_ROUND_UP(op_pll_fr->pll_op_clk_freq_hz,
+				      lim->vt_bk.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);
+			   lim->vt_bk.min_pix_clk_div
+			   * lim->vt_bk.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;
+	max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.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));
+			 DIV_ROUND_UP(op_pll_fr->pll_op_clk_freq_hz,
+				      lim->vt_bk.min_pix_clk_freq_hz));
 	dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
 		max_vt_div);
 
@@ -312,28 +317,28 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 	 * 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;
+	min_sys_div = lim->vt_bk.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));
+				       lim->vt_bk.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);
+			  op_pll_fr->pll_op_clk_freq_hz
+			  / lim->vt_bk.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;
+	max_sys_div = lim->vt_bk.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));
+				       lim->vt_bk.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));
+			  DIV_ROUND_UP(op_pll_fr->pll_op_clk_freq_hz,
+				       lim->vt_bk.min_pix_clk_freq_hz));
 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
 
 	/*
@@ -348,13 +353,13 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 		     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) {
+			if (pix_div < lim->vt_bk.min_pix_clk_div
+			    || pix_div > lim->vt_bk.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);
+					lim->vt_bk.min_pix_clk_div,
+					lim->vt_bk.max_pix_clk_div);
 				continue;
 			}
 
@@ -367,29 +372,32 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 			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_bk.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
+	pll->vt_bk.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;
+	pll->vt_bk.sys_clk_freq_hz =
+		op_pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
+	pll->vt_bk.pix_clk_freq_hz =
+		pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.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;
+		op_pll_bk->pix_clk_freq_hz * lane_op_clock_ratio;
+	pll->pixel_rate_pixel_array = pll->vt_bk.pix_clk_freq_hz;
 
-	return check_all_bounds(dev, limits, op_limits, pll, op_pll);
+	return check_all_bounds(dev, lim, op_lim_fr, op_lim_bk, pll, op_pll_fr,
+				op_pll_bk);
 }
 
-int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
+int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		      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;
+	const struct ccs_pll_branch_limits_fr *op_lim_fr = &lim->vt_fr;
+	const struct ccs_pll_branch_limits_bk *op_lim_bk = &lim->op_bk;
+	struct ccs_pll_branch_fr *op_pll_fr = &pll->vt_fr;
+	struct ccs_pll_branch_bk *op_pll_bk = &pll->op_bk;
+	uint16_t min_op_pre_pll_clk_div;
+	uint16_t max_op_pre_pll_clk_div;
 	uint32_t lane_op_clock_ratio;
 	uint32_t mul, div;
 	unsigned int i;
@@ -401,8 +409,9 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 		 * instead. The OP values are ignored for the rest of
 		 * the PLL calculation.
 		 */
-		op_limits = &limits->vt;
-		op_pll = &pll->vt;
+		op_lim_fr = &lim->vt_fr;
+		op_lim_bk = &lim->vt_bk;
+		op_pll_bk = &pll->vt_bk;
 	}
 
 	if (pll->flags & CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
@@ -417,11 +426,11 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 	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
+		op_pll_fr->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
+		op_pll_fr->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel
 			/ DIV_ROUND_UP(pll->bits_per_pixel,
 				       pll->parallel.bus_width);
 		break;
@@ -429,39 +438,40 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 		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,
+	/* Figure out limits for OP pre-pll divider based on extclk */
+	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
+		op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div);
+	max_op_pre_pll_clk_div =
+		min_t(uint16_t, op_lim_fr->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,
+				   op_lim_fr->min_pll_ip_clk_freq_hz));
+	min_op_pre_pll_clk_div =
+		max_t(uint16_t, op_lim_fr->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);
+					   op_lim_fr->max_pll_ip_clk_freq_hz)));
+	dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
+		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
 
-	i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
-	mul = pll->pll_op_clk_freq_hz / i;
+	i = gcd(op_pll_fr->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
+	mul = op_pll_fr->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,
+	min_op_pre_pll_clk_div =
+		max_t(uint16_t, min_op_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);
+					   op_lim_fr->max_pll_op_clk_freq_hz)));
+	dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
+		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
+
+	for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div;
+	     op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div;
+	     op_pll_fr->pre_pll_clk_div += 2 - (op_pll_fr->pre_pll_clk_div & 1)) {
+		rval = __ccs_pll_calculate(dev, lim, op_lim_fr, op_lim_bk, pll,
+					   op_pll_fr, op_pll_bk, mul, div,
+					   lane_op_clock_ratio);
 		if (rval)
 			continue;
 
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 07f7f9e8a1cc..03b1d8d11423 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -20,7 +20,14 @@
 #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			(1 << 0)
 #define CCS_PLL_FLAG_NO_OP_CLOCKS				(1 << 1)
 
-struct ccs_pll_branch {
+struct ccs_pll_branch_fr {
+	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_bk {
 	uint16_t sys_clk_div;
 	uint16_t pix_clk_div;
 	uint32_t sys_clk_freq_hz;
@@ -48,18 +55,26 @@ struct ccs_pll {
 	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;
+	struct ccs_pll_branch_fr vt_fr;
+	struct ccs_pll_branch_bk vt_bk;
+	struct ccs_pll_branch_bk op_bk;
 
 	uint32_t pixel_rate_csi;
 	uint32_t pixel_rate_pixel_array;
 };
 
-struct ccs_pll_branch_limits {
+struct ccs_pll_branch_limits_fr {
+	uint16_t min_pre_pll_clk_div;
+	uint16_t max_pre_pll_clk_div;
+	uint32_t min_pll_ip_clk_freq_hz;
+	uint32_t max_pll_ip_clk_freq_hz;
+	uint16_t min_pll_multiplier;
+	uint16_t max_pll_multiplier;
+	uint32_t min_pll_op_clk_freq_hz;
+	uint32_t max_pll_op_clk_freq_hz;
+};
+
+struct ccs_pll_branch_limits_bk {
 	uint16_t min_sys_clk_div;
 	uint16_t max_sys_clk_div;
 	uint32_t min_sys_clk_freq_hz;
@@ -74,17 +89,10 @@ 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;
+	struct ccs_pll_branch_limits_fr vt_fr;
+	struct ccs_pll_branch_limits_bk vt_bk;
+	struct ccs_pll_branch_limits_bk op_bk;
 
 	/* Other relevant limits */
 	uint32_t min_line_length_pck_bin;
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 4447ca367a84..00fb8edb8fc2 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -368,67 +368,70 @@ static int ccs_pll_configure(struct ccs_sensor *sensor)
 	struct ccs_pll *pll = &sensor->pll;
 	int rval;
 
-	rval = ccs_write(sensor, VT_PIX_CLK_DIV, pll->vt.pix_clk_div);
+	rval = ccs_write(sensor, VT_PIX_CLK_DIV, pll->vt_bk.pix_clk_div);
 	if (rval < 0)
 		return rval;
 
-	rval = ccs_write(sensor, VT_SYS_CLK_DIV, pll->vt.sys_clk_div);
+	rval = ccs_write(sensor, VT_SYS_CLK_DIV, pll->vt_bk.sys_clk_div);
 	if (rval < 0)
 		return rval;
 
-	rval = ccs_write(sensor, PRE_PLL_CLK_DIV, pll->pre_pll_clk_div);
+	rval = ccs_write(sensor, PRE_PLL_CLK_DIV, pll->vt_fr.pre_pll_clk_div);
 	if (rval < 0)
 		return rval;
 
-	rval = ccs_write(sensor, PLL_MULTIPLIER, pll->pll_multiplier);
+	rval = ccs_write(sensor, PLL_MULTIPLIER, pll->vt_fr.pll_multiplier);
 	if (rval < 0)
 		return rval;
 
 	/* Lane op clock ratio does not apply here. */
 	rval = ccs_write(sensor, REQUESTED_LINK_RATE,
-			 DIV_ROUND_UP(pll->op.sys_clk_freq_hz,
+			 DIV_ROUND_UP(pll->op_bk.sys_clk_freq_hz,
 				      1000000 / 256 / 256));
 	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);
+	rval = ccs_write(sensor, OP_PIX_CLK_DIV, pll->op_bk.pix_clk_div);
 	if (rval < 0)
 		return rval;
 
-	return ccs_write(sensor, OP_SYS_CLK_DIV, pll->op.sys_clk_div);
+	return ccs_write(sensor, OP_SYS_CLK_DIV, pll->op_bk.sys_clk_div);
 }
 
 static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
 	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),
-		.max_pll_ip_freq_hz = CCS_LIM(sensor, MAX_PLL_IP_CLK_FREQ_MHZ),
-		.min_pll_multiplier = CCS_LIM(sensor, MIN_PLL_MULTIPLIER),
-		.max_pll_multiplier = CCS_LIM(sensor, MAX_PLL_MULTIPLIER),
-		.min_pll_op_freq_hz = CCS_LIM(sensor, MIN_PLL_OP_CLK_FREQ_MHZ),
-		.max_pll_op_freq_hz = CCS_LIM(sensor, MAX_PLL_OP_CLK_FREQ_MHZ),
-
-		.op.min_sys_clk_div = CCS_LIM(sensor, MIN_OP_SYS_CLK_DIV),
-		.op.max_sys_clk_div = CCS_LIM(sensor, MAX_OP_SYS_CLK_DIV),
-		.op.min_pix_clk_div = CCS_LIM(sensor, MIN_OP_PIX_CLK_DIV),
-		.op.max_pix_clk_div = CCS_LIM(sensor, MAX_OP_PIX_CLK_DIV),
-		.op.min_sys_clk_freq_hz = CCS_LIM(sensor, MIN_OP_SYS_CLK_FREQ_MHZ),
-		.op.max_sys_clk_freq_hz = CCS_LIM(sensor, MAX_OP_SYS_CLK_FREQ_MHZ),
-		.op.min_pix_clk_freq_hz = CCS_LIM(sensor, MIN_OP_PIX_CLK_FREQ_MHZ),
-		.op.max_pix_clk_freq_hz = CCS_LIM(sensor, MAX_OP_PIX_CLK_FREQ_MHZ),
-
-		.vt.min_sys_clk_div = CCS_LIM(sensor, MIN_VT_SYS_CLK_DIV),
-		.vt.max_sys_clk_div = CCS_LIM(sensor, MAX_VT_SYS_CLK_DIV),
-		.vt.min_pix_clk_div = CCS_LIM(sensor, MIN_VT_PIX_CLK_DIV),
-		.vt.max_pix_clk_div = CCS_LIM(sensor, MAX_VT_PIX_CLK_DIV),
-		.vt.min_sys_clk_freq_hz = CCS_LIM(sensor, MIN_VT_SYS_CLK_FREQ_MHZ),
-		.vt.max_sys_clk_freq_hz = CCS_LIM(sensor, MAX_VT_SYS_CLK_FREQ_MHZ),
-		.vt.min_pix_clk_freq_hz = CCS_LIM(sensor, MIN_VT_PIX_CLK_FREQ_MHZ),
-		.vt.max_pix_clk_freq_hz = CCS_LIM(sensor, MAX_VT_PIX_CLK_FREQ_MHZ),
-
+		.vt_fr = {
+			.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_clk_freq_hz = CCS_LIM(sensor, MIN_PLL_IP_CLK_FREQ_MHZ),
+			.max_pll_ip_clk_freq_hz = CCS_LIM(sensor, MAX_PLL_IP_CLK_FREQ_MHZ),
+			.min_pll_multiplier = CCS_LIM(sensor, MIN_PLL_MULTIPLIER),
+			.max_pll_multiplier = CCS_LIM(sensor, MAX_PLL_MULTIPLIER),
+			.min_pll_op_clk_freq_hz = CCS_LIM(sensor, MIN_PLL_OP_CLK_FREQ_MHZ),
+			.max_pll_op_clk_freq_hz = CCS_LIM(sensor, MAX_PLL_OP_CLK_FREQ_MHZ),
+		},
+		.op_bk = {
+			 .min_sys_clk_div = CCS_LIM(sensor, MIN_OP_SYS_CLK_DIV),
+			 .max_sys_clk_div = CCS_LIM(sensor, MAX_OP_SYS_CLK_DIV),
+			 .min_pix_clk_div = CCS_LIM(sensor, MIN_OP_PIX_CLK_DIV),
+			 .max_pix_clk_div = CCS_LIM(sensor, MAX_OP_PIX_CLK_DIV),
+			 .min_sys_clk_freq_hz = CCS_LIM(sensor, MIN_OP_SYS_CLK_FREQ_MHZ),
+			 .max_sys_clk_freq_hz = CCS_LIM(sensor, MAX_OP_SYS_CLK_FREQ_MHZ),
+			 .min_pix_clk_freq_hz = CCS_LIM(sensor, MIN_OP_PIX_CLK_FREQ_MHZ),
+			 .max_pix_clk_freq_hz = CCS_LIM(sensor, MAX_OP_PIX_CLK_FREQ_MHZ),
+		 },
+		.vt_bk = {
+			 .min_sys_clk_div = CCS_LIM(sensor, MIN_VT_SYS_CLK_DIV),
+			 .max_sys_clk_div = CCS_LIM(sensor, MAX_VT_SYS_CLK_DIV),
+			 .min_pix_clk_div = CCS_LIM(sensor, MIN_VT_PIX_CLK_DIV),
+			 .max_pix_clk_div = CCS_LIM(sensor, MAX_VT_PIX_CLK_DIV),
+			 .min_sys_clk_freq_hz = CCS_LIM(sensor, MIN_VT_SYS_CLK_FREQ_MHZ),
+			 .max_sys_clk_freq_hz = CCS_LIM(sensor, MAX_VT_SYS_CLK_FREQ_MHZ),
+			 .min_pix_clk_freq_hz = CCS_LIM(sensor, MIN_VT_PIX_CLK_FREQ_MHZ),
+			 .max_pix_clk_freq_hz = CCS_LIM(sensor, MAX_VT_PIX_CLK_FREQ_MHZ),
+		 },
 		.min_line_length_pck_bin = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN),
 		.min_line_length_pck = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK),
 	};
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 03/38] ccs-pll: Use correct VT divisor for calculating VT SYS divisor
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
  2020-12-02 18:06 ` [PATCH 01/38] ccs-pll: Don't use div_u64 to divide a 32-bit number Sakari Ailus
  2020-12-02 18:06 ` [PATCH 02/38] ccs-pll: Split limits and PLL configuration into front and back parts Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 04/38] ccs-pll: End search if there are no better values available Sakari Ailus
                   ` (34 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Use the correct video timing divisor to calculate the SYS divisor. Instead
of the current value, the minimum was used. This could have resulted in a
too low SYS divisor.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index b2f0fa14ff92..ea0f84fc8a90 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -365,14 +365,14 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 
 			/* Check if this one is better. */
 			if (pix_div * sys_div
-			    <= roundup(min_vt_div, best_pix_div))
+			    <= roundup(vt_div, best_pix_div))
 				best_pix_div = pix_div;
 		}
 		if (best_pix_div < INT_MAX >> 1)
 			break;
 	}
 
-	pll->vt_bk.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
+	pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div);
 	pll->vt_bk.pix_clk_div = best_pix_div;
 
 	pll->vt_bk.sys_clk_freq_hz =
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 04/38] ccs-pll: End search if there are no better values available
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (2 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 03/38] ccs-pll: Use correct VT divisor for calculating VT SYS divisor Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 05/38] ccs-pll: Remove parallel bus support Sakari Ailus
                   ` (33 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The VT divisor search can be ended if we've already found the value that
corresponds exactly the total divisor, as there are no better (lower)
values available.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index ea0f84fc8a90..22e29127804a 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -352,6 +352,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		     sys_div <= max_sys_div;
 		     sys_div += 2 - (sys_div & 1)) {
 			uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div);
+			uint16_t rounded_div;
 
 			if (pix_div < lim->vt_bk.min_pix_clk_div
 			    || pix_div > lim->vt_bk.max_pix_clk_div) {
@@ -363,10 +364,15 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 				continue;
 			}
 
+			rounded_div = roundup(vt_div, best_pix_div);
+
 			/* Check if this one is better. */
-			if (pix_div * sys_div
-			    <= roundup(vt_div, best_pix_div))
+			if (pix_div * sys_div <= rounded_div)
 				best_pix_div = pix_div;
+
+			/* Bail out if we've already found the best value. */
+			if (vt_div == rounded_div)
+				break;
 		}
 		if (best_pix_div < INT_MAX >> 1)
 			break;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 05/38] ccs-pll: Remove parallel bus support
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (3 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 04/38] ccs-pll: End search if there are no better values available Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 06/38] ccs-pll: Differentiate between CSI-2 D-PHY and C-PHY Sakari Ailus
                   ` (32 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The parallel bus PLL calculation has no users. Remove it.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c |  5 -----
 drivers/media/i2c/ccs-pll.h | 14 ++++----------
 2 files changed, 4 insertions(+), 15 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 22e29127804a..da97a2b91717 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -435,11 +435,6 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		op_pll_fr->pll_op_clk_freq_hz = pll->link_freq * 2
 			* (pll->csi2.lanes / lane_op_clock_ratio);
 		break;
-	case CCS_PLL_BUS_TYPE_PARALLEL:
-		op_pll_fr->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;
 	}
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 03b1d8d11423..578c9272688a 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -13,8 +13,7 @@
 #define CCS_PLL_H
 
 /* CSI-2 or CCP-2 */
-#define CCS_PLL_BUS_TYPE_CSI2				0x00
-#define CCS_PLL_BUS_TYPE_PARALLEL				0x01
+#define CCS_PLL_BUS_TYPE_CSI2					0x00
 
 /* op pix clock is for all lanes in total normally */
 #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			(1 << 0)
@@ -37,14 +36,9 @@ struct ccs_pll_branch_bk {
 struct ccs_pll {
 	/* input values */
 	uint8_t bus_type;
-	union {
-		struct {
-			uint8_t lanes;
-		} csi2;
-		struct {
-			uint8_t bus_width;
-		} parallel;
-	};
+	struct {
+		uint8_t lanes;
+	} csi2;
 	unsigned long flags;
 	uint8_t binning_horizontal;
 	uint8_t binning_vertical;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 06/38] ccs-pll: Differentiate between CSI-2 D-PHY and C-PHY
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (4 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 05/38] ccs-pll: Remove parallel bus support Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 07/38] ccs-pll: Move the flags field down, away from 8-bit fields Sakari Ailus
                   ` (31 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Differentiate between CSI-2 D-PHY and C-PHY. This does not yet include
support for C-PHY.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c      | 2 +-
 drivers/media/i2c/ccs-pll.h      | 3 ++-
 drivers/media/i2c/ccs/ccs-core.c | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index da97a2b91717..c6435ed0597e 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -430,7 +430,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		pll->binning_vertical);
 
 	switch (pll->bus_type) {
-	case CCS_PLL_BUS_TYPE_CSI2:
+	case CCS_PLL_BUS_TYPE_CSI2_DPHY:
 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
 		op_pll_fr->pll_op_clk_freq_hz = pll->link_freq * 2
 			* (pll->csi2.lanes / lane_op_clock_ratio);
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 578c9272688a..d06a80c4fc52 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -13,7 +13,8 @@
 #define CCS_PLL_H
 
 /* CSI-2 or CCP-2 */
-#define CCS_PLL_BUS_TYPE_CSI2					0x00
+#define CCS_PLL_BUS_TYPE_CSI2_DPHY				0x00
+#define CCS_PLL_BUS_TYPE_CSI2_CPHY				0x01
 
 /* op pix clock is for all lanes in total normally */
 #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			(1 << 0)
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 00fb8edb8fc2..591953cec6ec 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3201,7 +3201,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 = CCS_PLL_BUS_TYPE_CSI2;
+	sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
 	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);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 07/38] ccs-pll: Move the flags field down, away from 8-bit fields
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (5 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 06/38] ccs-pll: Differentiate between CSI-2 D-PHY and C-PHY Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 08/38] ccs-pll: Document the structs in the header as well as the function Sakari Ailus
                   ` (30 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

This way the struct will use less memory, with better packing and no waste
due to unsigned long.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index d06a80c4fc52..1d908b23c934 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -40,12 +40,12 @@ struct ccs_pll {
 	struct {
 		uint8_t lanes;
 	} csi2;
-	unsigned long flags;
 	uint8_t binning_horizontal;
 	uint8_t binning_vertical;
 	uint8_t scale_m;
 	uint8_t scale_n;
 	uint8_t bits_per_pixel;
+	uint16_t flags;
 	uint32_t link_freq;
 	uint32_t ext_clk_freq_hz;
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 08/38] ccs-pll: Document the structs in the header as well as the function
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (6 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 07/38] ccs-pll: Move the flags field down, away from 8-bit fields Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 09/38] ccs-pll: Use the BIT macro Sakari Ailus
                   ` (29 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The CCS pll is used by the CCS driver at the moment, but documenting the
interface makes sense. It's non-trivial and the calculator could be used
elsewhere.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.h | 89 +++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 1d908b23c934..1b8d36068c7d 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -20,6 +20,16 @@
 #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			(1 << 0)
 #define CCS_PLL_FLAG_NO_OP_CLOCKS				(1 << 1)
 
+/**
+ * struct ccs_pll_branch_fr - CCS PLL configuration (front)
+ *
+ * A single branch front-end of the CCS PLL tree.
+ *
+ * @pre_pll_clk_div: Pre-PLL clock divisor
+ * @pll_multiplier: PLL multiplier
+ * @pll_ip_clk_freq_hz: PLL input clock frequency
+ * @pll_op_clk_freq_hz: PLL output clock frequency
+ */
 struct ccs_pll_branch_fr {
 	uint16_t pre_pll_clk_div;
 	uint16_t pll_multiplier;
@@ -27,6 +37,16 @@ struct ccs_pll_branch_fr {
 	uint32_t pll_op_clk_freq_hz;
 };
 
+/**
+ * struct ccs_pll_branch_bk - CCS PLL configuration (back)
+ *
+ * A single branch back-end of the CCS PLL tree.
+ *
+ * @sys_clk_div: System clock divider
+ * @pix_clk_div: Pixel clock divider
+ * @sys_clk_freq_hz: System clock frequency
+ * @pix_clk_freq_hz: Pixel clock frequency
+ */
 struct ccs_pll_branch_bk {
 	uint16_t sys_clk_div;
 	uint16_t pix_clk_div;
@@ -34,6 +54,30 @@ struct ccs_pll_branch_bk {
 	uint32_t pix_clk_freq_hz;
 };
 
+/**
+ * struct ccs_pll - Full CCS PLL configuration
+ *
+ * All information required to calculate CCS PLL configuration.
+ *
+ * @bus_type: Type of the data bus, CCS_PLL_BUS_TYPE_* (input)
+ * @csi2: CSI-2 related parameters
+ * @csi2.lanes: The number of the CSI-2 data lanes (input)
+ * @binning_vertical: Vertical binning factor (input)
+ * @binning_horizontal: Horizontal binning factor (input)
+ * @scale_m: Downscaling factor, M component, [16, max] (input)
+ * @scale_n: Downscaling factor, N component, typically 16 (input)
+ * @bits_per_pixel: Bits per pixel on the output data bus (input)
+ * @flags: CCS_PLL_FLAG_* (input)
+ * @link_freq: Chosen link frequency (input)
+ * @ext_clk_freq_hz: External clock frequency, i.e. the sensor's input clock
+ *		     (input)
+ * @vt_fr: Video timing front-end configuration (output)
+ * @vt_bk: Video timing back-end configuration (output)
+ * @op_bk: Operational timing back-end configuration (output)
+ * @pixel_rate_csi: Pixel rate on the output data bus (output)
+ * @pixel_rate_pixel_array: Nominal pixel rate in the sensor's pixel array
+ *			    (output)
+ */
 struct ccs_pll {
 	/* input values */
 	uint8_t bus_type;
@@ -58,6 +102,18 @@ struct ccs_pll {
 	uint32_t pixel_rate_pixel_array;
 };
 
+/**
+ * struct ccs_pll_branch_limits_fr - CCS PLL front-end limits
+ *
+ * @min_pre_pll_clk_div: Minimum pre-PLL clock divider
+ * @max_pre_pll_clk_div: Maximum pre-PLL clock divider
+ * @min_pll_ip_clk_freq_hz: Minimum PLL input clock frequency
+ * @max_pll_ip_clk_freq_hz: Maximum PLL input clock frequency
+ * @min_pll_multiplier: Minimum PLL multiplier
+ * @max_pll_multiplier: Maximum PLL multiplier
+ * @min_pll_op_clk_freq_hz: Minimum PLL output clock frequency
+ * @max_pll_op_clk_freq_hz: Maximum PLL output clock frequency
+ */
 struct ccs_pll_branch_limits_fr {
 	uint16_t min_pre_pll_clk_div;
 	uint16_t max_pre_pll_clk_div;
@@ -69,6 +125,18 @@ struct ccs_pll_branch_limits_fr {
 	uint32_t max_pll_op_clk_freq_hz;
 };
 
+/**
+ * struct ccs_pll_branch_limits_bk - CCS PLL back-end limits
+ *
+ * @min_sys_clk_div: Minimum system clock divider
+ * @max_sys_clk_div: Maximum system clock divider
+ * @min_sys_clk_freq_hz: Minimum system clock frequency
+ * @max_sys_clk_freq_hz: Maximum system clock frequency
+ * @min_pix_clk_div: Minimum pixel clock divider
+ * @max_pix_clk_div: Maximum pixel clock divider
+ * @min_pix_clk_freq_hz: Minimum pixel clock frequency
+ * @max_pix_clk_freq_hz: Maximum pixel clock frequency
+ */
 struct ccs_pll_branch_limits_bk {
 	uint16_t min_sys_clk_div;
 	uint16_t max_sys_clk_div;
@@ -80,6 +148,17 @@ struct ccs_pll_branch_limits_bk {
 	uint32_t max_pix_clk_freq_hz;
 };
 
+/**
+ * struct ccs_pll_limits - CCS PLL limits
+ *
+ * @min_ext_clk_freq_hz: Minimum external clock frequency
+ * @max_ext_clk_freq_hz: Maximum external clock frequency
+ * @vt_fr: Video timing front-end limits
+ * @vt_bk: Video timing back-end limits
+ * @op_bk: Operational timing back-end limits
+ * @min_line_length_pck_bin: Minimum line length in pixels, with binning
+ * @min_line_length_pck: Minimum line length in pixels without binning
+ */
 struct ccs_pll_limits {
 	/* Strict PLL limits */
 	uint32_t min_ext_clk_freq_hz;
@@ -96,6 +175,16 @@ struct ccs_pll_limits {
 
 struct device;
 
+/**
+ * ccs_pll_calculate - Calculate CCS PLL configuration based on input parameters
+ *
+ * @dev: Device pointer, used for printing messages
+ * @limits: Limits specific to the sensor
+ * @pll: Given PLL configuration
+ *
+ * Calculate the CCS PLL configuration based on the limits as well as given
+ * device specific, system specific or user configured input data.
+ */
 int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits,
 		      struct ccs_pll *pll);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 09/38] ccs-pll: Use the BIT macro
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (7 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 08/38] ccs-pll: Document the structs in the header as well as the function Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 10/38] ccs-pll: Begin calculation from OP system clock frequency Sakari Ailus
                   ` (28 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Use the BIT macro for setting individual bits.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.h | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 1b8d36068c7d..3ad4e6524ab6 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -12,13 +12,16 @@
 #ifndef CCS_PLL_H
 #define CCS_PLL_H
 
+#include <linux/bits.h>
+
 /* CSI-2 or CCP-2 */
 #define CCS_PLL_BUS_TYPE_CSI2_DPHY				0x00
 #define CCS_PLL_BUS_TYPE_CSI2_CPHY				0x01
 
+/* Old SMIA and implementation specific flags */
 /* 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)
+#define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			BIT(0)
+#define CCS_PLL_FLAG_NO_OP_CLOCKS				BIT(1)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 10/38] ccs-pll: Begin calculation from OP system clock frequency
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (8 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 09/38] ccs-pll: Use the BIT macro Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 11/38] ccs-pll: Fix condition for pre-PLL divider lower bound Sakari Ailus
                   ` (27 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The OP system clock frequency defines the CSI-2 bus clock frequency, not
the PLL output clock frequency. Both values were overwritten in the end,
but the wrong limit value was used for the OP system clock frequency,
possibly leading to too high frequencies being used.

Also remove now duplicated calculation of OP system clock frequency later
in the PLL calculator.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index c6435ed0597e..584be36f8c66 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -247,10 +247,6 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
 		* op_pll_fr->pll_multiplier;
 
-	/* Derive pll_op_clk_freq_hz. */
-	op_pll_bk->sys_clk_freq_hz =
-		op_pll_fr->pll_op_clk_freq_hz / op_pll_bk->sys_clk_div;
-
 	op_pll_bk->pix_clk_div = pll->bits_per_pixel;
 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
 
@@ -432,7 +428,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	switch (pll->bus_type) {
 	case CCS_PLL_BUS_TYPE_CSI2_DPHY:
 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
-		op_pll_fr->pll_op_clk_freq_hz = pll->link_freq * 2
+		op_pll_bk->sys_clk_freq_hz = pll->link_freq * 2
 			* (pll->csi2.lanes / lane_op_clock_ratio);
 		break;
 	default:
@@ -454,8 +450,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
 
-	i = gcd(op_pll_fr->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
-	mul = op_pll_fr->pll_op_clk_freq_hz / i;
+	i = gcd(op_pll_bk->sys_clk_freq_hz, pll->ext_clk_freq_hz);
+	mul = op_pll_bk->sys_clk_freq_hz / i;
 	div = pll->ext_clk_freq_hz / i;
 	dev_dbg(dev, "mul %u / div %u\n", mul, div);
 
@@ -463,7 +459,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		max_t(uint16_t, min_op_pre_pll_clk_div,
 		      clk_div_even_up(
 			      DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
-					   op_lim_fr->max_pll_op_clk_freq_hz)));
+					   op_lim_bk->max_sys_clk_freq_hz)));
 	dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 11/38] ccs-pll: Fix condition for pre-PLL divider lower bound
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (9 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 10/38] ccs-pll: Begin calculation from OP system clock frequency Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 12/38] ccs-pll: Avoid overflow in pre-PLL divisor lower bound search Sakari Ailus
                   ` (26 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The lower bound of the pre-PLL divider was calculated based on OP SYS
clock frequency which is also affected by the OP SYS clock divider. This
is wrong. The right clock frequency is that of the PLL output clock.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 584be36f8c66..b45e6b30c528 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -459,7 +459,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		max_t(uint16_t, min_op_pre_pll_clk_div,
 		      clk_div_even_up(
 			      DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
-					   op_lim_bk->max_sys_clk_freq_hz)));
+					   op_lim_fr->max_pll_op_clk_freq_hz)));
 	dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 12/38] ccs-pll: Avoid overflow in pre-PLL divisor lower bound search
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (10 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 11/38] ccs-pll: Fix condition for pre-PLL divider lower bound Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 13/38] ccs-pll: Fix comment on check against maximum PLL multiplier Sakari Ailus
                   ` (25 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The external clock frequency times the PLL multiplier may exceed the value
range of 32-bit unsigned integers. Instead perform the same calculation y
using two divisions.

The result has some potential to be different, but that's ok: this number
is used to limit the range of pre-PLL divisors to find optimal values. So
the effect of the rare case of a different result here would mean an
invalid pre-PLL divisor is tried. That will be found out a little later in
any case.

Also guard against dividing by zero if the external clock frequency is
higher than the maximum OP PLL output clock --- a rather improbable case.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index b45e6b30c528..78897a7c1448 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -40,6 +40,11 @@ static inline uint32_t is_one_or_even(uint32_t a)
 	return 1;
 }
 
+static inline uint32_t one_or_more(uint32_t a)
+{
+	return a ?: 1;
+}
+
 static int bounds_check(struct device *dev, uint32_t val,
 			uint32_t min, uint32_t max, char *str)
 {
@@ -458,8 +463,10 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	min_op_pre_pll_clk_div =
 		max_t(uint16_t, min_op_pre_pll_clk_div,
 		      clk_div_even_up(
-			      DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
-					   op_lim_fr->max_pll_op_clk_freq_hz)));
+			      mul /
+			      one_or_more(
+				      DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz,
+						   pll->ext_clk_freq_hz))));
 	dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n",
 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 13/38] ccs-pll: Fix comment on check against maximum PLL multiplier
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (11 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 12/38] ccs-pll: Avoid overflow in pre-PLL divisor lower bound search Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 14/38] ccs-pll: Fix check for PLL multiplier upper bound Sakari Ailus
                   ` (24 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The comment is about minimum PLL multiplier but the related check really
deals with the maximum PLL multiplier.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 78897a7c1448..f4c41d61e332 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -203,7 +203,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 			   / 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. */
+	/* Ensure we won't go above max_pll_multiplier. */
 	more_mul_max = min(more_mul_max,
 			   DIV_ROUND_UP(op_lim_fr->max_pll_multiplier, mul));
 	dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 14/38] ccs-pll: Fix check for PLL multiplier upper bound
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (12 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 13/38] ccs-pll: Fix comment on check against maximum PLL multiplier Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 15/38] ccs-pll: Use explicit 32-bit unsigned type Sakari Ailus
                   ` (23 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The additional multiplier (for higher VT timing) of the PLL multiplier was
checked against the upper limit but the result was rounded up, possibly
producing too high additional multiplier. Round down instead to keep
within hardware limits.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index f4c41d61e332..b23e959000a4 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -204,8 +204,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n",
 		more_mul_max);
 	/* Ensure we won't go above max_pll_multiplier. */
-	more_mul_max = min(more_mul_max,
-			   DIV_ROUND_UP(op_lim_fr->max_pll_multiplier, mul));
+	more_mul_max = min(more_mul_max, op_lim_fr->max_pll_multiplier / mul);
 	dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
 		more_mul_max);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 15/38] ccs-pll: Use explicit 32-bit unsigned type
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (13 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 14/38] ccs-pll: Fix check for PLL multiplier upper bound Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 16/38] ccs-pll: Add support for lane speed model Sakari Ailus
                   ` (22 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Use uint32_t instead of unsigned int for a variable that contains
explicitly 32-bit numbers.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index b23e959000a4..d33a2575329c 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -177,7 +177,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	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;
+	uint32_t i;
 
 	/*
 	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
@@ -406,7 +406,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	uint16_t max_op_pre_pll_clk_div;
 	uint32_t lane_op_clock_ratio;
 	uint32_t mul, div;
-	unsigned int i;
+	uint32_t i;
 	int rval = -EINVAL;
 
 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 16/38] ccs-pll: Add support for lane speed model
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (14 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 15/38] ccs-pll: Use explicit 32-bit unsigned type Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 17/38] ccs: " Sakari Ailus
                   ` (21 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

CCS PLL includes a capability to calculate the VT clocks on per-lane
basis. Add support for this feature.

Move calculation of the pixel rate on the CSI-2 bus early in the function
as everything needed to calculate it is already available.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 36 +++++++++++++++++++++++++-----------
 drivers/media/i2c/ccs-pll.h |  6 ++++++
 2 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index d33a2575329c..9750b49d834a 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -251,11 +251,12 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
 		* op_pll_fr->pll_multiplier;
 
-	op_pll_bk->pix_clk_div = pll->bits_per_pixel;
-	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
-
+	op_pll_bk->pix_clk_div = pll->bits_per_pixel
+		* pll->op_lanes / pll->csi2.lanes;
 	op_pll_bk->pix_clk_freq_hz =
 		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
+	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
+
 
 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
 		/* No OP clocks --- VT clocks are used instead. */
@@ -283,15 +284,16 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	 * divisors. One must make sure that horizontal blanking is
 	 * enough to accommodate the CSI-2 sync codes.
 	 *
-	 * Take scaling factor into account as well.
+	 * Take scaling factor and number of VT lanes 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_bk->pix_clk_div
-				  * op_pll_bk->sys_clk_div * pll->scale_n,
-				  lane_op_clock_ratio * vt_op_binning_div
-				  * pll->scale_m);
+				  * op_pll_bk->sys_clk_div * pll->scale_n
+				  * pll->vt_lanes,
+				  pll->op_lanes * vt_op_binning_div
+				  * pll->scale_m * lane_op_clock_ratio);
 
 	/* Find smallest and biggest allowed vt divisor. */
 	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
@@ -387,9 +389,8 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div;
 
 out_skip_vt_calc:
-	pll->pixel_rate_csi =
-		op_pll_bk->pix_clk_freq_hz * lane_op_clock_ratio;
-	pll->pixel_rate_pixel_array = pll->vt_bk.pix_clk_freq_hz;
+	pll->pixel_rate_pixel_array =
+		pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
 
 	return check_all_bounds(dev, lim, op_lim_fr, op_lim_bk, pll, op_pll_fr,
 				op_pll_bk);
@@ -409,6 +410,13 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	uint32_t i;
 	int rval = -EINVAL;
 
+	if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) {
+		pll->op_lanes = 1;
+		pll->vt_lanes = 1;
+	}
+	dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes);
+	dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes);
+
 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
 		/*
 		 * If there's no OP PLL at all, use the VT values
@@ -433,12 +441,18 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	case CCS_PLL_BUS_TYPE_CSI2_DPHY:
 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
 		op_pll_bk->sys_clk_freq_hz = pll->link_freq * 2
-			* (pll->csi2.lanes / lane_op_clock_ratio);
+			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
+			   1 : pll->csi2.lanes) / lane_op_clock_ratio;
 		break;
 	default:
 		return -EINVAL;
 	}
 
+	pll->pixel_rate_csi =
+		op_pll_bk->pix_clk_freq_hz
+		* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
+		   pll->csi2.lanes : 1) * lane_op_clock_ratio;
+
 	/* Figure out limits for OP pre-pll divider based on extclk */
 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
 		op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div);
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 3ad4e6524ab6..fe8eb3d4bcff 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -22,6 +22,8 @@
 /* op pix clock is for all lanes in total normally */
 #define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE			BIT(0)
 #define CCS_PLL_FLAG_NO_OP_CLOCKS				BIT(1)
+/* CCS PLL flags */
+#define CCS_PLL_FLAG_LANE_SPEED_MODEL				BIT(2)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
@@ -63,6 +65,8 @@ struct ccs_pll_branch_bk {
  * All information required to calculate CCS PLL configuration.
  *
  * @bus_type: Type of the data bus, CCS_PLL_BUS_TYPE_* (input)
+ * @op_lanes: Number of operational lanes (input)
+ * @vt_lanes: Number of video timing lanes (input)
  * @csi2: CSI-2 related parameters
  * @csi2.lanes: The number of the CSI-2 data lanes (input)
  * @binning_vertical: Vertical binning factor (input)
@@ -84,6 +88,8 @@ struct ccs_pll_branch_bk {
 struct ccs_pll {
 	/* input values */
 	uint8_t bus_type;
+	uint8_t op_lanes;
+	uint8_t vt_lanes;
 	struct {
 		uint8_t lanes;
 	} csi2;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 17/38] ccs: Add support for lane speed model
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (15 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 16/38] ccs-pll: Add support for lane speed model Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 18/38] ccs-pll: Add support for decoupled OP domain calculation Sakari Ailus
                   ` (20 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Convey the relevant PLL flags to the PLL calculator. Also the lane speed
model affects how the link rate is calculated on the CSI-2 bus, as the
rate is total of all lanes.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 591953cec6ec..f3d4209c818c 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -387,7 +387,9 @@ static int ccs_pll_configure(struct ccs_sensor *sensor)
 	/* Lane op clock ratio does not apply here. */
 	rval = ccs_write(sensor, REQUESTED_LINK_RATE,
 			 DIV_ROUND_UP(pll->op_bk.sys_clk_freq_hz,
-				      1000000 / 256 / 256));
+				      1000000 / 256 / 256) *
+			 (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
+			  sensor->pll.csi2.lanes : 1));
 	if (rval < 0 || sensor->pll.flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
 		return rval;
 
@@ -3203,6 +3205,13 @@ static int ccs_probe(struct i2c_client *client)
 	/* prepare PLL configuration input values */
 	sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
 	sensor->pll.csi2.lanes = sensor->hwcfg.lanes;
+	if (CCS_LIM(sensor, CLOCK_CALCULATION) &
+	    CCS_CLOCK_CALCULATION_LANE_SPEED) {
+		sensor->pll.vt_lanes =
+			CCS_LIM(sensor, NUM_OF_VT_LANES) + 1;
+		sensor->pll.op_lanes = sensor->pll.vt_lanes;
+		sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL;
+	}
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 18/38] ccs-pll: Add support for decoupled OP domain calculation
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (16 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 17/38] ccs: " Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 19/38] ccs-pll: Add support for extended input PLL clock divider Sakari Ailus
                   ` (19 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Add support for decoupled OP domain clock calculation. This means that the
number of VT and OP domain clocks are no longer dependent on the number of
CSI-2 lanes in the lane speed mode.

The support also replaces the existing quirk flag to calculate OP domain
clocks per lane.

Also support decoupled OP domain calculation in the CCS driver.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c       | 22 +++++++---------------
 drivers/media/i2c/ccs-pll.h       |  1 +
 drivers/media/i2c/ccs/ccs-core.c  | 14 +++++++++++---
 drivers/media/i2c/ccs/ccs-quirk.c |  5 ++++-
 4 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 9750b49d834a..cb19a36e54df 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -162,7 +162,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
 		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
 		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
-		    uint32_t div, uint32_t lane_op_clock_ratio)
+		    uint32_t div)
 {
 	uint32_t sys_div;
 	uint32_t best_pix_div = INT_MAX >> 1;
@@ -194,7 +194,8 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		min_t(uint32_t,
 		      more_mul_max,
 		      op_lim_fr->max_pll_op_clk_freq_hz
-		      / (pll->ext_clk_freq_hz / op_pll_fr->pre_pll_clk_div * mul));
+		      / (pll->ext_clk_freq_hz /
+			 op_pll_fr->pre_pll_clk_div * mul));
 	dev_dbg(dev, "more_mul_max: max_pll_op_clk_freq_hz check: %u\n",
 		more_mul_max);
 	/* Don't go above the division capability of op sys clock divider. */
@@ -257,7 +258,6 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->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;
@@ -293,7 +293,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 				  * op_pll_bk->sys_clk_div * pll->scale_n
 				  * pll->vt_lanes,
 				  pll->op_lanes * vt_op_binning_div
-				  * pll->scale_m * lane_op_clock_ratio);
+				  * pll->scale_m);
 
 	/* Find smallest and biggest allowed vt divisor. */
 	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
@@ -405,7 +405,6 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	struct ccs_pll_branch_bk *op_pll_bk = &pll->op_bk;
 	uint16_t min_op_pre_pll_clk_div;
 	uint16_t max_op_pre_pll_clk_div;
-	uint32_t lane_op_clock_ratio;
 	uint32_t mul, div;
 	uint32_t i;
 	int rval = -EINVAL;
@@ -428,12 +427,6 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		op_pll_bk = &pll->vt_bk;
 	}
 
-	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);
 
@@ -442,7 +435,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
 		op_pll_bk->sys_clk_freq_hz = pll->link_freq * 2
 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-			   1 : pll->csi2.lanes) / lane_op_clock_ratio;
+			   1 : pll->csi2.lanes);
 		break;
 	default:
 		return -EINVAL;
@@ -451,7 +444,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	pll->pixel_rate_csi =
 		op_pll_bk->pix_clk_freq_hz
 		* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-		   pll->csi2.lanes : 1) * lane_op_clock_ratio;
+		   pll->csi2.lanes : 1);
 
 	/* Figure out limits for OP pre-pll divider based on extclk */
 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
@@ -487,8 +480,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	     op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div;
 	     op_pll_fr->pre_pll_clk_div += 2 - (op_pll_fr->pre_pll_clk_div & 1)) {
 		rval = __ccs_pll_calculate(dev, lim, op_lim_fr, op_lim_bk, pll,
-					   op_pll_fr, op_pll_bk, mul, div,
-					   lane_op_clock_ratio);
+					   op_pll_fr, op_pll_bk, mul, div);
 		if (rval)
 			continue;
 
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index fe8eb3d4bcff..fe20af11a068 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -24,6 +24,7 @@
 #define CCS_PLL_FLAG_NO_OP_CLOCKS				BIT(1)
 /* CCS PLL flags */
 #define CCS_PLL_FLAG_LANE_SPEED_MODEL				BIT(2)
+#define CCS_PLL_FLAG_LINK_DECOUPLED				BIT(3)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index f3d4209c818c..41c006ad03c4 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3207,10 +3207,18 @@ static int ccs_probe(struct i2c_client *client)
 	sensor->pll.csi2.lanes = sensor->hwcfg.lanes;
 	if (CCS_LIM(sensor, CLOCK_CALCULATION) &
 	    CCS_CLOCK_CALCULATION_LANE_SPEED) {
-		sensor->pll.vt_lanes =
-			CCS_LIM(sensor, NUM_OF_VT_LANES) + 1;
-		sensor->pll.op_lanes = sensor->pll.vt_lanes;
 		sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL;
+		if (CCS_LIM(sensor, CLOCK_CALCULATION) &
+		    CCS_CLOCK_CALCULATION_LINK_DECOUPLED) {
+			sensor->pll.vt_lanes =
+				CCS_LIM(sensor, NUM_OF_VT_LANES) + 1;
+			sensor->pll.op_lanes =
+				CCS_LIM(sensor, NUM_OF_OP_LANES) + 1;
+			sensor->pll.flags |= CCS_PLL_FLAG_LINK_DECOUPLED;
+		} else {
+			sensor->pll.vt_lanes = sensor->pll.csi2.lanes;
+			sensor->pll.op_lanes = sensor->pll.csi2.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 4fe8c6f70579..e3d4c7a275bc 100644
--- a/drivers/media/i2c/ccs/ccs-quirk.c
+++ b/drivers/media/i2c/ccs/ccs-quirk.c
@@ -190,7 +190,10 @@ static int jt8ev1_post_streamoff(struct ccs_sensor *sensor)
 
 static int jt8ev1_init(struct ccs_sensor *sensor)
 {
-	sensor->pll.flags |= CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+	sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL |
+		CCS_PLL_FLAG_LINK_DECOUPLED;
+	sensor->pll.vt_lanes = 1;
+	sensor->pll.op_lanes = sensor->pll.csi2.lanes;
 
 	return 0;
 }
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 19/38] ccs-pll: Add support for extended input PLL clock divider
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (17 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 18/38] ccs-pll: Add support for decoupled OP domain calculation Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 20/38] ccs-pll: Support two cycles per pixel on OP domain Sakari Ailus
                   ` (18 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

CCS allows odd PLL dividers other than 1, granted that the corresponding
capability bit is set. Support this both in the PLL calculator and the CCS
driver.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c      | 4 +++-
 drivers/media/i2c/ccs-pll.h      | 1 +
 drivers/media/i2c/ccs/ccs-core.c | 3 +++
 3 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index cb19a36e54df..62939ca5b8e2 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -478,7 +478,9 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 
 	for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div;
 	     op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div;
-	     op_pll_fr->pre_pll_clk_div += 2 - (op_pll_fr->pre_pll_clk_div & 1)) {
+	     op_pll_fr->pre_pll_clk_div +=
+		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
+		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
 		rval = __ccs_pll_calculate(dev, lim, op_lim_fr, op_lim_bk, pll,
 					   op_pll_fr, op_pll_bk, mul, div);
 		if (rval)
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index fe20af11a068..807ae7250aa2 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -25,6 +25,7 @@
 /* CCS PLL flags */
 #define CCS_PLL_FLAG_LANE_SPEED_MODEL				BIT(2)
 #define CCS_PLL_FLAG_LINK_DECOUPLED				BIT(3)
+#define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER				BIT(4)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 41c006ad03c4..70f4f304fef6 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3220,6 +3220,9 @@ static int ccs_probe(struct i2c_client *client)
 			sensor->pll.op_lanes = sensor->pll.csi2.lanes;
 		}
 	}
+	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
+	    CCS_CLOCK_TREE_PLL_CAPABILITY_EXT_DIVIDER)
+		sensor->pll.flags |= CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER;
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 20/38] ccs-pll: Support two cycles per pixel on OP domain
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (18 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 19/38] ccs-pll: Add support for extended input PLL clock divider Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 21/38] ccs-pll: Add support flexible OP PLL pixel clock divider Sakari Ailus
                   ` (17 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The l parameter defines the number of clock cycles to process a single
pixel per OP lane. It is calculated based on a new register
op_bits_per_lane.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c      | 19 +++++++++++++------
 drivers/media/i2c/ccs-pll.h      |  2 ++
 drivers/media/i2c/ccs/ccs-core.c |  1 +
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 62939ca5b8e2..60ab0c043c7a 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -162,7 +162,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
 		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
 		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
-		    uint32_t div)
+		    uint32_t div, uint32_t l)
 {
 	uint32_t sys_div;
 	uint32_t best_pix_div = INT_MAX >> 1;
@@ -252,10 +252,15 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
 		* op_pll_fr->pll_multiplier;
 
-	op_pll_bk->pix_clk_div = pll->bits_per_pixel
-		* pll->op_lanes / pll->csi2.lanes;
+	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
+		op_pll_bk->pix_clk_div = pll->bits_per_pixel
+			* pll->op_lanes / pll->csi2.lanes / l;
+	else
+		op_pll_bk->pix_clk_div = pll->bits_per_pixel / l;
+
 	op_pll_bk->pix_clk_freq_hz =
 		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
+
 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
 
 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) {
@@ -291,7 +296,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
 	min_vt_div = DIV_ROUND_UP(op_pll_bk->pix_clk_div
 				  * op_pll_bk->sys_clk_div * pll->scale_n
-				  * pll->vt_lanes,
+				  * pll->vt_lanes * l,
 				  pll->op_lanes * vt_op_binning_div
 				  * pll->scale_m);
 
@@ -406,6 +411,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	uint16_t min_op_pre_pll_clk_div;
 	uint16_t max_op_pre_pll_clk_div;
 	uint32_t mul, div;
+	uint32_t l = (!pll->op_bits_per_lane ||
+		      pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2;
 	uint32_t i;
 	int rval = -EINVAL;
 
@@ -444,7 +451,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	pll->pixel_rate_csi =
 		op_pll_bk->pix_clk_freq_hz
 		* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-		   pll->csi2.lanes : 1);
+		   pll->csi2.lanes : 1) / l;
 
 	/* Figure out limits for OP pre-pll divider based on extclk */
 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
@@ -482,7 +489,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
 		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
 		rval = __ccs_pll_calculate(dev, lim, op_lim_fr, op_lim_bk, pll,
-					   op_pll_fr, op_pll_bk, mul, div);
+					   op_pll_fr, op_pll_bk, mul, div, l);
 		if (rval)
 			continue;
 
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 807ae7250aa2..207822cf2324 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -76,6 +76,7 @@ struct ccs_pll_branch_bk {
  * @scale_m: Downscaling factor, M component, [16, max] (input)
  * @scale_n: Downscaling factor, N component, typically 16 (input)
  * @bits_per_pixel: Bits per pixel on the output data bus (input)
+ * @op_bits_per_lane: Number of bits per OP lane (input)
  * @flags: CCS_PLL_FLAG_* (input)
  * @link_freq: Chosen link frequency (input)
  * @ext_clk_freq_hz: External clock frequency, i.e. the sensor's input clock
@@ -100,6 +101,7 @@ struct ccs_pll {
 	uint8_t scale_m;
 	uint8_t scale_n;
 	uint8_t bits_per_pixel;
+	uint8_t op_bits_per_lane;
 	uint16_t flags;
 	uint32_t link_freq;
 	uint32_t ext_clk_freq_hz;
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 70f4f304fef6..83de43fa1880 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3223,6 +3223,7 @@ static int ccs_probe(struct i2c_client *client)
 	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
 	    CCS_CLOCK_TREE_PLL_CAPABILITY_EXT_DIVIDER)
 		sensor->pll.flags |= CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER;
+	sensor->pll.op_bits_per_lane = CCS_LIM(sensor, OP_BITS_PER_LANE);
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 21/38] ccs-pll: Add support flexible OP PLL pixel clock divider
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (19 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 20/38] ccs-pll: Support two cycles per pixel on OP domain Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 22/38] ccs-pll: Add sanity checks Sakari Ailus
                   ` (16 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Flexible OP PLL pixel clock divider allows a higher OP pixel clock than
what the bus can transfer. This generally makes it easier to select pixel
clock dividers.

This changes how the pixel rate on the bus and minimum VT divisor are
calculated, as the pixel rate is no longer directly determined by the
OP pixel clock and the number of the lanes.

Also add a sanity check for sensors that do not support flexible OP PLL
pixel clock divider. This could have caused the PLL calculator to come up
with an invalid configuration for those devices.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c      | 26 +++++++++++++++++++-------
 drivers/media/i2c/ccs-pll.h      |  1 +
 drivers/media/i2c/ccs/ccs-core.c |  4 +++-
 3 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 60ab0c043c7a..e3a6493fd601 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -294,11 +294,11 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	 * 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_bk->pix_clk_div
-				  * op_pll_bk->sys_clk_div * pll->scale_n
-				  * pll->vt_lanes * l,
-				  pll->op_lanes * vt_op_binning_div
-				  * pll->scale_m);
+	min_vt_div = DIV_ROUND_UP(pll->bits_per_pixel * op_pll_bk->sys_clk_div
+				  * pll->scale_n * pll->vt_lanes,
+				  (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
+				   pll->csi2.lanes : 1)
+				  * 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);
@@ -420,6 +420,18 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		pll->op_lanes = 1;
 		pll->vt_lanes = 1;
 	}
+
+	/*
+	 * Make sure op_pix_clk_div will be integer --- unless flexible
+	 * op_pix_clk_div is supported
+	 */
+	if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) &&
+	    (pll->bits_per_pixel * pll->op_lanes) % (pll->csi2.lanes * l)) {
+		dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n",
+			pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l);
+		return -EINVAL;
+	}
+
 	dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes);
 	dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes);
 
@@ -449,9 +461,9 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	}
 
 	pll->pixel_rate_csi =
-		op_pll_bk->pix_clk_freq_hz
+		op_pll_bk->sys_clk_freq_hz
 		* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-		   pll->csi2.lanes : 1) / l;
+		   pll->csi2.lanes : 1) / pll->bits_per_pixel / l;
 
 	/* Figure out limits for OP pre-pll divider based on extclk */
 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 207822cf2324..1b5c20736fe8 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -26,6 +26,7 @@
 #define CCS_PLL_FLAG_LANE_SPEED_MODEL				BIT(2)
 #define CCS_PLL_FLAG_LINK_DECOUPLED				BIT(3)
 #define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER				BIT(4)
+#define CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV			BIT(5)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 83de43fa1880..95d274834f0e 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -30,7 +30,6 @@
 #include <media/v4l2-device.h>
 
 #include "ccs.h"
-#include "ccs-limits.h"
 
 #define CCS_ALIGN_DIM(dim, flags)	\
 	((flags) & V4L2_SEL_FLAG_GE	\
@@ -3223,6 +3222,9 @@ static int ccs_probe(struct i2c_client *client)
 	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
 	    CCS_CLOCK_TREE_PLL_CAPABILITY_EXT_DIVIDER)
 		sensor->pll.flags |= CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER;
+	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
+	    CCS_CLOCK_TREE_PLL_CAPABILITY_FLEXIBLE_OP_PIX_CLK_DIV)
+		sensor->pll.flags |= CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV;
 	sensor->pll.op_bits_per_lane = CCS_LIM(sensor, OP_BITS_PER_LANE);
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 22/38] ccs-pll: Add sanity checks
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (20 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 21/38] ccs-pll: Add support flexible OP PLL pixel clock divider Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 23/38] ccs-pll: Add C-PHY support Sakari Ailus
                   ` (15 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Add sanity checks for fields that could cause division by zero.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index e3a6493fd601..420d2c4d7b2c 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -421,6 +421,15 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		pll->vt_lanes = 1;
 	}
 
+	if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel ||
+	    !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m ||
+	    !op_lim_fr->min_pll_ip_clk_freq_hz ||
+	    !op_lim_fr->max_pll_ip_clk_freq_hz ||
+	    !op_lim_fr->min_pll_op_clk_freq_hz ||
+	    !op_lim_fr->max_pll_op_clk_freq_hz ||
+	    !op_lim_bk->max_sys_clk_div || !op_lim_fr->max_pll_multiplier)
+		return -EINVAL;
+
 	/*
 	 * Make sure op_pix_clk_div will be integer --- unless flexible
 	 * op_pix_clk_div is supported
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 23/38] ccs-pll: Add C-PHY support
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (21 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 22/38] ccs-pll: Add sanity checks Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 24/38] ccs-pll: Split off VT subtree calculation Sakari Ailus
                   ` (14 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Add C-PHY support for the CCS PLL calculator.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 420d2c4d7b2c..0fa7c6f67195 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -145,6 +145,10 @@ static int check_all_bounds(struct device *dev,
 	return rval;
 }
 
+#define CPHY_CONST		7
+#define DPHY_CONST		16
+#define PHY_CONST_DIV		16
+
 /*
  * Heuristically guess the PLL tree for a given common multiplier and
  * divisor. Begin with the operational timing and continue to video
@@ -162,7 +166,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
 		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
 		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
-		    uint32_t div, uint32_t l)
+		    uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
 {
 	uint32_t sys_div;
 	uint32_t best_pix_div = INT_MAX >> 1;
@@ -254,9 +258,11 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 
 	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
 		op_pll_bk->pix_clk_div = pll->bits_per_pixel
-			* pll->op_lanes / pll->csi2.lanes / l;
+			* pll->op_lanes * phy_const
+			/ PHY_CONST_DIV / pll->csi2.lanes / l;
 	else
-		op_pll_bk->pix_clk_div = pll->bits_per_pixel / l;
+		op_pll_bk->pix_clk_div =
+			pll->bits_per_pixel * phy_const / PHY_CONST_DIV / l;
 
 	op_pll_bk->pix_clk_freq_hz =
 		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
@@ -295,10 +301,11 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	 */
 	dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
 	min_vt_div = DIV_ROUND_UP(pll->bits_per_pixel * op_pll_bk->sys_clk_div
-				  * pll->scale_n * pll->vt_lanes,
+				  * pll->scale_n * pll->vt_lanes * phy_const,
 				  (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
 				   pll->csi2.lanes : 1)
-				  * vt_op_binning_div * pll->scale_m);
+				  * vt_op_binning_div * pll->scale_m
+				  * PHY_CONST_DIV);
 
 	/* Find smallest and biggest allowed vt divisor. */
 	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
@@ -408,6 +415,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	const struct ccs_pll_branch_limits_bk *op_lim_bk = &lim->op_bk;
 	struct ccs_pll_branch_fr *op_pll_fr = &pll->vt_fr;
 	struct ccs_pll_branch_bk *op_pll_bk = &pll->op_bk;
+	bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY;
+	uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST;
 	uint16_t min_op_pre_pll_clk_div;
 	uint16_t max_op_pre_pll_clk_div;
 	uint32_t mul, div;
@@ -465,14 +474,21 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
 			   1 : pll->csi2.lanes);
 		break;
+	case CCS_PLL_BUS_TYPE_CSI2_CPHY:
+		op_pll_bk->sys_clk_freq_hz =
+			pll->link_freq
+			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
+			   1 : pll->csi2.lanes);
+		break;
 	default:
 		return -EINVAL;
 	}
 
 	pll->pixel_rate_csi =
-		op_pll_bk->sys_clk_freq_hz
-		* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-		   pll->csi2.lanes : 1) / pll->bits_per_pixel / l;
+		div_u64((uint64_t)op_pll_bk->sys_clk_freq_hz
+			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
+			   pll->csi2.lanes : 1) * PHY_CONST_DIV,
+			phy_const * pll->bits_per_pixel * l);
 
 	/* Figure out limits for OP pre-pll divider based on extclk */
 	dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n",
@@ -510,7 +526,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
 		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
 		rval = __ccs_pll_calculate(dev, lim, op_lim_fr, op_lim_bk, pll,
-					   op_pll_fr, op_pll_bk, mul, div, l);
+					   op_pll_fr, op_pll_bk, mul, div, l,
+					   cphy, phy_const);
 		if (rval)
 			continue;
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 24/38] ccs-pll: Split off VT subtree calculation
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (22 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 23/38] ccs-pll: Add C-PHY support Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 25/38] ccs-pll: Check for derating and overrating, support non-derating sensors Sakari Ailus
                   ` (13 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Split off the VT sub clock tree calculation from the rest, into its own
function. Also call the op_pll_fr argument pll_fr, since soon these may
not be OP tree values.

This paves way for additional features in the future such as dual PLL
support.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 255 ++++++++++++++++++------------------
 1 file changed, 131 insertions(+), 124 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 0fa7c6f67195..3d43e737155f 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -149,130 +149,18 @@ static int check_all_bounds(struct device *dev,
 #define DPHY_CONST		16
 #define PHY_CONST_DIV		16
 
-/*
- * 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 *lim,
-		    const struct ccs_pll_branch_limits_fr *op_lim_fr,
-		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
-		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
-		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
-		    uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
+static void
+__ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
+		       const struct ccs_pll_branch_limits_bk *op_lim_bk,
+		       struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
+		       struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
+		       uint32_t phy_const)
 {
 	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;
-	uint32_t i;
-
-	/*
-	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
-	 * too high.
-	 */
-	dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div);
-
-	/* Don't go above max pll multiplier. */
-	more_mul_max = op_lim_fr->max_pll_multiplier / mul;
-	dev_dbg(dev, "more_mul_max: max_op_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,
-		      op_lim_fr->max_pll_op_clk_freq_hz
-		      / (pll->ext_clk_freq_hz /
-			 op_pll_fr->pre_pll_clk_div * mul));
-	dev_dbg(dev, "more_mul_max: max_pll_op_clk_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_lim_bk->max_sys_clk_div * op_pll_fr->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 max_pll_multiplier. */
-	more_mul_max = min(more_mul_max, op_lim_fr->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_clk_freq_hz. */
-	more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz,
-				    pll->ext_clk_freq_hz /
-				    op_pll_fr->pre_pll_clk_div * mul);
-	dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_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(op_lim_fr->min_pll_multiplier, mul));
-	dev_dbg(dev, "more_mul_min: min_op_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, op_pll_fr->pre_pll_clk_div) / div;
-	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
-	more_mul_factor = lcm(more_mul_factor, op_lim_bk->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;
-	}
-
-	op_pll_fr->pll_multiplier = mul * i;
-	op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div;
-	dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div);
-
-	op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
-		/ op_pll_fr->pre_pll_clk_div;
-
-	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
-		* op_pll_fr->pll_multiplier;
-
-	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
-		op_pll_bk->pix_clk_div = pll->bits_per_pixel
-			* pll->op_lanes * phy_const
-			/ PHY_CONST_DIV / pll->csi2.lanes / l;
-	else
-		op_pll_bk->pix_clk_div =
-			pll->bits_per_pixel * phy_const / PHY_CONST_DIV / l;
-
-	op_pll_bk->pix_clk_freq_hz =
-		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
-
-	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->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
@@ -310,7 +198,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	/* 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(op_pll_fr->pll_op_clk_freq_hz,
+			 DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
 				      lim->vt_bk.max_pix_clk_freq_hz));
 	dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n",
 		min_vt_div);
@@ -322,7 +210,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.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(op_pll_fr->pll_op_clk_freq_hz,
+			 DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
 				      lim->vt_bk.min_pix_clk_freq_hz));
 	dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
 		max_vt_div);
@@ -338,7 +226,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 				       lim->vt_bk.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,
-			  op_pll_fr->pll_op_clk_freq_hz
+			  pll_fr->pll_op_clk_freq_hz
 			  / lim->vt_bk.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);
@@ -351,7 +239,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 				       lim->vt_bk.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(op_pll_fr->pll_op_clk_freq_hz,
+			  DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
 				       lim->vt_bk.min_pix_clk_freq_hz));
 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
 
@@ -396,11 +284,130 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	pll->vt_bk.pix_clk_div = best_pix_div;
 
 	pll->vt_bk.sys_clk_freq_hz =
-		op_pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
+		pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
 	pll->vt_bk.pix_clk_freq_hz =
 		pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div;
+}
+
+/*
+ * 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 *lim,
+		    const struct ccs_pll_branch_limits_fr *op_lim_fr,
+		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
+		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
+		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
+		    uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
+{
+	/*
+	 * 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 i;
+
+	/*
+	 * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
+	 * too high.
+	 */
+	dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div);
+
+	/* Don't go above max pll multiplier. */
+	more_mul_max = op_lim_fr->max_pll_multiplier / mul;
+	dev_dbg(dev, "more_mul_max: max_op_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,
+		      op_lim_fr->max_pll_op_clk_freq_hz
+		      / (pll->ext_clk_freq_hz /
+			 op_pll_fr->pre_pll_clk_div * mul));
+	dev_dbg(dev, "more_mul_max: max_pll_op_clk_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_lim_bk->max_sys_clk_div * op_pll_fr->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 max_pll_multiplier. */
+	more_mul_max = min(more_mul_max, op_lim_fr->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_clk_freq_hz. */
+	more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz,
+				    pll->ext_clk_freq_hz /
+				    op_pll_fr->pre_pll_clk_div * mul);
+	dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_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(op_lim_fr->min_pll_multiplier, mul));
+	dev_dbg(dev, "more_mul_min: min_op_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, op_pll_fr->pre_pll_clk_div) / div;
+	dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
+	more_mul_factor = lcm(more_mul_factor, op_lim_bk->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;
+	}
+
+	op_pll_fr->pll_multiplier = mul * i;
+	op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div;
+	dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div);
+
+	op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
+		/ op_pll_fr->pre_pll_clk_div;
+
+	op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz
+		* op_pll_fr->pll_multiplier;
+
+	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
+		op_pll_bk->pix_clk_div = pll->bits_per_pixel
+			* pll->op_lanes * phy_const
+			/ PHY_CONST_DIV / pll->csi2.lanes / l;
+	else
+		op_pll_bk->pix_clk_div =
+			pll->bits_per_pixel * phy_const / PHY_CONST_DIV / l;
+
+	op_pll_bk->pix_clk_freq_hz =
+		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
+
+	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
+
+	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS))
+		__ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
+				       op_pll_bk, cphy, phy_const);
 
-out_skip_vt_calc:
 	pll->pixel_rate_pixel_array =
 		pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 25/38] ccs-pll: Check for derating and overrating, support non-derating sensors
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (23 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 24/38] ccs-pll: Split off VT subtree calculation Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 26/38] ccs-pll: Better separate OP and VT sub-tree calculation Sakari Ailus
                   ` (12 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Some sensors support derating (VT domain speed faster than OP) or
overrating (VT domain speed slower than OP). While this was supported for
the driver, the hardware support for the feature was never verified. Do
that now, and for those devices without that support, VT and OP speeds
have to match.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c      | 84 +++++++++++++++++++++-----------
 drivers/media/i2c/ccs-pll.h      |  2 +
 drivers/media/i2c/ccs/ccs-core.c |  7 +++
 3 files changed, 64 insertions(+), 29 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 3d43e737155f..96eb71f16c81 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -142,6 +142,18 @@ static int check_all_bounds(struct device *dev,
 			lim->vt_bk.max_pix_clk_freq_hz,
 			"vt_pix_clk_freq_hz");
 
+	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) &&
+	    pll->pixel_rate_pixel_array > pll->pixel_rate_csi) {
+		dev_dbg(dev, "device does not support derating\n");
+		return -EINVAL;
+	}
+
+	if (!(pll->flags & CCS_PLL_FLAG_FIFO_OVERRATING) &&
+	    pll->pixel_rate_pixel_array < pll->pixel_rate_csi) {
+		dev_dbg(dev, "device does not support overrating\n");
+		return -EINVAL;
+	}
+
 	return rval;
 }
 
@@ -163,37 +175,51 @@ __ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 	uint32_t min_sys_div, max_sys_div;
 
 	/*
-	 * 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.
+	 * Find out whether a sensor supports derating. If it does not, VT and
+	 * OP domains are required to run at the same pixel rate.
 	 */
-	if (lim->min_line_length_pck_bin > lim->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);
+	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) {
+		min_vt_div =
+			op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div
+			* pll->vt_lanes * phy_const
+			/ pll->op_lanes / PHY_CONST_DIV;
+	} else {
+		/*
+		 * 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 (lim->min_line_length_pck_bin > lim->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 and number of VT lanes 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(pll->bits_per_pixel * op_pll_bk->sys_clk_div
-				  * pll->scale_n * pll->vt_lanes * phy_const,
-				  (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-				   pll->csi2.lanes : 1)
-				  * vt_op_binning_div * pll->scale_m
-				  * PHY_CONST_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 and number of VT lanes 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(pll->bits_per_pixel
+				     * op_pll_bk->sys_clk_div * pll->scale_n
+				     * pll->vt_lanes * phy_const,
+				     (pll->flags &
+				      CCS_PLL_FLAG_LANE_SPEED_MODEL ?
+				      pll->csi2.lanes : 1)
+				     * vt_op_binning_div * pll->scale_m
+				     * PHY_CONST_DIV);
+	}
 
 	/* Find smallest and biggest allowed vt divisor. */
 	dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 1b5c20736fe8..03c2428c76cd 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -27,6 +27,8 @@
 #define CCS_PLL_FLAG_LINK_DECOUPLED				BIT(3)
 #define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER				BIT(4)
 #define CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV			BIT(5)
+#define CCS_PLL_FLAG_FIFO_DERATING				BIT(6)
+#define CCS_PLL_FLAG_FIFO_OVERRATING				BIT(7)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 95d274834f0e..849c1001e3cc 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3225,6 +3225,13 @@ static int ccs_probe(struct i2c_client *client)
 	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
 	    CCS_CLOCK_TREE_PLL_CAPABILITY_FLEXIBLE_OP_PIX_CLK_DIV)
 		sensor->pll.flags |= CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV;
+	if (CCS_LIM(sensor, FIFO_SUPPORT_CAPABILITY) &
+	    CCS_FIFO_SUPPORT_CAPABILITY_DERATING)
+		sensor->pll.flags |= CCS_PLL_FLAG_FIFO_DERATING;
+	if (CCS_LIM(sensor, FIFO_SUPPORT_CAPABILITY) &
+	    CCS_FIFO_SUPPORT_CAPABILITY_DERATING_OVERRATING)
+		sensor->pll.flags |= CCS_PLL_FLAG_FIFO_DERATING |
+				     CCS_PLL_FLAG_FIFO_OVERRATING;
 	sensor->pll.op_bits_per_lane = CCS_LIM(sensor, OP_BITS_PER_LANE);
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 26/38] ccs-pll: Better separate OP and VT sub-tree calculation
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (24 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 25/38] ccs-pll: Check for derating and overrating, support non-derating sensors Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 27/38] ccs-pll: Print relevant information on PLL tree Sakari Ailus
                   ` (11 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Better separate OP PLL branch calculation from VT branch calculation.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 54 +++++++++++++++++++++----------------
 1 file changed, 31 insertions(+), 23 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 96eb71f16c81..e879c03a3d3b 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -162,11 +162,11 @@ static int check_all_bounds(struct device *dev,
 #define PHY_CONST_DIV		16
 
 static void
-__ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
-		       const struct ccs_pll_branch_limits_bk *op_lim_bk,
-		       struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
-		       struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
-		       uint32_t phy_const)
+ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
+		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
+		     struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
+		     struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
+		     uint32_t phy_const)
 {
 	uint32_t sys_div;
 	uint32_t best_pix_div = INT_MAX >> 1;
@@ -174,6 +174,9 @@ __ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 	uint32_t min_vt_div, max_vt_div, vt_div;
 	uint32_t min_sys_div, max_sys_div;
 
+	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
+		goto out_calc_pixel_rate;
+
 	/*
 	 * Find out whether a sensor supports derating. If it does not, VT and
 	 * OP domains are required to run at the same pixel rate.
@@ -313,6 +316,10 @@ __ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 		pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div;
 	pll->vt_bk.pix_clk_freq_hz =
 		pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div;
+
+out_calc_pixel_rate:
+	pll->pixel_rate_pixel_array =
+		pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
 }
 
 /*
@@ -327,12 +334,12 @@ __ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
  * @return Zero on success, error code on error.
  */
 static int
-__ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
-		    const struct ccs_pll_branch_limits_fr *op_lim_fr,
-		    const struct ccs_pll_branch_limits_bk *op_lim_bk,
-		    struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
-		    struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
-		    uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
+ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim,
+		     const struct ccs_pll_branch_limits_fr *op_lim_fr,
+		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
+		     struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
+		     struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
+		     uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
 {
 	/*
 	 * Higher multipliers (and divisors) are often required than
@@ -430,15 +437,7 @@ __ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 
 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
 
-	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS))
-		__ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
-				       op_pll_bk, cphy, phy_const);
-
-	pll->pixel_rate_pixel_array =
-		pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes;
-
-	return check_all_bounds(dev, lim, op_lim_fr, op_lim_bk, pll, op_pll_fr,
-				op_pll_bk);
+	return 0;
 }
 
 int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
@@ -558,13 +557,22 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	     op_pll_fr->pre_pll_clk_div +=
 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
 		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
-		rval = __ccs_pll_calculate(dev, lim, op_lim_fr, op_lim_bk, pll,
-					   op_pll_fr, op_pll_bk, mul, div, l,
-					   cphy, phy_const);
+		rval = ccs_pll_calculate_op(dev, lim, op_lim_fr, op_lim_bk, pll,
+					    op_pll_fr, op_pll_bk, mul, div, l,
+					    cphy, phy_const);
+		if (rval)
+			continue;
+
+		ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
+				     op_pll_bk, cphy, phy_const);
+
+		rval = check_all_bounds(dev, lim, op_lim_fr, op_lim_bk, pll,
+					op_pll_fr, op_pll_bk);
 		if (rval)
 			continue;
 
 		print_pll(dev, pll);
+
 		return 0;
 	}
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 27/38] ccs-pll: Print relevant information on PLL tree
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (25 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 26/38] ccs-pll: Better separate OP and VT sub-tree calculation Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 28/38] ccs-pll: Rework bounds checks Sakari Ailus
                   ` (10 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Print information on PLL tree configuration based on the flags. This also
adds support for printing dual PLL trees, and better separates between OP
and VT PLL trees.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 85 ++++++++++++++++++++++++++++---------
 1 file changed, 66 insertions(+), 19 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index e879c03a3d3b..43735f6d0bb2 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -56,28 +56,75 @@ static int bounds_check(struct device *dev, uint32_t val,
 	return -EINVAL;
 }
 
-static void print_pll(struct device *dev, struct ccs_pll *pll)
+#define PLL_OP 1
+#define PLL_VT 2
+
+static const char *pll_string(unsigned int which)
 {
-	dev_dbg(dev, "pre_pll_clk_div\t%u\n",  pll->vt_fr.pre_pll_clk_div);
-	dev_dbg(dev, "pll_multiplier \t%u\n",  pll->vt_fr.pll_multiplier);
-	if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)) {
-		dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op_bk.sys_clk_div);
-		dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op_bk.pix_clk_div);
+	switch (which) {
+	case PLL_OP:
+		return "op";
+	case PLL_VT:
+		return "vt";
 	}
-	dev_dbg(dev, "vt_sys_clk_div \t%u\n",  pll->vt_bk.sys_clk_div);
-	dev_dbg(dev, "vt_pix_clk_div \t%u\n",  pll->vt_bk.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->vt_fr.pll_ip_clk_freq_hz);
-	dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->vt_fr.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_bk.sys_clk_freq_hz);
-		dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n",
-			pll->op_bk.pix_clk_freq_hz);
+
+	return NULL;
+}
+
+#define PLL_FL(f) CCS_PLL_FLAG_##f
+
+static void print_pll(struct device *dev, struct ccs_pll *pll)
+{
+	const struct {
+		struct ccs_pll_branch_fr *fr;
+		struct ccs_pll_branch_bk *bk;
+		unsigned int which;
+	} branches[] = {
+		{ &pll->vt_fr, &pll->vt_bk, PLL_VT },
+		{ NULL, &pll->op_bk, PLL_OP }
+	}, *br;
+	unsigned int i;
+
+	dev_dbg(dev, "ext_clk_freq_hz\t\t%u\n", pll->ext_clk_freq_hz);
+
+	for (i = 0, br = branches; i < ARRAY_SIZE(branches); i++, br++) {
+		const char *s = pll_string(br->which);
+
+		if (br->which == PLL_VT) {
+			dev_dbg(dev, "%s_pre_pll_clk_div\t\t%u\n",  s,
+				br->fr->pre_pll_clk_div);
+			dev_dbg(dev, "%s_pll_multiplier\t\t%u\n",  s,
+				br->fr->pll_multiplier);
+
+			dev_dbg(dev, "%s_pll_ip_clk_freq_hz\t%u\n", s,
+				br->fr->pll_ip_clk_freq_hz);
+			dev_dbg(dev, "%s_pll_op_clk_freq_hz\t%u\n", s,
+				br->fr->pll_op_clk_freq_hz);
+		}
+
+		if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) ||
+		    br->which == PLL_VT) {
+			dev_dbg(dev, "%s_sys_clk_div\t\t%u\n",  s,
+				br->bk->sys_clk_div);
+			dev_dbg(dev, "%s_pix_clk_div\t\t%u\n", s,
+				br->bk->pix_clk_div);
+
+			dev_dbg(dev, "%s_sys_clk_freq_hz\t%u\n", s,
+				br->bk->sys_clk_freq_hz);
+			dev_dbg(dev, "%s_pix_clk_freq_hz\t%u\n", s,
+				br->bk->pix_clk_freq_hz);
+		}
 	}
-	dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt_bk.sys_clk_freq_hz);
-	dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt_bk.pix_clk_freq_hz);
+
+	dev_dbg(dev, "flags%s%s%s%s%s%s\n",
+		pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "",
+		pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "",
+		pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ?
+		" ext-ip-pll-divider" : "",
+		pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ?
+		" flexible-op-pix-div" : "",
+		pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "",
+		pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "");
 }
 
 static int check_all_bounds(struct device *dev,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 28/38] ccs-pll: Rework bounds checks
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (26 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 27/38] ccs-pll: Print relevant information on PLL tree Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 29/38] ccs-pll: Make VT divisors 16-bit Sakari Ailus
                   ` (9 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Refactor bounds checks so that the caller can decide what to check. This
allows doing the checks early, when the values are available.

This also adds front OP PLL configuration and limits.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 148 ++++++++++++++++++++++--------------
 drivers/media/i2c/ccs-pll.h |   4 +
 2 files changed, 95 insertions(+), 57 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 43735f6d0bb2..a6443589b948 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -46,12 +46,14 @@ static inline uint32_t one_or_more(uint32_t a)
 }
 
 static int bounds_check(struct device *dev, uint32_t val,
-			uint32_t min, uint32_t max, char *str)
+			uint32_t min, uint32_t max, const char *prefix,
+			char *str)
 {
 	if (val >= min && val <= max)
 		return 0;
 
-	dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
+	dev_dbg(dev, "%s_%s out of bounds: %d (%d--%d)\n", prefix,
+		str, val, min, max);
 
 	return -EINVAL;
 }
@@ -81,7 +83,7 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 		unsigned int which;
 	} branches[] = {
 		{ &pll->vt_fr, &pll->vt_bk, PLL_VT },
-		{ NULL, &pll->op_bk, PLL_OP }
+		{ &pll->op_fr, &pll->op_bk, PLL_OP }
 	}, *br;
 	unsigned int i;
 
@@ -127,68 +129,90 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 		pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "");
 }
 
-static int check_all_bounds(struct device *dev,
-			    const struct ccs_pll_limits *lim,
-			    const struct ccs_pll_branch_limits_fr *op_lim_fr,
-			    const struct ccs_pll_branch_limits_bk *op_lim_bk,
-			    struct ccs_pll *pll,
-			    struct ccs_pll_branch_fr *op_pll_fr,
-			    struct ccs_pll_branch_bk *op_pll_bk)
+static int check_fr_bounds(struct device *dev,
+			   const struct ccs_pll_limits *lim,
+			   struct ccs_pll *pll, unsigned int which)
 {
+	const struct ccs_pll_branch_limits_fr *lim_fr;
+	struct ccs_pll_branch_fr *pll_fr;
+	const char *s = pll_string(which);
 	int rval;
 
-	rval = bounds_check(dev, op_pll_fr->pll_ip_clk_freq_hz,
-			    op_lim_fr->min_pll_ip_clk_freq_hz,
-			    op_lim_fr->max_pll_ip_clk_freq_hz,
-			    "pll_ip_clk_freq_hz");
-	if (!rval)
-		rval = bounds_check(
-			dev, op_pll_fr->pll_multiplier,
-			op_lim_fr->min_pll_multiplier,
-			op_lim_fr->max_pll_multiplier, "pll_multiplier");
-	if (!rval)
-		rval = bounds_check(
-			dev, op_pll_fr->pll_op_clk_freq_hz,
-			op_lim_fr->min_pll_op_clk_freq_hz,
-			op_lim_fr->max_pll_op_clk_freq_hz, "pll_op_clk_freq_hz");
+	if (which == PLL_OP) {
+		lim_fr = &lim->op_fr;
+		pll_fr = &pll->op_fr;
+	} else {
+		lim_fr = &lim->vt_fr;
+		pll_fr = &pll->vt_fr;
+	}
+
+	rval = bounds_check(dev, pll_fr->pre_pll_clk_div,
+			    lim_fr->min_pre_pll_clk_div,
+			    lim_fr->max_pre_pll_clk_div, s, "pre_pll_clk_div");
+
 	if (!rval)
-		rval = bounds_check(
-			dev, op_pll_bk->sys_clk_div,
-			op_lim_bk->min_sys_clk_div, op_lim_bk->max_sys_clk_div,
-			"op_sys_clk_div");
+		rval = bounds_check(dev, pll_fr->pll_ip_clk_freq_hz,
+				    lim_fr->min_pll_ip_clk_freq_hz,
+				    lim_fr->max_pll_ip_clk_freq_hz,
+				    s, "pll_ip_clk_freq_hz");
 	if (!rval)
-		rval = bounds_check(
-			dev, op_pll_bk->sys_clk_freq_hz,
-			op_lim_bk->min_sys_clk_freq_hz,
-			op_lim_bk->max_sys_clk_freq_hz,
-			"op_sys_clk_freq_hz");
+		rval = bounds_check(dev, pll_fr->pll_multiplier,
+				    lim_fr->min_pll_multiplier,
+				    lim_fr->max_pll_multiplier,
+				    s, "pll_multiplier");
 	if (!rval)
-		rval = bounds_check(
-			dev, op_pll_bk->pix_clk_freq_hz,
-			op_lim_bk->min_pix_clk_freq_hz,
-			op_lim_bk->max_pix_clk_freq_hz,
-			"op_pix_clk_freq_hz");
+		rval = bounds_check(dev, pll_fr->pll_op_clk_freq_hz,
+				    lim_fr->min_pll_op_clk_freq_hz,
+				    lim_fr->max_pll_op_clk_freq_hz,
+				    s, "pll_op_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;
+	return rval;
+}
+
+static int check_bk_bounds(struct device *dev,
+			   const struct ccs_pll_limits *lim,
+			   struct ccs_pll *pll, unsigned int which)
+{
+	const struct ccs_pll_branch_limits_bk *lim_bk;
+	struct ccs_pll_branch_bk *pll_bk;
+	const char *s = pll_string(which);
+	int rval;
+
+	if (which == PLL_OP) {
+		if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
+			return 0;
 
+		lim_bk = &lim->op_bk;
+		pll_bk = &pll->op_bk;
+	} else {
+		lim_bk = &lim->vt_bk;
+		pll_bk = &pll->vt_bk;
+	}
+
+	rval = bounds_check(dev, pll_bk->sys_clk_div,
+			    lim_bk->min_sys_clk_div,
+			    lim_bk->max_sys_clk_div, s, "op_sys_clk_div");
+	if (!rval)
+		rval = bounds_check(dev, pll_bk->sys_clk_freq_hz,
+				    lim_bk->min_sys_clk_freq_hz,
+				    lim_bk->max_sys_clk_freq_hz,
+				    s, "sys_clk_freq_hz");
 	if (!rval)
-		rval = bounds_check(
-			dev, pll->vt_bk.sys_clk_freq_hz,
-			lim->vt_bk.min_sys_clk_freq_hz,
-			lim->vt_bk.max_sys_clk_freq_hz,
-			"vt_sys_clk_freq_hz");
+		rval = bounds_check(dev, pll_bk->sys_clk_div,
+				    lim_bk->min_sys_clk_div,
+				    lim_bk->max_sys_clk_div,
+				    s, "sys_clk_div");
 	if (!rval)
-		rval = bounds_check(
-			dev, pll->vt_bk.pix_clk_freq_hz,
-			lim->vt_bk.min_pix_clk_freq_hz,
-			lim->vt_bk.max_pix_clk_freq_hz,
-			"vt_pix_clk_freq_hz");
+		rval = bounds_check(dev, pll_bk->pix_clk_freq_hz,
+				    lim_bk->min_pix_clk_freq_hz,
+				    lim_bk->max_pix_clk_freq_hz,
+				    s, "pix_clk_freq_hz");
+
+	return rval;
+}
 
+static int check_ext_bounds(struct device *dev, struct ccs_pll *pll)
+{
 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) &&
 	    pll->pixel_rate_pixel_array > pll->pixel_rate_csi) {
 		dev_dbg(dev, "device does not support derating\n");
@@ -201,7 +225,7 @@ static int check_all_bounds(struct device *dev,
 		return -EINVAL;
 	}
 
-	return rval;
+	return 0;
 }
 
 #define CPHY_CONST		7
@@ -610,11 +634,21 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		if (rval)
 			continue;
 
+		rval = check_fr_bounds(dev, lim, pll, PLL_VT);
+		if (rval)
+			continue;
+
+		rval = check_bk_bounds(dev, lim, pll, PLL_OP);
+		if (rval)
+			continue;
+
 		ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
 				     op_pll_bk, cphy, phy_const);
 
-		rval = check_all_bounds(dev, lim, op_lim_fr, op_lim_bk, pll,
-					op_pll_fr, op_pll_bk);
+		rval = check_bk_bounds(dev, lim, pll, PLL_VT);
+		if (rval)
+			continue;
+		rval = check_ext_bounds(dev, pll);
 		if (rval)
 			continue;
 
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 03c2428c76cd..6255803eee38 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -86,6 +86,7 @@ struct ccs_pll_branch_bk {
  *		     (input)
  * @vt_fr: Video timing front-end configuration (output)
  * @vt_bk: Video timing back-end configuration (output)
+ * @op_fr: Operational timing front-end configuration (output)
  * @op_bk: Operational timing back-end configuration (output)
  * @pixel_rate_csi: Pixel rate on the output data bus (output)
  * @pixel_rate_pixel_array: Nominal pixel rate in the sensor's pixel array
@@ -112,6 +113,7 @@ struct ccs_pll {
 	/* output values */
 	struct ccs_pll_branch_fr vt_fr;
 	struct ccs_pll_branch_bk vt_bk;
+	struct ccs_pll_branch_fr op_fr;
 	struct ccs_pll_branch_bk op_bk;
 
 	uint32_t pixel_rate_csi;
@@ -171,6 +173,7 @@ struct ccs_pll_branch_limits_bk {
  * @max_ext_clk_freq_hz: Maximum external clock frequency
  * @vt_fr: Video timing front-end limits
  * @vt_bk: Video timing back-end limits
+ * @op_fr: Operational timing front-end limits
  * @op_bk: Operational timing back-end limits
  * @min_line_length_pck_bin: Minimum line length in pixels, with binning
  * @min_line_length_pck: Minimum line length in pixels without binning
@@ -182,6 +185,7 @@ struct ccs_pll_limits {
 
 	struct ccs_pll_branch_limits_fr vt_fr;
 	struct ccs_pll_branch_limits_bk vt_bk;
+	struct ccs_pll_branch_limits_fr op_fr;
 	struct ccs_pll_branch_limits_bk op_bk;
 
 	/* Other relevant limits */
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 29/38] ccs-pll: Make VT divisors 16-bit
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (27 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 28/38] ccs-pll: Rework bounds checks Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 30/38] ccs-pll: Fix VT post-PLL divisor calculation Sakari Ailus
                   ` (8 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Make VT divisors 16-bit unsigned numbers. They don't need 32 bits after
all.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 51 ++++++++++++++++++-------------------
 1 file changed, 25 insertions(+), 26 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index a6443589b948..7512f0313010 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -239,11 +239,11 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 		     struct ccs_pll_branch_bk *op_pll_bk, bool cphy,
 		     uint32_t phy_const)
 {
-	uint32_t sys_div;
-	uint32_t best_pix_div = INT_MAX >> 1;
-	uint32_t vt_op_binning_div;
-	uint32_t min_vt_div, max_vt_div, vt_div;
-	uint32_t min_sys_div, max_sys_div;
+	uint16_t sys_div;
+	uint16_t best_pix_div = SHRT_MAX >> 1;
+	uint16_t vt_op_binning_div;
+	uint16_t min_vt_div, max_vt_div, vt_div;
+	uint16_t min_sys_div, max_sys_div;
 
 	if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
 		goto out_calc_pixel_rate;
@@ -297,20 +297,19 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 
 	/* 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_fr->pll_op_clk_freq_hz,
-				      lim->vt_bk.max_pix_clk_freq_hz));
+	min_vt_div = max_t(uint16_t, min_vt_div,
+			   DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
+					lim->vt_bk.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,
-			   lim->vt_bk.min_pix_clk_div
-			   * lim->vt_bk.min_sys_clk_div);
+	min_vt_div = max_t(uint16_t, min_vt_div, lim->vt_bk.min_pix_clk_div
+						 * lim->vt_bk.min_sys_clk_div);
 	dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div);
 
 	max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.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_fr->pll_op_clk_freq_hz,
+	max_vt_div = min_t(uint16_t, max_vt_div,
+			   DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
 				      lim->vt_bk.min_pix_clk_freq_hz));
 	dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
 		max_vt_div);
@@ -321,26 +320,26 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 	 */
 	min_sys_div = lim->vt_bk.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,
-				       lim->vt_bk.max_pix_clk_div));
+	min_sys_div = max_t(uint16_t, min_sys_div,
+			    DIV_ROUND_UP(min_vt_div,
+					 lim->vt_bk.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_fr->pll_op_clk_freq_hz
-			  / lim->vt_bk.max_sys_clk_freq_hz);
+	min_sys_div = max_t(uint16_t, min_sys_div,
+			    pll_fr->pll_op_clk_freq_hz
+			    / lim->vt_bk.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 = lim->vt_bk.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,
-				       lim->vt_bk.min_pix_clk_div));
+	max_sys_div = min_t(uint16_t, max_sys_div,
+			    DIV_ROUND_UP(max_vt_div,
+					 lim->vt_bk.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_fr->pll_op_clk_freq_hz,
-				       lim->vt_bk.min_pix_clk_freq_hz));
+	max_sys_div = min_t(uint16_t, max_sys_div,
+			    DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
+					 lim->vt_bk.min_pix_clk_freq_hz));
 	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
 
 	/*
@@ -376,7 +375,7 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 			if (vt_div == rounded_div)
 				break;
 		}
-		if (best_pix_div < INT_MAX >> 1)
+		if (best_pix_div < SHRT_MAX >> 1)
 			break;
 	}
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 30/38] ccs-pll: Fix VT post-PLL divisor calculation
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (28 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 29/38] ccs-pll: Make VT divisors 16-bit Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 31/38] ccs-pll: Separate VT divisor limit calculation from the rest Sakari Ailus
                   ` (7 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

The PLL calculator only searched even total divisor values apart from one,
but this is wrong: the total divisor is odd in cases where system divisor
is one. Fix this by including odd total PLL values where system divisor is
one to the search.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 7512f0313010..4321989b9013 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -347,14 +347,16 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 	 * 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;
+	for (vt_div = min_vt_div; vt_div <= max_vt_div; vt_div++) {
+		uint16_t __max_sys_div = vt_div & 1 ? 1 : max_sys_div;
+
+		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);
+			uint16_t pix_div;
 			uint16_t rounded_div;
 
+			pix_div = DIV_ROUND_UP(vt_div, sys_div);
+
 			if (pix_div < lim->vt_bk.min_pix_clk_div
 			    || pix_div > lim->vt_bk.max_pix_clk_div) {
 				dev_dbg(dev,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 31/38] ccs-pll: Separate VT divisor limit calculation from the rest
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (29 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 30/38] ccs-pll: Fix VT post-PLL divisor calculation Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 32/38] ccs-pll: Add trivial dual PLL support Sakari Ailus
                   ` (6 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Separate VT divisor limit calculation from the rest of the VT PLL branch
calculation. This way it can be used for dual PLL support as well.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 64 +++++++++++++++++++++----------------
 1 file changed, 37 insertions(+), 27 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 4321989b9013..8b300e786451 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -228,6 +228,41 @@ static int check_ext_bounds(struct device *dev, struct ccs_pll *pll)
 	return 0;
 }
 
+static void
+ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim,
+			struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr,
+			uint16_t min_vt_div, uint16_t max_vt_div,
+			uint16_t *min_sys_div, uint16_t *max_sys_div)
+{
+	/*
+	 * Find limits for sys_clk_div. Not all values are possible with all
+	 * values of pix_clk_div.
+	 */
+	*min_sys_div = lim->vt_bk.min_sys_clk_div;
+	dev_dbg(dev, "min_sys_div: %u\n", *min_sys_div);
+	*min_sys_div = max_t(uint16_t, *min_sys_div,
+			     DIV_ROUND_UP(min_vt_div,
+					  lim->vt_bk.max_pix_clk_div));
+	dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", *min_sys_div);
+	*min_sys_div = max_t(uint16_t, *min_sys_div,
+			     pll_fr->pll_op_clk_freq_hz
+			     / lim->vt_bk.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 = lim->vt_bk.max_sys_clk_div;
+	dev_dbg(dev, "max_sys_div: %u\n", *max_sys_div);
+	*max_sys_div = min_t(uint16_t, *max_sys_div,
+			     DIV_ROUND_UP(max_vt_div,
+					  lim->vt_bk.min_pix_clk_div));
+	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", *max_sys_div);
+	*max_sys_div = min_t(uint16_t, *max_sys_div,
+			     DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
+					  lim->vt_bk.min_pix_clk_freq_hz));
+	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", *max_sys_div);
+}
+
 #define CPHY_CONST		7
 #define DPHY_CONST		16
 #define PHY_CONST_DIV		16
@@ -314,33 +349,8 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 	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 = lim->vt_bk.min_sys_clk_div;
-	dev_dbg(dev, "min_sys_div: %u\n", min_sys_div);
-	min_sys_div = max_t(uint16_t, min_sys_div,
-			    DIV_ROUND_UP(min_vt_div,
-					 lim->vt_bk.max_pix_clk_div));
-	dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div);
-	min_sys_div = max_t(uint16_t, min_sys_div,
-			    pll_fr->pll_op_clk_freq_hz
-			    / lim->vt_bk.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 = lim->vt_bk.max_sys_clk_div;
-	dev_dbg(dev, "max_sys_div: %u\n", max_sys_div);
-	max_sys_div = min_t(uint16_t, max_sys_div,
-			    DIV_ROUND_UP(max_vt_div,
-					 lim->vt_bk.min_pix_clk_div));
-	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div);
-	max_sys_div = min_t(uint16_t, max_sys_div,
-			    DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz,
-					 lim->vt_bk.min_pix_clk_freq_hz));
-	dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
+	ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, min_vt_div,
+				max_vt_div, &min_sys_div, &max_sys_div);
 
 	/*
 	 * Find pix_div such that a legal pix_div * sys_div results
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 32/38] ccs-pll: Add trivial dual PLL support
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (30 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 31/38] ccs-pll: Separate VT divisor limit calculation from the rest Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 33/38] ccs: Dual " Sakari Ailus
                   ` (5 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Add support for sensors that have separate VT and OP domain PLLs.

This support is trivial in the sense that it aims for the same VT pixel
rate than that on the CSI-2 bus. The vast majority of sensors is better
supported by higher frequencies in VT domain in binned and possibly scaled
configurations.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 217 ++++++++++++++++++++++++++++++++----
 drivers/media/i2c/ccs-pll.h |   1 +
 2 files changed, 196 insertions(+), 22 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 8b300e786451..91b578a05a98 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -92,7 +92,8 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 	for (i = 0, br = branches; i < ARRAY_SIZE(branches); i++, br++) {
 		const char *s = pll_string(br->which);
 
-		if (br->which == PLL_VT) {
+		if (pll->flags & CCS_PLL_FLAG_DUAL_PLL ||
+		    br->which == PLL_VT) {
 			dev_dbg(dev, "%s_pre_pll_clk_div\t\t%u\n",  s,
 				br->fr->pre_pll_clk_div);
 			dev_dbg(dev, "%s_pll_multiplier\t\t%u\n",  s,
@@ -118,7 +119,7 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 		}
 	}
 
-	dev_dbg(dev, "flags%s%s%s%s%s%s\n",
+	dev_dbg(dev, "flags%s%s%s%s%s%s%s\n",
 		pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "",
 		pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "",
 		pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ?
@@ -126,7 +127,8 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 		pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ?
 		" flexible-op-pix-div" : "",
 		pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "",
-		pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "");
+		pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "",
+		pll->flags & PLL_FL(DUAL_PLL) ? " dual-pll" : "");
 }
 
 static int check_fr_bounds(struct device *dev,
@@ -267,6 +269,152 @@ ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim,
 #define DPHY_CONST		16
 #define PHY_CONST_DIV		16
 
+static inline int
+__ccs_pll_calculate_vt_tree(struct device *dev,
+			    const struct ccs_pll_limits *lim,
+			    struct ccs_pll *pll, uint32_t mul, uint32_t div)
+{
+	const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr;
+	const struct ccs_pll_branch_limits_bk *lim_bk = &lim->vt_bk;
+	struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr;
+	struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk;
+	uint32_t more_mul;
+	uint16_t best_pix_div = SHRT_MAX >> 1, best_div;
+	uint16_t vt_div, min_sys_div, max_sys_div, sys_div;
+
+	pll_fr->pll_ip_clk_freq_hz =
+		pll->ext_clk_freq_hz / pll_fr->pre_pll_clk_div;
+
+	dev_dbg(dev, "vt_pll_ip_clk_freq_hz %u\n", pll_fr->pll_ip_clk_freq_hz);
+
+	more_mul = one_or_more(DIV_ROUND_UP(lim_fr->min_pll_op_clk_freq_hz,
+					    pll_fr->pll_ip_clk_freq_hz * mul));
+
+	dev_dbg(dev, "more_mul: %u\n", more_mul);
+	more_mul *= DIV_ROUND_UP(lim_fr->min_pll_multiplier, mul * more_mul);
+	dev_dbg(dev, "more_mul2: %u\n", more_mul);
+
+	pll_fr->pll_multiplier = mul * more_mul;
+
+	if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz >
+	    lim_fr->max_pll_op_clk_freq_hz)
+		return -EINVAL;
+
+	pll_fr->pll_op_clk_freq_hz =
+		pll_fr->pll_ip_clk_freq_hz * pll_fr->pll_multiplier;
+
+	vt_div = div * more_mul;
+
+	ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, vt_div, vt_div,
+				&min_sys_div, &max_sys_div);
+
+	max_sys_div = (vt_div & 1) ? 1 : max_sys_div;
+
+	dev_dbg(dev, "vt min/max_sys_div: %u,%u\n", min_sys_div, max_sys_div);
+
+	for (sys_div = min_sys_div; sys_div <= max_sys_div;
+	     sys_div += 2 - (sys_div & 1)) {
+		uint16_t pix_div;
+
+		if (vt_div % sys_div)
+			continue;
+
+		pix_div = vt_div / sys_div;
+
+		if (pix_div < lim_bk->min_pix_clk_div ||
+		    pix_div > lim_bk->max_pix_clk_div) {
+			dev_dbg(dev,
+				"pix_div %u too small or too big (%u--%u)\n",
+				pix_div,
+				lim_bk->min_pix_clk_div,
+				lim_bk->max_pix_clk_div);
+			continue;
+		}
+
+		if (pix_div * sys_div <= best_div) {
+			best_pix_div = pix_div;
+			best_div = pix_div * sys_div;
+		}
+	}
+	if (best_pix_div == SHRT_MAX >> 1)
+		return -EINVAL;
+
+	pll_bk->sys_clk_div = best_div / best_pix_div;
+	pll_bk->pix_clk_div = best_pix_div;
+
+	pll_bk->sys_clk_freq_hz =
+		pll_fr->pll_op_clk_freq_hz / pll_bk->sys_clk_div;
+	pll_bk->pix_clk_freq_hz =
+		pll_bk->sys_clk_freq_hz / pll_bk->pix_clk_div;
+
+	pll->pixel_rate_pixel_array =
+		pll_bk->pix_clk_freq_hz * pll->vt_lanes;
+
+	return 0;
+}
+
+static int ccs_pll_calculate_vt_tree(struct device *dev,
+				     const struct ccs_pll_limits *lim,
+				     struct ccs_pll *pll)
+{
+	const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr;
+	struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr;
+	uint16_t min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div;
+	uint16_t max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div;
+	uint32_t pre_mul, pre_div;
+
+	pre_div = gcd(pll->pixel_rate_csi,
+		      pll->ext_clk_freq_hz * pll->vt_lanes);
+	pre_mul = pll->pixel_rate_csi / pre_div;
+	pre_div = pll->ext_clk_freq_hz * pll->vt_lanes / pre_div;
+
+	/* Make sure PLL input frequency is within limits */
+	max_pre_pll_clk_div =
+		min_t(uint16_t, max_pre_pll_clk_div,
+		      DIV_ROUND_UP(pll->ext_clk_freq_hz,
+				   lim_fr->min_pll_ip_clk_freq_hz));
+
+	min_pre_pll_clk_div = max_t(uint16_t, min_pre_pll_clk_div,
+				    pll->ext_clk_freq_hz /
+				    lim_fr->max_pll_ip_clk_freq_hz);
+
+	dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n",
+		min_pre_pll_clk_div, max_pre_pll_clk_div);
+
+	for (pll_fr->pre_pll_clk_div = min_pre_pll_clk_div;
+	     pll_fr->pre_pll_clk_div <= max_pre_pll_clk_div;
+	     pll_fr->pre_pll_clk_div +=
+		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
+		     2 - (pll_fr->pre_pll_clk_div & 1)) {
+		uint32_t mul, div;
+		int rval;
+
+		div = gcd(pre_mul * pll_fr->pre_pll_clk_div, pre_div);
+		mul = pre_mul * pll_fr->pre_pll_clk_div / div;
+		div = pre_div / div;
+
+		dev_dbg(dev, "vt pre-div/mul/div: %u,%u,%u\n",
+			pll_fr->pre_pll_clk_div, mul, div);
+
+		rval = __ccs_pll_calculate_vt_tree(dev, lim, pll,
+						   mul, div);
+		if (rval)
+			continue;
+
+		rval = check_fr_bounds(dev, lim, pll, PLL_VT);
+		if (rval)
+			continue;
+
+		rval = check_bk_bounds(dev, lim, pll, PLL_VT);
+		if (rval)
+			continue;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
 static void
 ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
@@ -525,10 +673,10 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim,
 int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		      struct ccs_pll *pll)
 {
-	const struct ccs_pll_branch_limits_fr *op_lim_fr = &lim->vt_fr;
-	const struct ccs_pll_branch_limits_bk *op_lim_bk = &lim->op_bk;
-	struct ccs_pll_branch_fr *op_pll_fr = &pll->vt_fr;
-	struct ccs_pll_branch_bk *op_pll_bk = &pll->op_bk;
+	const struct ccs_pll_branch_limits_fr *op_lim_fr;
+	const struct ccs_pll_branch_limits_bk *op_lim_bk;
+	struct ccs_pll_branch_fr *op_pll_fr;
+	struct ccs_pll_branch_bk *op_pll_bk;
 	bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY;
 	uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST;
 	uint16_t min_op_pre_pll_clk_div;
@@ -544,6 +692,28 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		pll->vt_lanes = 1;
 	}
 
+	if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) {
+		op_lim_fr = &lim->op_fr;
+		op_lim_bk = &lim->op_bk;
+		op_pll_fr = &pll->op_fr;
+		op_pll_bk = &pll->op_bk;
+	} else 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_lim_fr = &lim->vt_fr;
+		op_lim_bk = &lim->vt_bk;
+		op_pll_fr = &pll->vt_fr;
+		op_pll_bk = &pll->vt_bk;
+	} else {
+		op_lim_fr = &lim->vt_fr;
+		op_lim_bk = &lim->op_bk;
+		op_pll_fr = &pll->vt_fr;
+		op_pll_bk = &pll->op_bk;
+	}
+
 	if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel ||
 	    !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m ||
 	    !op_lim_fr->min_pll_ip_clk_freq_hz ||
@@ -567,17 +737,6 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes);
 	dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes);
 
-	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_lim_fr = &lim->vt_fr;
-		op_lim_bk = &lim->vt_bk;
-		op_pll_bk = &pll->vt_bk;
-	}
-
 	dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal,
 		pll->binning_vertical);
 
@@ -653,6 +812,9 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		if (rval)
 			continue;
 
+		if (pll->flags & CCS_PLL_FLAG_DUAL_PLL)
+			break;
+
 		ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr,
 				     op_pll_bk, cphy, phy_const);
 
@@ -663,14 +825,25 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		if (rval)
 			continue;
 
-		print_pll(dev, pll);
+		break;
+	}
+
+	if (rval) {
+		dev_dbg(dev, "unable to compute pre_pll divisor\n");
 
-		return 0;
+		return rval;
 	}
 
-	dev_dbg(dev, "unable to compute pre_pll divisor\n");
+	if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) {
+		rval = ccs_pll_calculate_vt_tree(dev, lim, pll);
 
-	return rval;
+		if (rval)
+			return rval;
+	}
+
+	print_pll(dev, pll);
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(ccs_pll_calculate);
 
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 6255803eee38..517ee504f44a 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -29,6 +29,7 @@
 #define CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV			BIT(5)
 #define CCS_PLL_FLAG_FIFO_DERATING				BIT(6)
 #define CCS_PLL_FLAG_FIFO_OVERRATING				BIT(7)
+#define CCS_PLL_FLAG_DUAL_PLL					BIT(8)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 33/38] ccs: Dual PLL support
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (31 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 32/38] ccs-pll: Add trivial dual PLL support Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 34/38] ccs-pll: Add support for DDR OP system and pixel clocks Sakari Ailus
                   ` (4 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Add support for sensors that either require dual PLL or support single or
dual PLL but use dual PLL as default.

Use sensor default configuration for sensors that support both modes.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c      |  9 +++++--
 drivers/media/i2c/ccs/ccs-core.c | 45 +++++++++++++++++++++++++++++++-
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 91b578a05a98..7df7b96e78e6 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -331,7 +331,10 @@ __ccs_pll_calculate_vt_tree(struct device *dev,
 			continue;
 		}
 
-		if (pix_div * sys_div <= best_div) {
+		dev_dbg(dev, "sys/pix/best_pix: %u,%u,%u\n", sys_div, pix_div,
+			best_pix_div);
+
+		if (pix_div * sys_div <= best_pix_div) {
 			best_pix_div = pix_div;
 			best_div = pix_div * sys_div;
 		}
@@ -804,7 +807,9 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		if (rval)
 			continue;
 
-		rval = check_fr_bounds(dev, lim, pll, PLL_VT);
+		rval = check_fr_bounds(dev, lim, pll,
+				       pll->flags & CCS_PLL_FLAG_DUAL_PLL ?
+				       PLL_OP : PLL_VT);
 		if (rval)
 			continue;
 
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 849c1001e3cc..137d951b551a 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -396,7 +396,23 @@ static int ccs_pll_configure(struct ccs_sensor *sensor)
 	if (rval < 0)
 		return rval;
 
-	return ccs_write(sensor, OP_SYS_CLK_DIV, pll->op_bk.sys_clk_div);
+	rval = ccs_write(sensor, OP_SYS_CLK_DIV, pll->op_bk.sys_clk_div);
+	if (rval < 0)
+		return rval;
+
+	if (!(pll->flags & CCS_PLL_FLAG_DUAL_PLL))
+		return 0;
+
+	rval = ccs_write(sensor, PLL_MODE, CCS_PLL_MODE_DUAL);
+	if (rval < 0)
+		return rval;
+
+	rval = ccs_write(sensor, OP_PRE_PLL_CLK_DIV,
+			 pll->op_fr.pre_pll_clk_div);
+	if (rval < 0)
+		return rval;
+
+	return ccs_write(sensor, OP_PLL_MULTIPLIER, pll->op_fr.pll_multiplier);
 }
 
 static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll)
@@ -413,6 +429,16 @@ static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll)
 			.min_pll_op_clk_freq_hz = CCS_LIM(sensor, MIN_PLL_OP_CLK_FREQ_MHZ),
 			.max_pll_op_clk_freq_hz = CCS_LIM(sensor, MAX_PLL_OP_CLK_FREQ_MHZ),
 		},
+		.op_fr = {
+			.min_pre_pll_clk_div = CCS_LIM(sensor, MIN_OP_PRE_PLL_CLK_DIV),
+			.max_pre_pll_clk_div = CCS_LIM(sensor, MAX_OP_PRE_PLL_CLK_DIV),
+			.min_pll_ip_clk_freq_hz = CCS_LIM(sensor, MIN_OP_PLL_IP_CLK_FREQ_MHZ),
+			.max_pll_ip_clk_freq_hz = CCS_LIM(sensor, MAX_OP_PLL_IP_CLK_FREQ_MHZ),
+			.min_pll_multiplier = CCS_LIM(sensor, MIN_OP_PLL_MULTIPLIER),
+			.max_pll_multiplier = CCS_LIM(sensor, MAX_OP_PLL_MULTIPLIER),
+			.min_pll_op_clk_freq_hz = CCS_LIM(sensor, MIN_OP_PLL_OP_CLK_FREQ_MHZ),
+			.max_pll_op_clk_freq_hz = CCS_LIM(sensor, MAX_OP_PLL_OP_CLK_FREQ_MHZ),
+		},
 		.op_bk = {
 			 .min_sys_clk_div = CCS_LIM(sensor, MIN_OP_SYS_CLK_DIV),
 			 .max_sys_clk_div = CCS_LIM(sensor, MAX_OP_SYS_CLK_DIV),
@@ -3232,6 +3258,23 @@ static int ccs_probe(struct i2c_client *client)
 	    CCS_FIFO_SUPPORT_CAPABILITY_DERATING_OVERRATING)
 		sensor->pll.flags |= CCS_PLL_FLAG_FIFO_DERATING |
 				     CCS_PLL_FLAG_FIFO_OVERRATING;
+	if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
+	    CCS_CLOCK_TREE_PLL_CAPABILITY_DUAL_PLL) {
+		if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) &
+		    CCS_CLOCK_TREE_PLL_CAPABILITY_SINGLE_PLL) {
+			u32 v;
+
+			/* Use sensor default in PLL mode selection */
+			rval = ccs_read(sensor, PLL_MODE, &v);
+			if (rval)
+				goto out_cleanup;
+
+			if (v == CCS_PLL_MODE_DUAL)
+				sensor->pll.flags |= CCS_PLL_FLAG_DUAL_PLL;
+		} else {
+			sensor->pll.flags |= CCS_PLL_FLAG_DUAL_PLL;
+		}
+	}
 	sensor->pll.op_bits_per_lane = CCS_LIM(sensor, OP_BITS_PER_LANE);
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
 	sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 34/38] ccs-pll: Add support for DDR OP system and pixel clocks
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (32 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 33/38] ccs: Dual " Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 35/38] ccs: Add support for DDR OP SYS and OP PIX clocks Sakari Ailus
                   ` (3 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Add support for dual data rate operational system and pixel clocks. This
is implemented using two PLL flags.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 64 +++++++++++++++++++++++++------------
 drivers/media/i2c/ccs-pll.h |  2 ++
 2 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 7df7b96e78e6..5a0162347777 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -119,7 +119,7 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 		}
 	}
 
-	dev_dbg(dev, "flags%s%s%s%s%s%s%s\n",
+	dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n",
 		pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "",
 		pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "",
 		pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ?
@@ -128,7 +128,19 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 		" flexible-op-pix-div" : "",
 		pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "",
 		pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "",
-		pll->flags & PLL_FL(DUAL_PLL) ? " dual-pll" : "");
+		pll->flags & PLL_FL(DUAL_PLL) ? " dual-pll" : "",
+		pll->flags & PLL_FL(OP_SYS_DDR) ? " op-sys-ddr" : "",
+		pll->flags & PLL_FL(OP_PIX_DDR) ? " op-pix-ddr" : "");
+}
+
+static uint32_t op_sys_ddr(uint32_t flags)
+{
+	return flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0;
+}
+
+static uint32_t op_pix_ddr(uint32_t flags)
+{
+	return flags & CCS_PLL_FLAG_OP_PIX_DDR ? 1 : 0;
 }
 
 static int check_fr_bounds(struct device *dev,
@@ -441,8 +453,8 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 	if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) {
 		min_vt_div =
 			op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div
-			* pll->vt_lanes * phy_const
-			/ pll->op_lanes / PHY_CONST_DIV;
+			* pll->vt_lanes * phy_const / pll->op_lanes
+			/ (PHY_CONST_DIV << op_pix_ddr(pll->flags));
 	} else {
 		/*
 		 * Some sensors perform analogue binning and some do this
@@ -478,7 +490,7 @@ ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim,
 				      CCS_PLL_FLAG_LANE_SPEED_MODEL ?
 				      pll->csi2.lanes : 1)
 				     * vt_op_binning_div * pll->scale_m
-				     * PHY_CONST_DIV);
+				     * PHY_CONST_DIV << op_pix_ddr(pll->flags));
 	}
 
 	/* Find smallest and biggest allowed vt divisor. */
@@ -572,7 +584,8 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim,
 		     const struct ccs_pll_branch_limits_bk *op_lim_bk,
 		     struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr,
 		     struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul,
-		     uint32_t div, uint32_t l, bool cphy, uint32_t phy_const)
+		     uint32_t div, uint32_t op_sys_clk_freq_hz_sdr, uint32_t l,
+		     bool cphy, uint32_t phy_const)
 {
 	/*
 	 * Higher multipliers (and divisors) are often required than
@@ -658,15 +671,22 @@ ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim,
 		* op_pll_fr->pll_multiplier;
 
 	if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)
-		op_pll_bk->pix_clk_div = pll->bits_per_pixel
-			* pll->op_lanes * phy_const
-			/ PHY_CONST_DIV / pll->csi2.lanes / l;
+		op_pll_bk->pix_clk_div =
+			(pll->bits_per_pixel
+			 * pll->op_lanes * (phy_const << op_sys_ddr(pll->flags))
+			 / PHY_CONST_DIV / pll->csi2.lanes / l)
+			>> op_pix_ddr(pll->flags);
 	else
 		op_pll_bk->pix_clk_div =
-			pll->bits_per_pixel * phy_const / PHY_CONST_DIV / l;
+			(pll->bits_per_pixel
+			 * (phy_const << op_sys_ddr(pll->flags))
+			 / PHY_CONST_DIV / l) >> op_pix_ddr(pll->flags);
 
 	op_pll_bk->pix_clk_freq_hz =
-		op_pll_bk->sys_clk_freq_hz / op_pll_bk->pix_clk_div;
+		(op_sys_clk_freq_hz_sdr >> op_pix_ddr(pll->flags))
+		/ op_pll_bk->pix_clk_div;
+	op_pll_bk->sys_clk_freq_hz =
+		op_sys_clk_freq_hz_sdr >> op_sys_ddr(pll->flags);
 
 	dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div);
 
@@ -682,6 +702,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	struct ccs_pll_branch_bk *op_pll_bk;
 	bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY;
 	uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST;
+	uint32_t op_sys_clk_freq_hz_sdr;
 	uint16_t min_op_pre_pll_clk_div;
 	uint16_t max_op_pre_pll_clk_div;
 	uint32_t mul, div;
@@ -731,7 +752,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	 * op_pix_clk_div is supported
 	 */
 	if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) &&
-	    (pll->bits_per_pixel * pll->op_lanes) % (pll->csi2.lanes * l)) {
+	    (pll->bits_per_pixel * pll->op_lanes) %
+	    (pll->csi2.lanes * l << op_pix_ddr(pll->flags))) {
 		dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n",
 			pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l);
 		return -EINVAL;
@@ -746,12 +768,12 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	switch (pll->bus_type) {
 	case CCS_PLL_BUS_TYPE_CSI2_DPHY:
 		/* CSI transfers 2 bits per clock per lane; thus times 2 */
-		op_pll_bk->sys_clk_freq_hz = pll->link_freq * 2
+		op_sys_clk_freq_hz_sdr = pll->link_freq * 2
 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
 			   1 : pll->csi2.lanes);
 		break;
 	case CCS_PLL_BUS_TYPE_CSI2_CPHY:
-		op_pll_bk->sys_clk_freq_hz =
+		op_sys_clk_freq_hz_sdr =
 			pll->link_freq
 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
 			   1 : pll->csi2.lanes);
@@ -761,7 +783,7 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	}
 
 	pll->pixel_rate_csi =
-		div_u64((uint64_t)op_pll_bk->sys_clk_freq_hz
+		div_u64((uint64_t)op_sys_clk_freq_hz_sdr
 			* (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
 			   pll->csi2.lanes : 1) * PHY_CONST_DIV,
 			phy_const * pll->bits_per_pixel * l);
@@ -781,9 +803,10 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 	dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n",
 		min_op_pre_pll_clk_div, max_op_pre_pll_clk_div);
 
-	i = gcd(op_pll_bk->sys_clk_freq_hz, pll->ext_clk_freq_hz);
-	mul = op_pll_bk->sys_clk_freq_hz / i;
-	div = pll->ext_clk_freq_hz / i;
+	i = gcd(op_sys_clk_freq_hz_sdr,
+		pll->ext_clk_freq_hz << op_pix_ddr(pll->flags));
+	mul = op_sys_clk_freq_hz_sdr / i;
+	div = (pll->ext_clk_freq_hz << op_pix_ddr(pll->flags)) / i;
 	dev_dbg(dev, "mul %u / div %u\n", mul, div);
 
 	min_op_pre_pll_clk_div =
@@ -802,8 +825,9 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 		     (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 :
 		     2 - (op_pll_fr->pre_pll_clk_div & 1)) {
 		rval = ccs_pll_calculate_op(dev, lim, op_lim_fr, op_lim_bk, pll,
-					    op_pll_fr, op_pll_bk, mul, div, l,
-					    cphy, phy_const);
+					    op_pll_fr, op_pll_bk, mul, div,
+					    op_sys_clk_freq_hz_sdr, l, cphy,
+					    phy_const);
 		if (rval)
 			continue;
 
diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h
index 517ee504f44a..b97d7ff50ea5 100644
--- a/drivers/media/i2c/ccs-pll.h
+++ b/drivers/media/i2c/ccs-pll.h
@@ -30,6 +30,8 @@
 #define CCS_PLL_FLAG_FIFO_DERATING				BIT(6)
 #define CCS_PLL_FLAG_FIFO_OVERRATING				BIT(7)
 #define CCS_PLL_FLAG_DUAL_PLL					BIT(8)
+#define CCS_PLL_FLAG_OP_SYS_DDR					BIT(9)
+#define CCS_PLL_FLAG_OP_PIX_DDR					BIT(10)
 
 /**
  * struct ccs_pll_branch_fr - CCS PLL configuration (front)
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 35/38] ccs: Add support for DDR OP SYS and OP PIX clocks
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (33 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 34/38] ccs-pll: Add support for DDR OP system and pixel clocks Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 36/38] ccs: Print written register values Sakari Ailus
                   ` (2 subsequent siblings)
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Support dual data rate operational system and pixel clocks by conveying
the flags to the PLL calculator and updating how the link rate is
calculated.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 137d951b551a..c41c47b40e73 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -388,7 +388,8 @@ static int ccs_pll_configure(struct ccs_sensor *sensor)
 			 DIV_ROUND_UP(pll->op_bk.sys_clk_freq_hz,
 				      1000000 / 256 / 256) *
 			 (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-			  sensor->pll.csi2.lanes : 1));
+			  sensor->pll.csi2.lanes : 1) <<
+			 (pll->flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0));
 	if (rval < 0 || sensor->pll.flags & CCS_PLL_FLAG_NO_OP_CLOCKS)
 		return rval;
 
@@ -3274,6 +3275,12 @@ static int ccs_probe(struct i2c_client *client)
 		} else {
 			sensor->pll.flags |= CCS_PLL_FLAG_DUAL_PLL;
 		}
+		if (CCS_LIM(sensor, CLOCK_CALCULATION) &
+		    CCS_CLOCK_CALCULATION_DUAL_PLL_OP_SYS_DDR)
+			sensor->pll.flags |= CCS_PLL_FLAG_OP_SYS_DDR;
+		if (CCS_LIM(sensor, CLOCK_CALCULATION) &
+		    CCS_CLOCK_CALCULATION_DUAL_PLL_OP_PIX_DDR)
+			sensor->pll.flags |= CCS_PLL_FLAG_OP_PIX_DDR;
 	}
 	sensor->pll.op_bits_per_lane = CCS_LIM(sensor, OP_BITS_PER_LANE);
 	sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 36/38] ccs: Print written register values
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (34 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 35/38] ccs: Add support for DDR OP SYS and OP PIX clocks Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 37/38] ccs-pll: Print pixel rates Sakari Ailus
  2020-12-02 18:06 ` [PATCH 38/38] ccs: Add support for obtaining C-PHY configuration from firmware Sakari Ailus
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

This helps debugging register writes.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-reg-access.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c
index 3de863e3bf26..b776af2a3c33 100644
--- a/drivers/media/i2c/ccs/ccs-reg-access.c
+++ b/drivers/media/i2c/ccs/ccs-reg-access.c
@@ -338,6 +338,10 @@ int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val)
 	put_unaligned_be16(CCS_REG_ADDR(reg), data);
 	put_unaligned_be32(val << (8 * (sizeof(val) - len)), data + 2);
 
+	dev_dbg(&client->dev, "writing reg 0x%4.4x value 0x%*.*x (%u)\n",
+		CCS_REG_ADDR(reg), ccs_reg_width(reg) << 1,
+		ccs_reg_width(reg) << 1, val, val);
+
 	r = ccs_write_retry(client, &msg);
 	if (r)
 		dev_err(&client->dev,
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 37/38] ccs-pll: Print pixel rates
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (35 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 36/38] ccs: Print written register values Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  2020-12-02 18:06 ` [PATCH 38/38] ccs: Add support for obtaining C-PHY configuration from firmware Sakari Ailus
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Print pixel rates on CSI-2 bus as well as in pixel array as the variation
allowed in PLL capabilities makes this non-trivial to figure out
otherwise.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs-pll.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c
index 5a0162347777..eb7b6f01f623 100644
--- a/drivers/media/i2c/ccs-pll.c
+++ b/drivers/media/i2c/ccs-pll.c
@@ -119,6 +119,11 @@ static void print_pll(struct device *dev, struct ccs_pll *pll)
 		}
 	}
 
+	dev_dbg(dev, "pixel rate in pixel array:\t%u\n",
+		pll->pixel_rate_pixel_array);
+	dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n",
+		pll->pixel_rate_csi);
+
 	dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n",
 		pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "",
 		pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "",
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH 38/38] ccs: Add support for obtaining C-PHY configuration from firmware
  2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
                   ` (36 preceding siblings ...)
  2020-12-02 18:06 ` [PATCH 37/38] ccs-pll: Print pixel rates Sakari Ailus
@ 2020-12-02 18:06 ` Sakari Ailus
  37 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2020-12-02 18:06 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab

Try parsing the firmware also as C-PHY. Do this only after D-PHY as older
firmware may not explicitly specify bus-type in which case D-PHY is the
default.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/i2c/ccs/ccs-core.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index c41c47b40e73..cd787155b698 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -2926,6 +2926,10 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev)
 		hwcfg->csi_signalling_mode = CCS_CSI_SIGNALING_MODE_CSI_2_DPHY;
 		hwcfg->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
 		break;
+	case V4L2_MBUS_CSI2_CPHY:
+		hwcfg->csi_signalling_mode = CCS_CSI_SIGNALING_MODE_CSI_2_CPHY;
+		hwcfg->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+		break;
 	case V4L2_MBUS_CSI1:
 	case V4L2_MBUS_CCP2:
 		hwcfg->csi_signalling_mode = (bus_cfg.bus.mipi_csi1.strobe) ?
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2020-12-02 18:15 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-02 18:06 [PATCH 00/38] Support additional CCS PLL features, C-PHY Sakari Ailus
2020-12-02 18:06 ` [PATCH 01/38] ccs-pll: Don't use div_u64 to divide a 32-bit number Sakari Ailus
2020-12-02 18:06 ` [PATCH 02/38] ccs-pll: Split limits and PLL configuration into front and back parts Sakari Ailus
2020-12-02 18:06 ` [PATCH 03/38] ccs-pll: Use correct VT divisor for calculating VT SYS divisor Sakari Ailus
2020-12-02 18:06 ` [PATCH 04/38] ccs-pll: End search if there are no better values available Sakari Ailus
2020-12-02 18:06 ` [PATCH 05/38] ccs-pll: Remove parallel bus support Sakari Ailus
2020-12-02 18:06 ` [PATCH 06/38] ccs-pll: Differentiate between CSI-2 D-PHY and C-PHY Sakari Ailus
2020-12-02 18:06 ` [PATCH 07/38] ccs-pll: Move the flags field down, away from 8-bit fields Sakari Ailus
2020-12-02 18:06 ` [PATCH 08/38] ccs-pll: Document the structs in the header as well as the function Sakari Ailus
2020-12-02 18:06 ` [PATCH 09/38] ccs-pll: Use the BIT macro Sakari Ailus
2020-12-02 18:06 ` [PATCH 10/38] ccs-pll: Begin calculation from OP system clock frequency Sakari Ailus
2020-12-02 18:06 ` [PATCH 11/38] ccs-pll: Fix condition for pre-PLL divider lower bound Sakari Ailus
2020-12-02 18:06 ` [PATCH 12/38] ccs-pll: Avoid overflow in pre-PLL divisor lower bound search Sakari Ailus
2020-12-02 18:06 ` [PATCH 13/38] ccs-pll: Fix comment on check against maximum PLL multiplier Sakari Ailus
2020-12-02 18:06 ` [PATCH 14/38] ccs-pll: Fix check for PLL multiplier upper bound Sakari Ailus
2020-12-02 18:06 ` [PATCH 15/38] ccs-pll: Use explicit 32-bit unsigned type Sakari Ailus
2020-12-02 18:06 ` [PATCH 16/38] ccs-pll: Add support for lane speed model Sakari Ailus
2020-12-02 18:06 ` [PATCH 17/38] ccs: " Sakari Ailus
2020-12-02 18:06 ` [PATCH 18/38] ccs-pll: Add support for decoupled OP domain calculation Sakari Ailus
2020-12-02 18:06 ` [PATCH 19/38] ccs-pll: Add support for extended input PLL clock divider Sakari Ailus
2020-12-02 18:06 ` [PATCH 20/38] ccs-pll: Support two cycles per pixel on OP domain Sakari Ailus
2020-12-02 18:06 ` [PATCH 21/38] ccs-pll: Add support flexible OP PLL pixel clock divider Sakari Ailus
2020-12-02 18:06 ` [PATCH 22/38] ccs-pll: Add sanity checks Sakari Ailus
2020-12-02 18:06 ` [PATCH 23/38] ccs-pll: Add C-PHY support Sakari Ailus
2020-12-02 18:06 ` [PATCH 24/38] ccs-pll: Split off VT subtree calculation Sakari Ailus
2020-12-02 18:06 ` [PATCH 25/38] ccs-pll: Check for derating and overrating, support non-derating sensors Sakari Ailus
2020-12-02 18:06 ` [PATCH 26/38] ccs-pll: Better separate OP and VT sub-tree calculation Sakari Ailus
2020-12-02 18:06 ` [PATCH 27/38] ccs-pll: Print relevant information on PLL tree Sakari Ailus
2020-12-02 18:06 ` [PATCH 28/38] ccs-pll: Rework bounds checks Sakari Ailus
2020-12-02 18:06 ` [PATCH 29/38] ccs-pll: Make VT divisors 16-bit Sakari Ailus
2020-12-02 18:06 ` [PATCH 30/38] ccs-pll: Fix VT post-PLL divisor calculation Sakari Ailus
2020-12-02 18:06 ` [PATCH 31/38] ccs-pll: Separate VT divisor limit calculation from the rest Sakari Ailus
2020-12-02 18:06 ` [PATCH 32/38] ccs-pll: Add trivial dual PLL support Sakari Ailus
2020-12-02 18:06 ` [PATCH 33/38] ccs: Dual " Sakari Ailus
2020-12-02 18:06 ` [PATCH 34/38] ccs-pll: Add support for DDR OP system and pixel clocks Sakari Ailus
2020-12-02 18:06 ` [PATCH 35/38] ccs: Add support for DDR OP SYS and OP PIX clocks Sakari Ailus
2020-12-02 18:06 ` [PATCH 36/38] ccs: Print written register values Sakari Ailus
2020-12-02 18:06 ` [PATCH 37/38] ccs-pll: Print pixel rates Sakari Ailus
2020-12-02 18:06 ` [PATCH 38/38] ccs: Add support for obtaining C-PHY configuration from firmware Sakari Ailus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).