diff options
Diffstat (limited to 'drivers/media/i2c/smiapp-pll.c')
-rw-r--r-- | drivers/media/i2c/smiapp-pll.c | 280 |
1 files changed, 157 insertions, 123 deletions
diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c index 2335529b195c..e40d9027df3d 100644 --- a/drivers/media/i2c/smiapp-pll.c +++ b/drivers/media/i2c/smiapp-pll.c @@ -65,26 +65,89 @@ static int bounds_check(struct device *dev, uint32_t val, static void print_pll(struct device *dev, struct smiapp_pll *pll) { - dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); - dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); - if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); - dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); + dev_dbg(dev, "pre_pll_clk_div\t%u\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pll_multiplier \t%u\n", pll->pll_multiplier); + if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { + dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div); + dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div); } - dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); - dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); - - dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); - dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); - dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", - pll->op_sys_clk_freq_hz); - dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", - pll->op_pix_clk_freq_hz); + dev_dbg(dev, "vt_sys_clk_div \t%u\n", pll->vt.sys_clk_div); + dev_dbg(dev, "vt_pix_clk_div \t%u\n", pll->vt.pix_clk_div); + + dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz); + dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz); + dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz); + if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { + dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n", + pll->op.sys_clk_freq_hz); + dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n", + pll->op.pix_clk_freq_hz); } - dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); - dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); + dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt.sys_clk_freq_hz); + dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt.pix_clk_freq_hz); +} + +static int check_all_bounds(struct device *dev, + const struct smiapp_pll_limits *limits, + const struct smiapp_pll_branch_limits *op_limits, + struct smiapp_pll *pll, + struct smiapp_pll_branch *op_pll) +{ + int rval; + + rval = bounds_check(dev, pll->pll_ip_clk_freq_hz, + limits->min_pll_ip_freq_hz, + limits->max_pll_ip_freq_hz, + "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->pll_multiplier, + limits->min_pll_multiplier, limits->max_pll_multiplier, + "pll_multiplier"); + if (!rval) + rval = bounds_check( + dev, pll->pll_op_clk_freq_hz, + limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, + "pll_op_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, op_pll->sys_clk_div, + op_limits->min_sys_clk_div, op_limits->max_sys_clk_div, + "op_sys_clk_div"); + if (!rval) + rval = bounds_check( + dev, op_pll->sys_clk_freq_hz, + op_limits->min_sys_clk_freq_hz, + op_limits->max_sys_clk_freq_hz, + "op_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, op_pll->pix_clk_freq_hz, + op_limits->min_pix_clk_freq_hz, + op_limits->max_pix_clk_freq_hz, + "op_pix_clk_freq_hz"); + + /* + * If there are no OP clocks, the VT clocks are contained in + * the OP clock struct. + */ + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) + return rval; + + if (!rval) + rval = bounds_check( + dev, pll->vt.sys_clk_freq_hz, + limits->vt.min_sys_clk_freq_hz, + limits->vt.max_sys_clk_freq_hz, + "vt_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt.pix_clk_freq_hz, + limits->vt.min_pix_clk_freq_hz, + limits->vt.max_pix_clk_freq_hz, + "vt_pix_clk_freq_hz"); + + return rval; } /* @@ -98,10 +161,11 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll) * * @return Zero on success, error code on error. */ -static int __smiapp_pll_calculate(struct device *dev, - const struct smiapp_pll_limits *limits, - struct smiapp_pll *pll, uint32_t mul, - uint32_t div, uint32_t lane_op_clock_ratio) +static int __smiapp_pll_calculate( + struct device *dev, const struct smiapp_pll_limits *limits, + const struct smiapp_pll_branch_limits *op_limits, + struct smiapp_pll *pll, struct smiapp_pll_branch *op_pll, uint32_t mul, + uint32_t div, uint32_t lane_op_clock_ratio) { uint32_t sys_div; uint32_t best_pix_div = INT_MAX >> 1; @@ -117,17 +181,16 @@ static int __smiapp_pll_calculate(struct device *dev, uint32_t min_vt_div, max_vt_div, vt_div; uint32_t min_sys_div, max_sys_div; unsigned int i; - int rval; /* * 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 %d\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pre_pll_clk_div %u\n", pll->pre_pll_clk_div); /* Don't go above max pll multiplier. */ more_mul_max = limits->max_pll_multiplier / mul; - dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", + dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %u\n", more_mul_max); /* Don't go above max pll op frequency. */ more_mul_max = @@ -135,30 +198,30 @@ static int __smiapp_pll_calculate(struct device *dev, 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: %d\n", + dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %u\n", more_mul_max); /* Don't go above the division capability of op sys clock divider. */ more_mul_max = min(more_mul_max, - limits->op.max_sys_clk_div * pll->pre_pll_clk_div + op_limits->max_sys_clk_div * pll->pre_pll_clk_div / div); - dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n", more_mul_max); /* Ensure we won't go above min_pll_multiplier. */ more_mul_max = min(more_mul_max, DIV_ROUND_UP(limits->max_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", + 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: %d\n", + dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %u\n", more_mul_min); /* Ensure we won't go below min_pll_multiplier. */ more_mul_min = max(more_mul_min, DIV_ROUND_UP(limits->min_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", + dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %u\n", more_mul_min); if (more_mul_min > more_mul_max) { @@ -168,23 +231,23 @@ static int __smiapp_pll_calculate(struct device *dev, } more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; - dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); - more_mul_factor = lcm(more_mul_factor, limits->op.min_sys_clk_div); + dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div); dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", more_mul_factor); i = roundup(more_mul_min, more_mul_factor); if (!is_one_or_even(i)) i <<= 1; - dev_dbg(dev, "final more_mul: %d\n", i); + dev_dbg(dev, "final more_mul: %u\n", i); if (i > more_mul_max) { - dev_dbg(dev, "final more_mul is bad, max %d\n", more_mul_max); + dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); return -EINVAL; } pll->pll_multiplier = mul * i; - pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; - dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); + op_pll->sys_clk_div = div * i / pll->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll->sys_clk_div); pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz / pll->pre_pll_clk_div; @@ -193,14 +256,19 @@ static int __smiapp_pll_calculate(struct device *dev, * pll->pll_multiplier; /* Derive pll_op_clk_freq_hz. */ - pll->op_sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; + op_pll->sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / op_pll->sys_clk_div; + + op_pll->pix_clk_div = pll->bits_per_pixel; + dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll->pix_clk_div); - pll->op_pix_clk_div = pll->bits_per_pixel; - dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); + op_pll->pix_clk_freq_hz = + op_pll->sys_clk_freq_hz / op_pll->pix_clk_div; - pll->op_pix_clk_freq_hz = - pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + /* No OP clocks --- VT clocks are used instead. */ + goto out_skip_vt_calc; + } /* * Some sensors perform analogue binning and some do this @@ -214,7 +282,7 @@ static int __smiapp_pll_calculate(struct device *dev, vt_op_binning_div = pll->binning_horizontal; else vt_op_binning_div = 1; - dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); + dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div); /* * Profile 2 supports vt_pix_clk_div E [4, 10] @@ -227,30 +295,30 @@ static int __smiapp_pll_calculate(struct device *dev, * * Find absolute limits for the factor of vt divider. */ - dev_dbg(dev, "scale_m: %d\n", pll->scale_m); - min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div + dev_dbg(dev, "scale_m: %u\n", pll->scale_m); + min_vt_div = DIV_ROUND_UP(op_pll->pix_clk_div * op_pll->sys_clk_div * pll->scale_n, lane_op_clock_ratio * vt_op_binning_div * pll->scale_m); /* Find smallest and biggest allowed vt divisor. */ - dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); + dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); min_vt_div = max(min_vt_div, DIV_ROUND_UP(pll->pll_op_clk_freq_hz, limits->vt.max_pix_clk_freq_hz)); - dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", min_vt_div); min_vt_div = max_t(uint32_t, min_vt_div, limits->vt.min_pix_clk_div * limits->vt.min_sys_clk_div); - dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div; - dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); + dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); max_vt_div = min(max_vt_div, DIV_ROUND_UP(pll->pll_op_clk_freq_hz, limits->vt.min_pix_clk_freq_hz)); - dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", max_vt_div); /* @@ -258,28 +326,28 @@ static int __smiapp_pll_calculate(struct device *dev, * with all values of pix_clk_div. */ min_sys_div = limits->vt.min_sys_clk_div; - dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); + dev_dbg(dev, "min_sys_div: %u\n", min_sys_div); min_sys_div = max(min_sys_div, DIV_ROUND_UP(min_vt_div, limits->vt.max_pix_clk_div)); - dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div); min_sys_div = max(min_sys_div, pll->pll_op_clk_freq_hz / limits->vt.max_sys_clk_freq_hz); - dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); + 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: %d\n", min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div); max_sys_div = limits->vt.max_sys_clk_div; - dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); + dev_dbg(dev, "max_sys_div: %u\n", max_sys_div); max_sys_div = min(max_sys_div, DIV_ROUND_UP(max_vt_div, limits->vt.min_pix_clk_div)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div); max_sys_div = min(max_sys_div, DIV_ROUND_UP(pll->pll_op_clk_freq_hz, limits->vt.min_pix_clk_freq_hz)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div); /* * Find pix_div such that a legal pix_div * sys_div results @@ -296,7 +364,7 @@ static int __smiapp_pll_calculate(struct device *dev, if (pix_div < limits->vt.min_pix_clk_div || pix_div > limits->vt.max_pix_clk_div) { dev_dbg(dev, - "pix_div %d too small or too big (%d--%d)\n", + "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); @@ -312,73 +380,28 @@ static int __smiapp_pll_calculate(struct device *dev, break; } - pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); - pll->vt_pix_clk_div = best_pix_div; + pll->vt.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); + pll->vt.pix_clk_div = best_pix_div; - pll->vt_sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; - pll->vt_pix_clk_freq_hz = - pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; + pll->vt.sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->vt.sys_clk_div; + pll->vt.pix_clk_freq_hz = + pll->vt.sys_clk_freq_hz / pll->vt.pix_clk_div; +out_skip_vt_calc: pll->pixel_rate_csi = - pll->op_pix_clk_freq_hz * lane_op_clock_ratio; + op_pll->pix_clk_freq_hz * lane_op_clock_ratio; + pll->pixel_rate_pixel_array = pll->vt.pix_clk_freq_hz; - rval = bounds_check(dev, pll->pll_ip_clk_freq_hz, - limits->min_pll_ip_freq_hz, - limits->max_pll_ip_freq_hz, - "pll_ip_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->pll_multiplier, - limits->min_pll_multiplier, limits->max_pll_multiplier, - "pll_multiplier"); - if (!rval) - rval = bounds_check( - dev, pll->pll_op_clk_freq_hz, - limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, - "pll_op_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->op_sys_clk_div, - limits->op.min_sys_clk_div, limits->op.max_sys_clk_div, - "op_sys_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->op_pix_clk_div, - limits->op.min_pix_clk_div, limits->op.max_pix_clk_div, - "op_pix_clk_div"); - if (!rval) - rval = bounds_check( - dev, pll->op_sys_clk_freq_hz, - limits->op.min_sys_clk_freq_hz, - limits->op.max_sys_clk_freq_hz, - "op_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->op_pix_clk_freq_hz, - limits->op.min_pix_clk_freq_hz, - limits->op.max_pix_clk_freq_hz, - "op_pix_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt_sys_clk_freq_hz, - limits->vt.min_sys_clk_freq_hz, - limits->vt.max_sys_clk_freq_hz, - "vt_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt_pix_clk_freq_hz, - limits->vt.min_pix_clk_freq_hz, - limits->vt.max_pix_clk_freq_hz, - "vt_pix_clk_freq_hz"); - - return rval; + return check_all_bounds(dev, limits, op_limits, pll, op_pll); } int smiapp_pll_calculate(struct device *dev, const struct smiapp_pll_limits *limits, struct smiapp_pll *pll) { + const struct smiapp_pll_branch_limits *op_limits = &limits->op; + struct smiapp_pll_branch *op_pll = &pll->op; uint16_t min_pre_pll_clk_div; uint16_t max_pre_pll_clk_div; uint32_t lane_op_clock_ratio; @@ -386,13 +409,23 @@ int smiapp_pll_calculate(struct device *dev, unsigned int i; int rval = -EINVAL; + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + /* + * If there's no OP PLL at all, use the VT values + * instead. The OP values are ignored for the rest of + * the PLL calculation. + */ + op_limits = &limits->vt; + op_pll = &pll->vt; + } + if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) lane_op_clock_ratio = pll->csi2.lanes; else lane_op_clock_ratio = 1; - dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); + dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio); - dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, + dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, pll->binning_vertical); switch (pll->bus_type) { @@ -411,7 +444,7 @@ int smiapp_pll_calculate(struct device *dev, } /* Figure out limits for pre-pll divider based on extclk */ - dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", + 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, @@ -422,26 +455,27 @@ int smiapp_pll_calculate(struct device *dev, 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: %d / %d\n", + dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n", min_pre_pll_clk_div, max_pre_pll_clk_div); i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); mul = div_u64(pll->pll_op_clk_freq_hz, i); div = pll->ext_clk_freq_hz / i; - dev_dbg(dev, "mul %d / div %d\n", mul, div); + dev_dbg(dev, "mul %u / div %u\n", mul, div); min_pre_pll_clk_div = max_t(uint16_t, min_pre_pll_clk_div, clk_div_even_up( DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, limits->max_pll_op_freq_hz))); - dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", + dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n", min_pre_pll_clk_div, max_pre_pll_clk_div); for (pll->pre_pll_clk_div = min_pre_pll_clk_div; pll->pre_pll_clk_div <= max_pre_pll_clk_div; pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { - rval = __smiapp_pll_calculate(dev, limits, pll, mul, div, + rval = __smiapp_pll_calculate(dev, limits, op_limits, pll, + op_pll, mul, div, lane_op_clock_ratio); if (rval) continue; |