int smiapp_pll_calculate(struct device *dev, const struct smiapp_pll_limits *limits, struct smiapp_pll *pll) { uint16_t min_pre_pll_clk_div; uint16_t max_pre_pll_clk_div; uint32_t lane_op_clock_ratio, lane_vt_clock_ratio; uint32_t mul, div; unsigned int i; int rval = -EINVAL; dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, pll->binning_vertical); if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) lane_op_clock_ratio = pll->csi2.lanes; else lane_op_clock_ratio = 1; dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio); if (pll->flags & SMIAPP_PLL_FLAG_VT_PIX_CLOCK_PER_LANE) lane_vt_clock_ratio = pll->csi2.lanes; else lane_vt_clock_ratio = 1; dev_dbg(dev, "lane_vt_clock_ratio: %u\n", lane_vt_clock_ratio); switch (pll->bus_type) { case SMIAPP_PLL_BUS_TYPE_CSI2: /* CSI transfers 2 bits per clock per lane; thus times 2 */ pll->pll_op_clk_freq_hz = pll->link_freq * 2 * (pll->csi2.lanes / lane_op_clock_ratio); break; case SMIAPP_PLL_BUS_TYPE_PARALLEL: pll->pll_op_clk_freq_hz = div_u64( pll->link_freq * pll->bits_per_pixel, DIV_ROUND_UP(pll->bits_per_pixel, pll->parallel.bus_width)); break; default: return -EINVAL; } /* * Half op pix divisor will give us double the rate compared * to the regular case. Thus divide the desired pll op clock * frequency by two. */ if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_DIV_HALF) pll->pll_op_clk_freq_hz /= 2; /* * If it'll be multiplied by two in the end divide it now to * avoid achieving double the desired clock. */ if (pll->flags & SMIAPP_PLL_FLAG_PIX_CLOCK_DOUBLE) pll->pll_op_clk_freq_hz /= 2; /* Figure out limits for pre-pll divider based on extclk */ dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n", limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); max_pre_pll_clk_div = min_t(uint16_t, limits->max_pre_pll_clk_div, clk_div( pll->ext_clk_freq_hz / limits->min_pll_ip_freq_hz, pll->flags & SMIAPP_PLL_FLAG_ALLOW_ODD_PRE_PLL_CLK_DIV)); min_pre_pll_clk_div = max_t(uint16_t, limits->min_pre_pll_clk_div, clk_div_up( DIV_ROUND_UP(pll->ext_clk_freq_hz, limits->max_pll_ip_freq_hz), pll->flags & SMIAPP_PLL_FLAG_ALLOW_ODD_PRE_PLL_CLK_DIV)); dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", min_pre_pll_clk_div, max_pre_pll_clk_div); i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); mul = div_u64(pll->pll_op_clk_freq_hz, i); div = pll->ext_clk_freq_hz / i; dev_dbg(dev, "mul %u / div %u\n", mul, div); min_pre_pll_clk_div = max_t(uint16_t, min_pre_pll_clk_div, clk_div_up( DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, limits->max_pll_op_freq_hz), pll->flags & SMIAPP_PLL_FLAG_ALLOW_ODD_PRE_PLL_CLK_DIV)); dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\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 += pll->flags & SMIAPP_PLL_FLAG_ALLOW_ODD_PRE_PLL_CLK_DIV ? 1 : (2 - (pll->pre_pll_clk_div & 1))) { rval = __smiapp_pll_calculate(dev, limits, pll, mul, div, lane_op_clock_ratio, lane_vt_clock_ratio); if (rval) continue; print_pll(dev, pll); return 0; } dev_info(dev, "unable to compute pre_pll divisor\n"); return rval; }
int smiapp_pll_calculate(struct device *dev, const struct smiapp_pll_limits *limits, struct smiapp_pll *pll) { const struct smiapp_pll_branch_limits *op_limits = &limits->op; struct smiapp_pll_branch *op_pll = &pll->op; uint16_t min_pre_pll_clk_div; uint16_t max_pre_pll_clk_div; uint32_t lane_op_clock_ratio; uint32_t mul, div; unsigned int i; int rval = -EINVAL; if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { /* * If there's no OP PLL at all, use the VT values * instead. The OP values are ignored for the rest of * the PLL calculation. */ op_limits = &limits->vt; op_pll = &pll->vt; } if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) lane_op_clock_ratio = pll->csi2.lanes; else lane_op_clock_ratio = 1; dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio); dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, pll->binning_vertical); switch (pll->bus_type) { case SMIAPP_PLL_BUS_TYPE_CSI2: /* CSI transfers 2 bits per clock per lane; thus times 2 */ pll->pll_op_clk_freq_hz = pll->link_freq * 2 * (pll->csi2.lanes / lane_op_clock_ratio); break; case SMIAPP_PLL_BUS_TYPE_PARALLEL: pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel / DIV_ROUND_UP(pll->bits_per_pixel, pll->parallel.bus_width); break; default: return -EINVAL; } /* Figure out limits for pre-pll divider based on extclk */ dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n", limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); max_pre_pll_clk_div = min_t(uint16_t, limits->max_pre_pll_clk_div, clk_div_even(pll->ext_clk_freq_hz / limits->min_pll_ip_freq_hz)); min_pre_pll_clk_div = max_t(uint16_t, limits->min_pre_pll_clk_div, clk_div_even_up( DIV_ROUND_UP(pll->ext_clk_freq_hz, limits->max_pll_ip_freq_hz))); dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n", min_pre_pll_clk_div, max_pre_pll_clk_div); i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); mul = div_u64(pll->pll_op_clk_freq_hz, i); div = pll->ext_clk_freq_hz / i; dev_dbg(dev, "mul %u / div %u\n", mul, div); min_pre_pll_clk_div = max_t(uint16_t, min_pre_pll_clk_div, clk_div_even_up( DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, limits->max_pll_op_freq_hz))); dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n", min_pre_pll_clk_div, max_pre_pll_clk_div); for (pll->pre_pll_clk_div = min_pre_pll_clk_div; pll->pre_pll_clk_div <= max_pre_pll_clk_div; pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { rval = __smiapp_pll_calculate(dev, limits, op_limits, pll, op_pll, mul, div, lane_op_clock_ratio); if (rval) continue; print_pll(dev, pll); return 0; } dev_dbg(dev, "unable to compute pre_pll divisor\n"); return rval; }