2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-02-25 20:24:50 +04:00
/*
* Aptina Sensor PLL Configuration
*
* Copyright ( C ) 2012 Laurent Pinchart < laurent . pinchart @ ideasonboard . com >
*/
# include <linux/device.h>
# include <linux/gcd.h>
# include <linux/kernel.h>
# include <linux/lcm.h>
# include <linux/module.h>
# include "aptina-pll.h"
int aptina_pll_calculate ( struct device * dev ,
const struct aptina_pll_limits * limits ,
struct aptina_pll * pll )
{
unsigned int mf_min ;
unsigned int mf_max ;
unsigned int p1_min ;
unsigned int p1_max ;
unsigned int p1 ;
unsigned int div ;
dev_dbg ( dev , " PLL: ext clock %u pix clock %u \n " ,
pll - > ext_clock , pll - > pix_clock ) ;
if ( pll - > ext_clock < limits - > ext_clock_min | |
pll - > ext_clock > limits - > ext_clock_max ) {
dev_err ( dev , " pll: invalid external clock frequency. \n " ) ;
return - EINVAL ;
}
if ( pll - > pix_clock = = 0 | | pll - > pix_clock > limits - > pix_clock_max ) {
dev_err ( dev , " pll: invalid pixel clock frequency. \n " ) ;
return - EINVAL ;
}
/* Compute the multiplier M and combined N*P1 divisor. */
div = gcd ( pll - > pix_clock , pll - > ext_clock ) ;
pll - > m = pll - > pix_clock / div ;
div = pll - > ext_clock / div ;
/* We now have the smallest M and N*P1 values that will result in the
* desired pixel clock frequency , but they might be out of the valid
* range . Compute the factor by which we should multiply them given the
* following constraints :
*
* - minimum / maximum multiplier
* - minimum / maximum multiplier output clock frequency assuming the
* minimum / maximum N value
* - minimum / maximum combined N * P1 divisor
*/
mf_min = DIV_ROUND_UP ( limits - > m_min , pll - > m ) ;
mf_min = max ( mf_min , limits - > out_clock_min /
( pll - > ext_clock / limits - > n_min * pll - > m ) ) ;
mf_min = max ( mf_min , limits - > n_min * limits - > p1_min / div ) ;
mf_max = limits - > m_max / pll - > m ;
mf_max = min ( mf_max , limits - > out_clock_max /
( pll - > ext_clock / limits - > n_max * pll - > m ) ) ;
mf_max = min ( mf_max , DIV_ROUND_UP ( limits - > n_max * limits - > p1_max , div ) ) ;
dev_dbg ( dev , " pll: mf min %u max %u \n " , mf_min , mf_max ) ;
if ( mf_min > mf_max ) {
dev_err ( dev , " pll: no valid combined N*P1 divisor. \n " ) ;
return - EINVAL ;
}
/*
* We ' re looking for the highest acceptable P1 value for which a
* multiplier factor MF exists that fulfills the following conditions :
*
* 1. p1 is in the [ p1_min , p1_max ] range given by the limits and is
* even
* 2. mf is in the [ mf_min , mf_max ] range computed above
* 3. div * mf is a multiple of p1 , in order to compute
* n = div * mf / p1
* m = pll - > m * mf
* 4. the internal clock frequency , given by ext_clock / n , is in the
* [ int_clock_min , int_clock_max ] range given by the limits
* 5. the output clock frequency , given by ext_clock / n * m , is in the
* [ out_clock_min , out_clock_max ] range given by the limits
*
* The first naive approach is to iterate over all p1 values acceptable
* according to ( 1 ) and all mf values acceptable according to ( 2 ) , and
* stop at the first combination that fulfills ( 3 ) , ( 4 ) and ( 5 ) . This
* has a O ( n ^ 2 ) complexity .
*
* Instead of iterating over all mf values in the [ mf_min , mf_max ] range
* we can compute the mf increment between two acceptable values
* according to ( 3 ) with
*
* mf_inc = p1 / gcd ( div , p1 ) ( 6 )
*
* and round the minimum up to the nearest multiple of mf_inc . This will
* restrict the number of mf values to be checked .
*
* Furthermore , conditions ( 4 ) and ( 5 ) only restrict the range of
* acceptable p1 and mf values by modifying the minimum and maximum
* limits . ( 5 ) can be expressed as
*
* ext_clock / ( div * mf / p1 ) * m * mf > = out_clock_min
* ext_clock / ( div * mf / p1 ) * m * mf < = out_clock_max
*
* or
*
* p1 > = out_clock_min * div / ( ext_clock * m ) ( 7 )
* p1 < = out_clock_max * div / ( ext_clock * m )
*
* Similarly , ( 4 ) can be expressed as
*
* mf > = ext_clock * p1 / ( int_clock_max * div ) ( 8 )
* mf < = ext_clock * p1 / ( int_clock_min * div )
*
* We can thus iterate over the restricted p1 range defined by the
* combination of ( 1 ) and ( 7 ) , and then compute the restricted mf range
* defined by the combination of ( 2 ) , ( 6 ) and ( 8 ) . If the resulting mf
* range is not empty , any value in the mf range is acceptable . We thus
* select the mf lwoer bound and the corresponding p1 value .
*/
if ( limits - > p1_min = = 0 ) {
dev_err ( dev , " pll: P1 minimum value must be >0. \n " ) ;
return - EINVAL ;
}
p1_min = max ( limits - > p1_min , DIV_ROUND_UP ( limits - > out_clock_min * div ,
pll - > ext_clock * pll - > m ) ) ;
p1_max = min ( limits - > p1_max , limits - > out_clock_max * div /
( pll - > ext_clock * pll - > m ) ) ;
for ( p1 = p1_max & ~ 1 ; p1 > = p1_min ; p1 - = 2 ) {
unsigned int mf_inc = p1 / gcd ( div , p1 ) ;
unsigned int mf_high ;
unsigned int mf_low ;
2012-04-23 16:59:25 +04:00
mf_low = roundup ( max ( mf_min , DIV_ROUND_UP ( pll - > ext_clock * p1 ,
limits - > int_clock_max * div ) ) , mf_inc ) ;
2012-02-25 20:24:50 +04:00
mf_high = min ( mf_max , pll - > ext_clock * p1 /
( limits - > int_clock_min * div ) ) ;
if ( mf_low > mf_high )
continue ;
pll - > n = div * mf_low / p1 ;
pll - > m * = mf_low ;
pll - > p1 = p1 ;
dev_dbg ( dev , " PLL: N %u M %u P1 %u \n " , pll - > n , pll - > m , pll - > p1 ) ;
return 0 ;
}
dev_err ( dev , " pll: no valid N and P1 divisors found. \n " ) ;
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( aptina_pll_calculate ) ;
MODULE_DESCRIPTION ( " Aptina PLL Helpers " ) ;
MODULE_AUTHOR ( " Laurent Pinchart <laurent.pinchart@ideasonboard.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;