2011-12-07 20:45:25 +04:00
/*
2012-08-14 23:23:43 +04:00
* drivers / media / i2c / smiapp - pll . c
2011-12-07 20:45:25 +04:00
*
* Generic driver for SMIA / SMIA + + compliant camera modules
*
* Copyright ( C ) 2011 - - 2012 Nokia Corporation
2012-10-28 13:44:17 +04:00
* Contact : Sakari Ailus < sakari . ailus @ iki . fi >
2011-12-07 20:45:25 +04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*/
2014-10-18 13:16:20 +04:00
# include <linux/device.h>
2011-12-07 20:45:25 +04:00
# include <linux/gcd.h>
# include <linux/lcm.h>
# include <linux/module.h>
# include "smiapp-pll.h"
/* Return an even number or one. */
static inline uint32_t clk_div_even ( uint32_t a )
{
return max_t ( uint32_t , 1 , a & ~ 1 ) ;
}
/* Return an even number or one. */
static inline uint32_t clk_div_even_up ( uint32_t a )
{
if ( a = = 1 )
return 1 ;
return ( a + 1 ) & ~ 1 ;
}
static inline uint32_t is_one_or_even ( uint32_t a )
{
if ( a = = 1 )
return 1 ;
if ( a & 1 )
return 0 ;
return 1 ;
}
static int bounds_check ( struct device * dev , uint32_t val ,
uint32_t min , uint32_t max , char * str )
{
if ( val > = min & & val < = max )
return 0 ;
2012-10-22 23:27:27 +04:00
dev_dbg ( dev , " %s out of bounds: %d (%d--%d) \n " , str , val , min , max ) ;
2011-12-07 20:45:25 +04:00
return - EINVAL ;
}
static void print_pll ( struct device * dev , struct smiapp_pll * pll )
{
2014-04-01 17:31:59 +04:00
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 ) ;
2014-04-01 17:22:46 +04:00
if ( ! ( pll - > flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS ) ) {
2014-09-16 16:07:11 +04:00
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 ) ;
2011-12-07 20:45:25 +04:00
}
2014-09-16 16:07:11 +04:00
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 ) ;
2011-12-07 20:45:25 +04:00
2014-04-01 17:31:59 +04:00
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 ) ;
2014-04-01 17:22:46 +04:00
if ( ! ( pll - > flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS ) ) {
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " op_sys_clk_freq_hz \t %u \n " ,
2014-09-16 16:07:11 +04:00
pll - > op . sys_clk_freq_hz ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " op_pix_clk_freq_hz \t %u \n " ,
2014-09-16 16:07:11 +04:00
pll - > op . pix_clk_freq_hz ) ;
2011-12-07 20:45:25 +04:00
}
2014-09-16 16:07:11 +04:00
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 ) ;
2011-12-07 20:45:25 +04:00
}
2014-09-16 01:35:18 +04:00
static int check_all_bounds ( struct device * dev ,
const struct smiapp_pll_limits * limits ,
2014-09-16 16:39:08 +04:00
const struct smiapp_pll_branch_limits * op_limits ,
struct smiapp_pll * pll ,
struct smiapp_pll_branch * op_pll )
2014-09-16 01:35:18 +04:00
{
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 (
2014-09-16 16:39:08 +04:00
dev , op_pll - > sys_clk_div ,
op_limits - > min_sys_clk_div , op_limits - > max_sys_clk_div ,
2014-09-16 01:35:18 +04:00
" op_sys_clk_div " ) ;
if ( ! rval )
rval = bounds_check (
2014-09-16 16:39:08 +04:00
dev , op_pll - > sys_clk_freq_hz ,
op_limits - > min_sys_clk_freq_hz ,
op_limits - > max_sys_clk_freq_hz ,
2014-09-16 01:35:18 +04:00
" op_sys_clk_freq_hz " ) ;
if ( ! rval )
rval = bounds_check (
2014-09-16 16:39:08 +04:00
dev , op_pll - > pix_clk_freq_hz ,
op_limits - > min_pix_clk_freq_hz ,
op_limits - > max_pix_clk_freq_hz ,
2014-09-16 01:35:18 +04:00
" op_pix_clk_freq_hz " ) ;
2014-09-16 01:47:32 +04:00
/*
* 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 ;
2014-09-16 01:35:18 +04:00
if ( ! rval )
rval = bounds_check (
2014-09-16 16:07:11 +04:00
dev , pll - > vt . sys_clk_freq_hz ,
2014-09-16 01:35:18 +04:00
limits - > vt . min_sys_clk_freq_hz ,
limits - > vt . max_sys_clk_freq_hz ,
" vt_sys_clk_freq_hz " ) ;
if ( ! rval )
rval = bounds_check (
2014-09-16 16:07:11 +04:00
dev , pll - > vt . pix_clk_freq_hz ,
2014-09-16 01:35:18 +04:00
limits - > vt . min_pix_clk_freq_hz ,
limits - > vt . max_pix_clk_freq_hz ,
" vt_pix_clk_freq_hz " ) ;
return rval ;
}
2013-08-10 21:49:46 +04:00
/*
* 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 .
*/
2014-09-16 16:39:08 +04:00
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 )
2011-12-07 20:45:25 +04:00
{
uint32_t sys_div ;
uint32_t best_pix_div = INT_MAX > > 1 ;
uint32_t vt_op_binning_div ;
2013-08-10 21:49:46 +04:00
/*
* 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 .
*/
2011-12-07 20:45:25 +04:00
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 ;
unsigned int i ;
/*
* Get pre_pll_clk_div so that our pll_op_clk_freq_hz won ' t be
* too high .
*/
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " pre_pll_clk_div %u \n " , pll - > pre_pll_clk_div ) ;
2011-12-07 20:45:25 +04:00
/* Don't go above max pll multiplier. */
more_mul_max = limits - > max_pll_multiplier / mul ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " more_mul_max: max_pll_multiplier check: %u \n " ,
2011-12-07 20:45:25 +04:00
more_mul_max ) ;
/* Don't go above max pll op frequency. */
more_mul_max =
2012-10-20 16:08:22 +04:00
min_t ( uint32_t ,
2011-12-07 20:45:25 +04:00
more_mul_max ,
limits - > max_pll_op_freq_hz
/ ( pll - > ext_clk_freq_hz / pll - > pre_pll_clk_div * mul ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " more_mul_max: max_pll_op_freq_hz check: %u \n " ,
2011-12-07 20:45:25 +04:00
more_mul_max ) ;
/* Don't go above the division capability of op sys clock divider. */
more_mul_max = min ( more_mul_max ,
2014-09-16 16:39:08 +04:00
op_limits - > max_sys_clk_div * pll - > pre_pll_clk_div
2011-12-07 20:45:25 +04:00
/ div ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " more_mul_max: max_op_sys_clk_div check: %u \n " ,
2011-12-07 20:45:25 +04:00
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 ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " more_mul_max: min_pll_multiplier check: %u \n " ,
2011-12-07 20:45:25 +04:00
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 ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " more_mul_min: min_pll_op_freq_hz check: %u \n " ,
2011-12-07 20:45:25 +04:00
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 ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " more_mul_min: min_pll_multiplier check: %u \n " ,
2011-12-07 20:45:25 +04:00
more_mul_min ) ;
if ( more_mul_min > more_mul_max ) {
2012-10-22 23:27:27 +04:00
dev_dbg ( dev ,
" unable to compute more_mul_min and more_mul_max \n " ) ;
2011-12-07 20:45:25 +04:00
return - EINVAL ;
}
more_mul_factor = lcm ( div , pll - > pre_pll_clk_div ) / div ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " more_mul_factor: %u \n " , more_mul_factor ) ;
2014-09-16 16:39:08 +04:00
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 " ,
2011-12-07 20:45:25 +04:00
more_mul_factor ) ;
i = roundup ( more_mul_min , more_mul_factor ) ;
if ( ! is_one_or_even ( i ) )
i < < = 1 ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " final more_mul: %u \n " , i ) ;
2011-12-07 20:45:25 +04:00
if ( i > more_mul_max ) {
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " final more_mul is bad, max %u \n " , more_mul_max ) ;
2011-12-07 20:45:25 +04:00
return - EINVAL ;
}
pll - > pll_multiplier = mul * i ;
2014-09-16 16:39:08 +04:00
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 ) ;
2011-12-07 20:45:25 +04:00
pll - > pll_ip_clk_freq_hz = pll - > ext_clk_freq_hz
/ pll - > pre_pll_clk_div ;
pll - > pll_op_clk_freq_hz = pll - > pll_ip_clk_freq_hz
* pll - > pll_multiplier ;
/* Derive pll_op_clk_freq_hz. */
2014-09-16 16:39:08 +04:00
op_pll - > sys_clk_freq_hz =
pll - > pll_op_clk_freq_hz / op_pll - > sys_clk_div ;
2011-12-07 20:45:25 +04:00
2014-09-16 16:39:08 +04:00
op_pll - > pix_clk_div = pll - > bits_per_pixel ;
dev_dbg ( dev , " op_pix_clk_div: %u \n " , op_pll - > pix_clk_div ) ;
2011-12-07 20:45:25 +04:00
2014-09-16 16:39:08 +04:00
op_pll - > pix_clk_freq_hz =
op_pll - > sys_clk_freq_hz / op_pll - > 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 ;
}
2011-12-07 20:45:25 +04:00
/*
* 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 ( limits - > min_line_length_pck_bin > limits - > min_line_length_pck
/ pll - > binning_horizontal )
vt_op_binning_div = pll - > binning_horizontal ;
else
vt_op_binning_div = 1 ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " vt_op_binning_div: %u \n " , vt_op_binning_div ) ;
2011-12-07 20:45:25 +04:00
/*
* 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 into account as well .
*
* Find absolute limits for the factor of vt divider .
*/
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " scale_m: %u \n " , pll - > scale_m ) ;
2014-09-16 16:39:08 +04:00
min_vt_div = DIV_ROUND_UP ( op_pll - > pix_clk_div * op_pll - > sys_clk_div
2011-12-07 20:45:25 +04:00
* pll - > scale_n ,
lane_op_clock_ratio * vt_op_binning_div
* pll - > scale_m ) ;
/* Find smallest and biggest allowed vt divisor. */
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min_vt_div: %u \n " , min_vt_div ) ;
2011-12-07 20:45:25 +04:00
min_vt_div = max ( min_vt_div ,
DIV_ROUND_UP ( pll - > pll_op_clk_freq_hz ,
2012-10-22 18:40:56 +04:00
limits - > vt . max_pix_clk_freq_hz ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min_vt_div: max_vt_pix_clk_freq_hz: %u \n " ,
2011-12-07 20:45:25 +04:00
min_vt_div ) ;
min_vt_div = max_t ( uint32_t , min_vt_div ,
2012-10-22 18:40:56 +04:00
limits - > vt . min_pix_clk_div
* limits - > vt . min_sys_clk_div ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min_vt_div: min_vt_clk_div: %u \n " , min_vt_div ) ;
2011-12-07 20:45:25 +04:00
2012-10-22 18:40:56 +04:00
max_vt_div = limits - > vt . max_sys_clk_div * limits - > vt . max_pix_clk_div ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " max_vt_div: %u \n " , max_vt_div ) ;
2011-12-07 20:45:25 +04:00
max_vt_div = min ( max_vt_div ,
DIV_ROUND_UP ( pll - > pll_op_clk_freq_hz ,
2012-10-22 18:40:56 +04:00
limits - > vt . min_pix_clk_freq_hz ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " max_vt_div: min_vt_pix_clk_freq_hz: %u \n " ,
2011-12-07 20:45:25 +04:00
max_vt_div ) ;
/*
* Find limitsits for sys_clk_div . Not all values are possible
* with all values of pix_clk_div .
*/
2012-10-22 18:40:56 +04:00
min_sys_div = limits - > vt . min_sys_clk_div ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min_sys_div: %u \n " , min_sys_div ) ;
2011-12-07 20:45:25 +04:00
min_sys_div = max ( min_sys_div ,
DIV_ROUND_UP ( min_vt_div ,
2012-10-22 18:40:56 +04:00
limits - > vt . max_pix_clk_div ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min_sys_div: max_vt_pix_clk_div: %u \n " , min_sys_div ) ;
2011-12-07 20:45:25 +04:00
min_sys_div = max ( min_sys_div ,
pll - > pll_op_clk_freq_hz
2012-10-22 18:40:56 +04:00
/ limits - > vt . max_sys_clk_freq_hz ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min_sys_div: max_pll_op_clk_freq_hz: %u \n " , min_sys_div ) ;
2011-12-07 20:45:25 +04:00
min_sys_div = clk_div_even_up ( min_sys_div ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min_sys_div: one or even: %u \n " , min_sys_div ) ;
2011-12-07 20:45:25 +04:00
2012-10-22 18:40:56 +04:00
max_sys_div = limits - > vt . max_sys_clk_div ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " max_sys_div: %u \n " , max_sys_div ) ;
2011-12-07 20:45:25 +04:00
max_sys_div = min ( max_sys_div ,
DIV_ROUND_UP ( max_vt_div ,
2012-10-22 18:40:56 +04:00
limits - > vt . min_pix_clk_div ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " max_sys_div: min_vt_pix_clk_div: %u \n " , max_sys_div ) ;
2011-12-07 20:45:25 +04:00
max_sys_div = min ( max_sys_div ,
DIV_ROUND_UP ( pll - > pll_op_clk_freq_hz ,
2012-10-22 18:40:56 +04:00
limits - > vt . min_pix_clk_freq_hz ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " max_sys_div: min_vt_pix_clk_freq_hz: %u \n " , max_sys_div ) ;
2011-12-07 20:45:25 +04:00
/*
* Find pix_div such that a legal pix_div * sys_div results
* 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 ;
sys_div + = 2 - ( sys_div & 1 ) ) {
2012-10-20 16:08:22 +04:00
uint16_t pix_div = DIV_ROUND_UP ( vt_div , sys_div ) ;
2011-12-07 20:45:25 +04:00
2012-10-22 18:40:56 +04:00
if ( pix_div < limits - > vt . min_pix_clk_div
| | pix_div > limits - > vt . max_pix_clk_div ) {
2011-12-07 20:45:25 +04:00
dev_dbg ( dev ,
2014-04-01 17:31:59 +04:00
" pix_div %u too small or too big (%u--%u) \n " ,
2011-12-07 20:45:25 +04:00
pix_div ,
2012-10-22 18:40:56 +04:00
limits - > vt . min_pix_clk_div ,
limits - > vt . max_pix_clk_div ) ;
2011-12-07 20:45:25 +04:00
continue ;
}
/* Check if this one is better. */
if ( pix_div * sys_div
< = roundup ( min_vt_div , best_pix_div ) )
best_pix_div = pix_div ;
}
if ( best_pix_div < INT_MAX > > 1 )
break ;
}
2014-09-16 16:07:11 +04:00
pll - > vt . sys_clk_div = DIV_ROUND_UP ( min_vt_div , best_pix_div ) ;
pll - > vt . pix_clk_div = best_pix_div ;
2011-12-07 20:45:25 +04:00
2014-09-16 16:07:11 +04:00
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 ;
2011-12-07 20:45:25 +04:00
2014-09-16 16:39:08 +04:00
out_skip_vt_calc :
2011-12-07 20:45:25 +04:00
pll - > pixel_rate_csi =
2014-09-16 16:39:08 +04:00
op_pll - > pix_clk_freq_hz * lane_op_clock_ratio ;
2014-04-02 02:18:09 +04:00
pll - > pixel_rate_pixel_array = pll - > vt . pix_clk_freq_hz ;
2011-12-07 20:45:25 +04:00
2014-09-16 16:39:08 +04:00
return check_all_bounds ( dev , limits , op_limits , pll , op_pll ) ;
2011-12-07 20:45:25 +04:00
}
2012-10-22 23:27:27 +04:00
2012-10-22 18:40:57 +04:00
int smiapp_pll_calculate ( struct device * dev ,
const struct smiapp_pll_limits * limits ,
2012-10-22 23:27:27 +04:00
struct smiapp_pll * pll )
{
2014-09-16 16:39:08 +04:00
const struct smiapp_pll_branch_limits * op_limits = & limits - > op ;
struct smiapp_pll_branch * op_pll = & pll - > op ;
2012-10-22 18:40:57 +04:00
uint16_t min_pre_pll_clk_div ;
uint16_t max_pre_pll_clk_div ;
2012-10-22 23:27:27 +04:00
uint32_t lane_op_clock_ratio ;
uint32_t mul , div ;
unsigned int i ;
int rval = - EINVAL ;
2014-09-16 16:39:08 +04:00
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 ;
}
2012-10-22 23:27:27 +04:00
if ( pll - > flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE )
2012-10-20 17:35:25 +04:00
lane_op_clock_ratio = pll - > csi2 . lanes ;
2012-10-22 23:27:27 +04:00
else
lane_op_clock_ratio = 1 ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " lane_op_clock_ratio: %u \n " , lane_op_clock_ratio ) ;
2012-10-22 23:27:27 +04:00
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " binning: %ux%u \n " , pll - > binning_horizontal ,
2012-10-22 23:27:27 +04:00
pll - > binning_vertical ) ;
2012-10-20 17:35:25 +04:00
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 ;
}
2012-10-22 23:27:27 +04:00
/* Figure out limits for pre-pll divider based on extclk */
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " min / max pre_pll_clk_div: %u / %u \n " ,
2012-10-22 23:27:27 +04:00
limits - > min_pre_pll_clk_div , limits - > max_pre_pll_clk_div ) ;
2012-10-22 18:40:57 +04:00
max_pre_pll_clk_div =
2012-10-22 23:27:27 +04:00
min_t ( uint16_t , limits - > max_pre_pll_clk_div ,
clk_div_even ( pll - > ext_clk_freq_hz /
limits - > min_pll_ip_freq_hz ) ) ;
2012-10-22 18:40:57 +04:00
min_pre_pll_clk_div =
2012-10-22 23:27:27 +04:00
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 ) ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " pre-pll check: min / max pre_pll_clk_div: %u / %u \n " ,
2012-10-22 18:40:57 +04:00
min_pre_pll_clk_div , max_pre_pll_clk_div ) ;
2012-10-22 23:27:27 +04:00
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 ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " mul %u / div %u \n " , mul , div ) ;
2012-10-22 23:27:27 +04:00
2012-10-22 18:40:57 +04:00
min_pre_pll_clk_div =
max_t ( uint16_t , min_pre_pll_clk_div ,
2012-10-22 23:27:27 +04:00
clk_div_even_up (
DIV_ROUND_UP ( mul * pll - > ext_clk_freq_hz ,
limits - > max_pll_op_freq_hz ) ) ) ;
2014-04-01 17:31:59 +04:00
dev_dbg ( dev , " pll_op check: min / max pre_pll_clk_div: %u / %u \n " ,
2012-10-22 18:40:57 +04:00
min_pre_pll_clk_div , max_pre_pll_clk_div ) ;
2012-10-22 23:27:27 +04:00
2012-10-22 18:40:57 +04:00
for ( pll - > pre_pll_clk_div = min_pre_pll_clk_div ;
pll - > pre_pll_clk_div < = max_pre_pll_clk_div ;
2012-10-22 23:27:27 +04:00
pll - > pre_pll_clk_div + = 2 - ( pll - > pre_pll_clk_div & 1 ) ) {
2014-09-16 16:39:08 +04:00
rval = __smiapp_pll_calculate ( dev , limits , op_limits , pll ,
op_pll , mul , div ,
2012-10-22 23:27:27 +04:00
lane_op_clock_ratio ) ;
if ( rval )
continue ;
print_pll ( dev , pll ) ;
return 0 ;
}
2016-09-08 16:50:07 +03:00
dev_dbg ( dev , " unable to compute pre_pll divisor \n " ) ;
2012-10-22 23:27:27 +04:00
return rval ;
}
2011-12-07 20:45:25 +04:00
EXPORT_SYMBOL_GPL ( smiapp_pll_calculate ) ;
2012-10-28 13:44:17 +04:00
MODULE_AUTHOR ( " Sakari Ailus <sakari.ailus@iki.fi> " ) ;
2011-12-07 20:45:25 +04:00
MODULE_DESCRIPTION ( " Generic SMIA/SMIA++ PLL calculator " ) ;
MODULE_LICENSE ( " GPL " ) ;