diff mbox series

[v2,04/12] media: i2c: Support 19.2MHz input clock in ov8865

Message ID 20210809225845.916430-5-djrscally@gmail.com
State New
Headers show
Series Extensions to ov8865 driver | expand

Commit Message

Daniel Scally Aug. 9, 2021, 10:58 p.m. UTC
The ov8865 driver as written expects a 24MHz input clock, but the sensor
is sometimes found on x86 platforms with a 19.2MHz input clock supplied.
Add a set of PLL configurations to the driver to support that rate too.
As ACPI doesn't auto-configure the clock rate, check for a clock-frequency
during probe and set that rate if one is found.

Signed-off-by: Daniel Scally <djrscally@gmail.com>
---
Changes in v2:

	- Added an enum defining the possible frequency rates to index the
	  array (Andy)

 drivers/media/i2c/ov8865.c | 164 +++++++++++++++++++++++++++----------
 1 file changed, 121 insertions(+), 43 deletions(-)

Comments

Andy Shevchenko Aug. 10, 2021, 1:04 p.m. UTC | #1
On Tue, Aug 10, 2021 at 1:59 AM Daniel Scally <djrscally@gmail.com> wrote:
>

> The ov8865 driver as written expects a 24MHz input clock, but the sensor

> is sometimes found on x86 platforms with a 19.2MHz input clock supplied.

> Add a set of PLL configurations to the driver to support that rate too.

> As ACPI doesn't auto-configure the clock rate, check for a clock-frequency

> during probe and set that rate if one is found.


...

> +enum extclk_rate {

> +       OV8865_19_2_MHZ,

> +       OV8865_24_MHZ,

> +       OV8865_NUM_SUPPORTED_RATES,


Same here, i.e. no comma,
If these are the only problems during review of v2, you can ignore
them, they are not critical at all.

> +};


...

> +static const struct ov8865_pll2_config ov8865_pll2_configs_native[] = {


> +       /* 24MHz input clock */

> +       {

> +               .pll_pre_div_half       = 1,

> +               .pll_pre_div            = 0,

> +               .pll_mul                = 30,

> +               .dac_div                = 2,

> +               .sys_pre_div            = 5,

> +               .sys_div                = 0,

> +       }


+ comma.

>  };


...

> +       /* 24MHz input clock */

> +       {

>         .pll_pre_div_half       = 1,

>         .pll_pre_div            = 0,

>         .pll_mul                = 30,

>         .dac_div                = 2,

>         .sys_pre_div            = 10,

>         .sys_div                = 0,

> +       }


Ditto.

...

> +       /*

> +        * We could have either a 24MHz or 19.2MHz clock rate. Check for a

> +        * clock-frequency property and if found, set that rate. This should

> +        * cover ACPI case. If the system uses devicetree then the configured


the ACPI case

> +        * rate should already be set, so we'll have to check it.

> +        */


> +


Redundant blank line.

> +       ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",

> +                                      &rate);

> +       if (!ret) {


I'm wondering if something like

... rate = 0;

       fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &rate);
       if (rate > 0) {

can be used.

> +               ret = clk_set_rate(sensor->extclk, rate);

> +               if (ret) {

> +                       dev_err(dev, "failed to set clock rate\n");

> +                       return ret;

> +               }

> +       }


-- 
With Best Regards,
Andy Shevchenko
Sakari Ailus Aug. 10, 2021, 1:34 p.m. UTC | #2
Hi Daniel,

Thanks for the set.

On Mon, Aug 09, 2021 at 11:58:37PM +0100, Daniel Scally wrote:
> The ov8865 driver as written expects a 24MHz input clock, but the sensor

> is sometimes found on x86 platforms with a 19.2MHz input clock supplied.

> Add a set of PLL configurations to the driver to support that rate too.

> As ACPI doesn't auto-configure the clock rate, check for a clock-frequency

> during probe and set that rate if one is found.

> 

> Signed-off-by: Daniel Scally <djrscally@gmail.com>

> ---

> Changes in v2:

> 

> 	- Added an enum defining the possible frequency rates to index the

> 	  array (Andy)

> 

>  drivers/media/i2c/ov8865.c | 164 +++++++++++++++++++++++++++----------

>  1 file changed, 121 insertions(+), 43 deletions(-)

> 

> diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c

> index fe700787bfb9..1382b16d1a09 100644

> --- a/drivers/media/i2c/ov8865.c

> +++ b/drivers/media/i2c/ov8865.c

> @@ -21,10 +21,6 @@

>  #include <media/v4l2-image-sizes.h>

>  #include <media/v4l2-mediabus.h>

>  

> -/* Clock rate */

> -

> -#define OV8865_EXTCLK_RATE			24000000

> -

>  /* Register definitions */

>  

>  /* System */

> @@ -567,6 +563,19 @@ struct ov8865_sclk_config {

>  	unsigned int sclk_div;

>  };

>  

> +/* Clock rate */

> +

> +enum extclk_rate {

> +	OV8865_19_2_MHZ,

> +	OV8865_24_MHZ,

> +	OV8865_NUM_SUPPORTED_RATES,

> +};

> +

> +static const unsigned long supported_extclk_rates[] = {

> +	[OV8865_19_2_MHZ] = 19200000,

> +	[OV8865_24_MHZ] = 24000000,

> +};

> +

>  /*

>   * General formulas for (array-centered) mode calculation:

>   * - photo_array_width = 3296

> @@ -665,6 +674,9 @@ struct ov8865_sensor {

>  	struct regulator *avdd;

>  	struct regulator *dvdd;

>  	struct regulator *dovdd;

> +

> +	unsigned long extclk_rate;

> +	enum extclk_rate extclk_rate_idx;

>  	struct clk *extclk;

>  

>  	struct v4l2_fwnode_endpoint endpoint;

> @@ -680,49 +692,83 @@ struct ov8865_sensor {

>  /* Static definitions */

>  

>  /*

> - * EXTCLK = 24 MHz

>   * PHY_SCLK = 720 MHz

>   * MIPI_PCLK = 90 MHz

>   */

> -static const struct ov8865_pll1_config ov8865_pll1_config_native = {

> -	.pll_pre_div_half	= 1,

> -	.pll_pre_div		= 0,

> -	.pll_mul		= 30,

> -	.m_div			= 1,

> -	.mipi_div		= 3,

> -	.pclk_div		= 1,

> -	.sys_pre_div		= 1,

> -	.sys_div		= 2,

> +

> +static const struct ov8865_pll1_config ov8865_pll1_configs_native[] = {

> +	{ /* 19.2 MHz input clock */

> +		.pll_pre_div_half	= 1,

> +		.pll_pre_div		= 2,

> +		.pll_mul		= 75,

> +		.m_div			= 1,

> +		.mipi_div		= 3,

> +		.pclk_div		= 1,

> +		.sys_pre_div		= 1,

> +		.sys_div		= 2,

> +	},

> +	{ /* 24MHz input clock */

> +		.pll_pre_div_half	= 1,

> +		.pll_pre_div		= 0,

> +		.pll_mul		= 30,

> +		.m_div			= 1,

> +		.mipi_div		= 3,

> +		.pclk_div		= 1,

> +		.sys_pre_div		= 1,

> +		.sys_div		= 2,

> +	},


Could you instead add a struct specific to the clock frequency with
pointers to these? See e.g. the ov8856 driver how this could otherwise end
up...

>  };

>  

>  /*

> - * EXTCLK = 24 MHz

>   * DAC_CLK = 360 MHz

>   * SCLK = 144 MHz

>   */

>  

> -static const struct ov8865_pll2_config ov8865_pll2_config_native = {

> -	.pll_pre_div_half	= 1,

> -	.pll_pre_div		= 0,

> -	.pll_mul		= 30,

> -	.dac_div		= 2,

> -	.sys_pre_div		= 5,

> -	.sys_div		= 0,

> +static const struct ov8865_pll2_config ov8865_pll2_configs_native[] = {

> +	/* 19.2MHz input clock */

> +	{

> +		.pll_pre_div_half	= 1,

> +		.pll_pre_div		= 5,

> +		.pll_mul		= 75,

> +		.dac_div		= 1,

> +		.sys_pre_div		= 1,

> +		.sys_div		= 3,

> +	},

> +	/* 24MHz input clock */

> +	{

> +		.pll_pre_div_half	= 1,

> +		.pll_pre_div		= 0,

> +		.pll_mul		= 30,

> +		.dac_div		= 2,

> +		.sys_pre_div		= 5,

> +		.sys_div		= 0,

> +	}

>  };

>  

>  /*

> - * EXTCLK = 24 MHz

>   * DAC_CLK = 360 MHz

>   * SCLK = 72 MHz

>   */

>  

> -static const struct ov8865_pll2_config ov8865_pll2_config_binning = {

> +static const struct ov8865_pll2_config ov8865_pll2_configs_binning[] = {

> +	/* 19.2MHz input clock */

> +	{

> +	.pll_pre_div_half	= 1,

> +	.pll_pre_div		= 2,

> +	.pll_mul		= 75,

> +	.dac_div		= 2,

> +	.sys_pre_div		= 10,

> +	.sys_div		= 0,

> +	},

> +	/* 24MHz input clock */

> +	{

>  	.pll_pre_div_half	= 1,

>  	.pll_pre_div		= 0,

>  	.pll_mul		= 30,

>  	.dac_div		= 2,

>  	.sys_pre_div		= 10,

>  	.sys_div		= 0,

> +	}

>  };

>  

>  static const struct ov8865_sclk_config ov8865_sclk_config_native = {

> @@ -934,8 +980,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>  		.frame_interval			= { 1, 30 },

>  

>  		/* PLL */

> -		.pll1_config			= &ov8865_pll1_config_native,

> -		.pll2_config			= &ov8865_pll2_config_native,

> +		.pll1_config			= ov8865_pll1_configs_native,

> +		.pll2_config			= ov8865_pll2_configs_native,

>  		.sclk_config			= &ov8865_sclk_config_native,

>  

>  		/* Registers */

> @@ -990,8 +1036,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>  		.frame_interval			= { 1, 30 },

>  

>  		/* PLL */

> -		.pll1_config			= &ov8865_pll1_config_native,

> -		.pll2_config			= &ov8865_pll2_config_native,

> +		.pll1_config			= ov8865_pll1_configs_native,

> +		.pll2_config			= ov8865_pll2_configs_native,

>  		.sclk_config			= &ov8865_sclk_config_native,

>  

>  		/* Registers */

> @@ -1050,8 +1096,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>  		.frame_interval			= { 1, 30 },

>  

>  		/* PLL */

> -		.pll1_config			= &ov8865_pll1_config_native,

> -		.pll2_config			= &ov8865_pll2_config_binning,

> +		.pll1_config			= ov8865_pll1_configs_native,

> +		.pll2_config			= ov8865_pll2_configs_binning,

>  		.sclk_config			= &ov8865_sclk_config_native,

>  

>  		/* Registers */

> @@ -1116,8 +1162,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>  		.frame_interval			= { 1, 90 },

>  

>  		/* PLL */

> -		.pll1_config			= &ov8865_pll1_config_native,

> -		.pll2_config			= &ov8865_pll2_config_binning,

> +		.pll1_config			= ov8865_pll1_configs_native,

> +		.pll2_config			= ov8865_pll2_configs_binning,

>  		.sclk_config			= &ov8865_sclk_config_native,

>  

>  		/* Registers */

> @@ -1513,12 +1559,11 @@ static int ov8865_isp_configure(struct ov8865_sensor *sensor)

>  static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor,

>  					   const struct ov8865_mode *mode)

>  {

> -	const struct ov8865_pll1_config *config = mode->pll1_config;

> -	unsigned long extclk_rate;

> +	const struct ov8865_pll1_config *config;

>  	unsigned long pll1_rate;

>  

> -	extclk_rate = clk_get_rate(sensor->extclk);

> -	pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half;

> +	config = &mode->pll1_config[sensor->extclk_rate_idx];

> +	pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half;

>  

>  	switch (config->pll_pre_div) {

>  	case 0:

> @@ -1552,10 +1597,12 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,

>  				      const struct ov8865_mode *mode,

>  				      u32 mbus_code)

>  {

> -	const struct ov8865_pll1_config *config = mode->pll1_config;

> +	const struct ov8865_pll1_config *config;

>  	u8 value;

>  	int ret;

>  

> +	config = &mode->pll1_config[sensor->extclk_rate_idx];

> +

>  	switch (mbus_code) {

>  	case MEDIA_BUS_FMT_SBGGR10_1X10:

>  		value = OV8865_MIPI_BIT_SEL(10);

> @@ -1622,9 +1669,11 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,

>  static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor,

>  				      const struct ov8865_mode *mode)

>  {

> -	const struct ov8865_pll2_config *config = mode->pll2_config;

> +	const struct ov8865_pll2_config *config;

>  	int ret;

>  

> +	config = &mode->pll2_config[sensor->extclk_rate_idx];

> +

>  	ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG,

>  			   OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) |

>  			   OV8865_PLL_CTRL12_DAC_DIV(config->dac_div));

> @@ -2053,9 +2102,11 @@ static int ov8865_mode_configure(struct ov8865_sensor *sensor,

>  static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor,

>  					       const struct ov8865_mode *mode)

>  {

> -	const struct ov8865_pll1_config *config = mode->pll1_config;

> +	const struct ov8865_pll1_config *config;

>  	unsigned long pll1_rate;

>  

> +	config = &mode->pll1_config[sensor->extclk_rate_idx];

> +

>  	pll1_rate = ov8865_mode_pll1_rate(sensor, mode);

>  

>  	return pll1_rate / config->m_div / 2;

> @@ -2783,7 +2834,8 @@ static int ov8865_probe(struct i2c_client *client)

>  	struct ov8865_sensor *sensor;

>  	struct v4l2_subdev *subdev;

>  	struct media_pad *pad;

> -	unsigned long rate;

> +	unsigned int rate;

> +	unsigned int i;

>  	int ret;

>  

>  	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);

> @@ -2858,13 +2910,39 @@ static int ov8865_probe(struct i2c_client *client)

>  		goto error_endpoint;

>  	}

>  

> -	rate = clk_get_rate(sensor->extclk);

> -	if (rate != OV8865_EXTCLK_RATE) {

> -		dev_err(dev, "clock rate %lu Hz is unsupported\n", rate);

> +	/*

> +	 * We could have either a 24MHz or 19.2MHz clock rate. Check for a

> +	 * clock-frequency property and if found, set that rate. This should

> +	 * cover ACPI case. If the system uses devicetree then the configured

> +	 * rate should already be set, so we'll have to check it.

> +	 */

> +

> +	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",

> +				       &rate);

> +	if (!ret) {

> +		ret = clk_set_rate(sensor->extclk, rate);

> +		if (ret) {

> +			dev_err(dev, "failed to set clock rate\n");

> +			return ret;

> +		}

> +	}

> +

> +	sensor->extclk_rate = clk_get_rate(sensor->extclk);

> +

> +	for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) {

> +		if (sensor->extclk_rate == supported_extclk_rates[i])

> +			break;

> +	}

> +

> +	if (i == ARRAY_SIZE(supported_extclk_rates)) {

> +		dev_err(dev, "clock rate %lu Hz is unsupported\n",

> +			sensor->extclk_rate);

>  		ret = -EINVAL;

>  		goto error_endpoint;

>  	}

>  

> +	sensor->extclk_rate_idx = i;

> +

>  	/* Subdev, entity and pad */

>  

>  	subdev = &sensor->subdev;


-- 
Kind regards,

Sakari Ailus
Daniel Scally Aug. 10, 2021, 9:37 p.m. UTC | #3
Hi Sakari - thanks for all the comments

On 10/08/2021 14:34, Sakari Ailus wrote:
> Hi Daniel,

>

> Thanks for the set.

>

> On Mon, Aug 09, 2021 at 11:58:37PM +0100, Daniel Scally wrote:

>> The ov8865 driver as written expects a 24MHz input clock, but the sensor

>> is sometimes found on x86 platforms with a 19.2MHz input clock supplied.

>> Add a set of PLL configurations to the driver to support that rate too.

>> As ACPI doesn't auto-configure the clock rate, check for a clock-frequency

>> during probe and set that rate if one is found.

>>

>> Signed-off-by: Daniel Scally <djrscally@gmail.com>

>> ---

>> Changes in v2:

>>

>> 	- Added an enum defining the possible frequency rates to index the

>> 	  array (Andy)

>>

>>  drivers/media/i2c/ov8865.c | 164 +++++++++++++++++++++++++++----------

>>  1 file changed, 121 insertions(+), 43 deletions(-)

>>

>> diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c

>> index fe700787bfb9..1382b16d1a09 100644

>> --- a/drivers/media/i2c/ov8865.c

>> +++ b/drivers/media/i2c/ov8865.c

>> @@ -21,10 +21,6 @@

>>  #include <media/v4l2-image-sizes.h>

>>  #include <media/v4l2-mediabus.h>

>>  

>> -/* Clock rate */

>> -

>> -#define OV8865_EXTCLK_RATE			24000000

>> -

>>  /* Register definitions */

>>  

>>  /* System */

>> @@ -567,6 +563,19 @@ struct ov8865_sclk_config {

>>  	unsigned int sclk_div;

>>  };

>>  

>> +/* Clock rate */

>> +

>> +enum extclk_rate {

>> +	OV8865_19_2_MHZ,

>> +	OV8865_24_MHZ,

>> +	OV8865_NUM_SUPPORTED_RATES,

>> +};

>> +

>> +static const unsigned long supported_extclk_rates[] = {

>> +	[OV8865_19_2_MHZ] = 19200000,

>> +	[OV8865_24_MHZ] = 24000000,

>> +};

>> +

>>  /*

>>   * General formulas for (array-centered) mode calculation:

>>   * - photo_array_width = 3296

>> @@ -665,6 +674,9 @@ struct ov8865_sensor {

>>  	struct regulator *avdd;

>>  	struct regulator *dvdd;

>>  	struct regulator *dovdd;

>> +

>> +	unsigned long extclk_rate;

>> +	enum extclk_rate extclk_rate_idx;

>>  	struct clk *extclk;

>>  

>>  	struct v4l2_fwnode_endpoint endpoint;

>> @@ -680,49 +692,83 @@ struct ov8865_sensor {

>>  /* Static definitions */

>>  

>>  /*

>> - * EXTCLK = 24 MHz

>>   * PHY_SCLK = 720 MHz

>>   * MIPI_PCLK = 90 MHz

>>   */

>> -static const struct ov8865_pll1_config ov8865_pll1_config_native = {

>> -	.pll_pre_div_half	= 1,

>> -	.pll_pre_div		= 0,

>> -	.pll_mul		= 30,

>> -	.m_div			= 1,

>> -	.mipi_div		= 3,

>> -	.pclk_div		= 1,

>> -	.sys_pre_div		= 1,

>> -	.sys_div		= 2,

>> +

>> +static const struct ov8865_pll1_config ov8865_pll1_configs_native[] = {

>> +	{ /* 19.2 MHz input clock */

>> +		.pll_pre_div_half	= 1,

>> +		.pll_pre_div		= 2,

>> +		.pll_mul		= 75,

>> +		.m_div			= 1,

>> +		.mipi_div		= 3,

>> +		.pclk_div		= 1,

>> +		.sys_pre_div		= 1,

>> +		.sys_div		= 2,

>> +	},

>> +	{ /* 24MHz input clock */

>> +		.pll_pre_div_half	= 1,

>> +		.pll_pre_div		= 0,

>> +		.pll_mul		= 30,

>> +		.m_div			= 1,

>> +		.mipi_div		= 3,

>> +		.pclk_div		= 1,

>> +		.sys_pre_div		= 1,

>> +		.sys_div		= 2,

>> +	},

> Could you instead add a struct specific to the clock frequency with

> pointers to these? See e.g. the ov8856 driver how this could otherwise end

> up...



You mean something like


static struct ov8865_pll_configs_19_2_mhz {

    .pll1_config_native = &ov8865_pll1_config_native,

    ...

};



static struct ov8865_pll_configs_24_mhz {

    .pll1_config_native = &ov8865_pll1_config_native,

    ...

};


or am I misunderstanding?

>

>>  };

>>  

>>  /*

>> - * EXTCLK = 24 MHz

>>   * DAC_CLK = 360 MHz

>>   * SCLK = 144 MHz

>>   */

>>  

>> -static const struct ov8865_pll2_config ov8865_pll2_config_native = {

>> -	.pll_pre_div_half	= 1,

>> -	.pll_pre_div		= 0,

>> -	.pll_mul		= 30,

>> -	.dac_div		= 2,

>> -	.sys_pre_div		= 5,

>> -	.sys_div		= 0,

>> +static const struct ov8865_pll2_config ov8865_pll2_configs_native[] = {

>> +	/* 19.2MHz input clock */

>> +	{

>> +		.pll_pre_div_half	= 1,

>> +		.pll_pre_div		= 5,

>> +		.pll_mul		= 75,

>> +		.dac_div		= 1,

>> +		.sys_pre_div		= 1,

>> +		.sys_div		= 3,

>> +	},

>> +	/* 24MHz input clock */

>> +	{

>> +		.pll_pre_div_half	= 1,

>> +		.pll_pre_div		= 0,

>> +		.pll_mul		= 30,

>> +		.dac_div		= 2,

>> +		.sys_pre_div		= 5,

>> +		.sys_div		= 0,

>> +	}

>>  };

>>  

>>  /*

>> - * EXTCLK = 24 MHz

>>   * DAC_CLK = 360 MHz

>>   * SCLK = 72 MHz

>>   */

>>  

>> -static const struct ov8865_pll2_config ov8865_pll2_config_binning = {

>> +static const struct ov8865_pll2_config ov8865_pll2_configs_binning[] = {

>> +	/* 19.2MHz input clock */

>> +	{

>> +	.pll_pre_div_half	= 1,

>> +	.pll_pre_div		= 2,

>> +	.pll_mul		= 75,

>> +	.dac_div		= 2,

>> +	.sys_pre_div		= 10,

>> +	.sys_div		= 0,

>> +	},

>> +	/* 24MHz input clock */

>> +	{

>>  	.pll_pre_div_half	= 1,

>>  	.pll_pre_div		= 0,

>>  	.pll_mul		= 30,

>>  	.dac_div		= 2,

>>  	.sys_pre_div		= 10,

>>  	.sys_div		= 0,

>> +	}

>>  };

>>  

>>  static const struct ov8865_sclk_config ov8865_sclk_config_native = {

>> @@ -934,8 +980,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>>  		.frame_interval			= { 1, 30 },

>>  

>>  		/* PLL */

>> -		.pll1_config			= &ov8865_pll1_config_native,

>> -		.pll2_config			= &ov8865_pll2_config_native,

>> +		.pll1_config			= ov8865_pll1_configs_native,

>> +		.pll2_config			= ov8865_pll2_configs_native,

>>  		.sclk_config			= &ov8865_sclk_config_native,

>>  

>>  		/* Registers */

>> @@ -990,8 +1036,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>>  		.frame_interval			= { 1, 30 },

>>  

>>  		/* PLL */

>> -		.pll1_config			= &ov8865_pll1_config_native,

>> -		.pll2_config			= &ov8865_pll2_config_native,

>> +		.pll1_config			= ov8865_pll1_configs_native,

>> +		.pll2_config			= ov8865_pll2_configs_native,

>>  		.sclk_config			= &ov8865_sclk_config_native,

>>  

>>  		/* Registers */

>> @@ -1050,8 +1096,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>>  		.frame_interval			= { 1, 30 },

>>  

>>  		/* PLL */

>> -		.pll1_config			= &ov8865_pll1_config_native,

>> -		.pll2_config			= &ov8865_pll2_config_binning,

>> +		.pll1_config			= ov8865_pll1_configs_native,

>> +		.pll2_config			= ov8865_pll2_configs_binning,

>>  		.sclk_config			= &ov8865_sclk_config_native,

>>  

>>  		/* Registers */

>> @@ -1116,8 +1162,8 @@ static const struct ov8865_mode ov8865_modes[] = {

>>  		.frame_interval			= { 1, 90 },

>>  

>>  		/* PLL */

>> -		.pll1_config			= &ov8865_pll1_config_native,

>> -		.pll2_config			= &ov8865_pll2_config_binning,

>> +		.pll1_config			= ov8865_pll1_configs_native,

>> +		.pll2_config			= ov8865_pll2_configs_binning,

>>  		.sclk_config			= &ov8865_sclk_config_native,

>>  

>>  		/* Registers */

>> @@ -1513,12 +1559,11 @@ static int ov8865_isp_configure(struct ov8865_sensor *sensor)

>>  static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor,

>>  					   const struct ov8865_mode *mode)

>>  {

>> -	const struct ov8865_pll1_config *config = mode->pll1_config;

>> -	unsigned long extclk_rate;

>> +	const struct ov8865_pll1_config *config;

>>  	unsigned long pll1_rate;

>>  

>> -	extclk_rate = clk_get_rate(sensor->extclk);

>> -	pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half;

>> +	config = &mode->pll1_config[sensor->extclk_rate_idx];

>> +	pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half;

>>  

>>  	switch (config->pll_pre_div) {

>>  	case 0:

>> @@ -1552,10 +1597,12 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,

>>  				      const struct ov8865_mode *mode,

>>  				      u32 mbus_code)

>>  {

>> -	const struct ov8865_pll1_config *config = mode->pll1_config;

>> +	const struct ov8865_pll1_config *config;

>>  	u8 value;

>>  	int ret;

>>  

>> +	config = &mode->pll1_config[sensor->extclk_rate_idx];

>> +

>>  	switch (mbus_code) {

>>  	case MEDIA_BUS_FMT_SBGGR10_1X10:

>>  		value = OV8865_MIPI_BIT_SEL(10);

>> @@ -1622,9 +1669,11 @@ static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,

>>  static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor,

>>  				      const struct ov8865_mode *mode)

>>  {

>> -	const struct ov8865_pll2_config *config = mode->pll2_config;

>> +	const struct ov8865_pll2_config *config;

>>  	int ret;

>>  

>> +	config = &mode->pll2_config[sensor->extclk_rate_idx];

>> +

>>  	ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG,

>>  			   OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) |

>>  			   OV8865_PLL_CTRL12_DAC_DIV(config->dac_div));

>> @@ -2053,9 +2102,11 @@ static int ov8865_mode_configure(struct ov8865_sensor *sensor,

>>  static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor,

>>  					       const struct ov8865_mode *mode)

>>  {

>> -	const struct ov8865_pll1_config *config = mode->pll1_config;

>> +	const struct ov8865_pll1_config *config;

>>  	unsigned long pll1_rate;

>>  

>> +	config = &mode->pll1_config[sensor->extclk_rate_idx];

>> +

>>  	pll1_rate = ov8865_mode_pll1_rate(sensor, mode);

>>  

>>  	return pll1_rate / config->m_div / 2;

>> @@ -2783,7 +2834,8 @@ static int ov8865_probe(struct i2c_client *client)

>>  	struct ov8865_sensor *sensor;

>>  	struct v4l2_subdev *subdev;

>>  	struct media_pad *pad;

>> -	unsigned long rate;

>> +	unsigned int rate;

>> +	unsigned int i;

>>  	int ret;

>>  

>>  	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);

>> @@ -2858,13 +2910,39 @@ static int ov8865_probe(struct i2c_client *client)

>>  		goto error_endpoint;

>>  	}

>>  

>> -	rate = clk_get_rate(sensor->extclk);

>> -	if (rate != OV8865_EXTCLK_RATE) {

>> -		dev_err(dev, "clock rate %lu Hz is unsupported\n", rate);

>> +	/*

>> +	 * We could have either a 24MHz or 19.2MHz clock rate. Check for a

>> +	 * clock-frequency property and if found, set that rate. This should

>> +	 * cover ACPI case. If the system uses devicetree then the configured

>> +	 * rate should already be set, so we'll have to check it.

>> +	 */

>> +

>> +	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",

>> +				       &rate);

>> +	if (!ret) {

>> +		ret = clk_set_rate(sensor->extclk, rate);

>> +		if (ret) {

>> +			dev_err(dev, "failed to set clock rate\n");

>> +			return ret;

>> +		}

>> +	}

>> +

>> +	sensor->extclk_rate = clk_get_rate(sensor->extclk);

>> +

>> +	for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) {

>> +		if (sensor->extclk_rate == supported_extclk_rates[i])

>> +			break;

>> +	}

>> +

>> +	if (i == ARRAY_SIZE(supported_extclk_rates)) {

>> +		dev_err(dev, "clock rate %lu Hz is unsupported\n",

>> +			sensor->extclk_rate);

>>  		ret = -EINVAL;

>>  		goto error_endpoint;

>>  	}

>>  

>> +	sensor->extclk_rate_idx = i;

>> +

>>  	/* Subdev, entity and pad */

>>  

>>  	subdev = &sensor->subdev;
Daniel Scally Aug. 10, 2021, 9:46 p.m. UTC | #4
Hi Andy

On 10/08/2021 14:04, Andy Shevchenko wrote:
>> +       ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",

>> +                                      &rate);

>> +       if (!ret) {

> I'm wondering if something like

>

> ... rate = 0;

>

>        fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &rate);

>        if (rate > 0) {

>

> can be used.

Should be fine, and I agree that it makes the point of the check more
clear, thanks.
Sakari Ailus Aug. 10, 2021, 9:49 p.m. UTC | #5
On Tue, Aug 10, 2021 at 10:37:35PM +0100, Daniel Scally wrote:
> Hi Sakari - thanks for all the comments


You're welcome!

Nice patches btw.

> 

> On 10/08/2021 14:34, Sakari Ailus wrote:

> > Hi Daniel,

> >

> > Thanks for the set.

> >

> > On Mon, Aug 09, 2021 at 11:58:37PM +0100, Daniel Scally wrote:

> >> The ov8865 driver as written expects a 24MHz input clock, but the sensor

> >> is sometimes found on x86 platforms with a 19.2MHz input clock supplied.

> >> Add a set of PLL configurations to the driver to support that rate too.

> >> As ACPI doesn't auto-configure the clock rate, check for a clock-frequency

> >> during probe and set that rate if one is found.

> >>

> >> Signed-off-by: Daniel Scally <djrscally@gmail.com>

> >> ---

> >> Changes in v2:

> >>

> >> 	- Added an enum defining the possible frequency rates to index the

> >> 	  array (Andy)

> >>

> >>  drivers/media/i2c/ov8865.c | 164 +++++++++++++++++++++++++++----------

> >>  1 file changed, 121 insertions(+), 43 deletions(-)

> >>

> >> diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c

> >> index fe700787bfb9..1382b16d1a09 100644

> >> --- a/drivers/media/i2c/ov8865.c

> >> +++ b/drivers/media/i2c/ov8865.c

> >> @@ -21,10 +21,6 @@

> >>  #include <media/v4l2-image-sizes.h>

> >>  #include <media/v4l2-mediabus.h>

> >>  

> >> -/* Clock rate */

> >> -

> >> -#define OV8865_EXTCLK_RATE			24000000

> >> -

> >>  /* Register definitions */

> >>  

> >>  /* System */

> >> @@ -567,6 +563,19 @@ struct ov8865_sclk_config {

> >>  	unsigned int sclk_div;

> >>  };

> >>  

> >> +/* Clock rate */

> >> +

> >> +enum extclk_rate {

> >> +	OV8865_19_2_MHZ,

> >> +	OV8865_24_MHZ,

> >> +	OV8865_NUM_SUPPORTED_RATES,

> >> +};

> >> +

> >> +static const unsigned long supported_extclk_rates[] = {

> >> +	[OV8865_19_2_MHZ] = 19200000,

> >> +	[OV8865_24_MHZ] = 24000000,

> >> +};

> >> +

> >>  /*

> >>   * General formulas for (array-centered) mode calculation:

> >>   * - photo_array_width = 3296

> >> @@ -665,6 +674,9 @@ struct ov8865_sensor {

> >>  	struct regulator *avdd;

> >>  	struct regulator *dvdd;

> >>  	struct regulator *dovdd;

> >> +

> >> +	unsigned long extclk_rate;

> >> +	enum extclk_rate extclk_rate_idx;

> >>  	struct clk *extclk;

> >>  

> >>  	struct v4l2_fwnode_endpoint endpoint;

> >> @@ -680,49 +692,83 @@ struct ov8865_sensor {

> >>  /* Static definitions */

> >>  

> >>  /*

> >> - * EXTCLK = 24 MHz

> >>   * PHY_SCLK = 720 MHz

> >>   * MIPI_PCLK = 90 MHz

> >>   */

> >> -static const struct ov8865_pll1_config ov8865_pll1_config_native = {

> >> -	.pll_pre_div_half	= 1,

> >> -	.pll_pre_div		= 0,

> >> -	.pll_mul		= 30,

> >> -	.m_div			= 1,

> >> -	.mipi_div		= 3,

> >> -	.pclk_div		= 1,

> >> -	.sys_pre_div		= 1,

> >> -	.sys_div		= 2,

> >> +

> >> +static const struct ov8865_pll1_config ov8865_pll1_configs_native[] = {

> >> +	{ /* 19.2 MHz input clock */

> >> +		.pll_pre_div_half	= 1,

> >> +		.pll_pre_div		= 2,

> >> +		.pll_mul		= 75,

> >> +		.m_div			= 1,

> >> +		.mipi_div		= 3,

> >> +		.pclk_div		= 1,

> >> +		.sys_pre_div		= 1,

> >> +		.sys_div		= 2,

> >> +	},

> >> +	{ /* 24MHz input clock */

> >> +		.pll_pre_div_half	= 1,

> >> +		.pll_pre_div		= 0,

> >> +		.pll_mul		= 30,

> >> +		.m_div			= 1,

> >> +		.mipi_div		= 3,

> >> +		.pclk_div		= 1,

> >> +		.sys_pre_div		= 1,

> >> +		.sys_div		= 2,

> >> +	},

> > Could you instead add a struct specific to the clock frequency with

> > pointers to these? See e.g. the ov8856 driver how this could otherwise end

> > up...

> 

> 

> You mean something like

> 

> 

> static struct ov8865_pll_configs_19_2_mhz {

> 

>     .pll1_config_native = &ov8865_pll1_config_native,

> 

>     ...

> 

> };

> 

> 

> 

> static struct ov8865_pll_configs_24_mhz {

> 

>     .pll1_config_native = &ov8865_pll1_config_native,

> 

>     ...

> 

> };

> 

> 

> or am I misunderstanding?


Yes, please --- ov8865_pll1_config_native above is thus the PLL
configuration for the 24 MHz clock.

-- 
Sakari Ailus
Daniel Scally Sept. 7, 2021, 10:44 p.m. UTC | #6
Hi Sakari

On 10/08/2021 22:49, Sakari Ailus wrote:
> On Tue, Aug 10, 2021 at 10:37:35PM +0100, Daniel Scally wrote:

>> Hi Sakari - thanks for all the comments

> You're welcome!

>

> Nice patches btw.



Thanks!

>

>> On 10/08/2021 14:34, Sakari Ailus wrote:

>>> Hi Daniel,

>>>

>>> Thanks for the set.

>>>

>>> On Mon, Aug 09, 2021 at 11:58:37PM +0100, Daniel Scally wrote:

>>>> The ov8865 driver as written expects a 24MHz input clock, but the sensor

>>>> is sometimes found on x86 platforms with a 19.2MHz input clock supplied.

>>>> Add a set of PLL configurations to the driver to support that rate too.

>>>> As ACPI doesn't auto-configure the clock rate, check for a clock-frequency

>>>> during probe and set that rate if one is found.

>>>>

>>>> Signed-off-by: Daniel Scally <djrscally@gmail.com>

>>>> ---

>>>> Changes in v2:

>>>>

>>>> 	- Added an enum defining the possible frequency rates to index the

>>>> 	  array (Andy)

>>>>

>>>>  drivers/media/i2c/ov8865.c | 164 +++++++++++++++++++++++++++----------

>>>>  1 file changed, 121 insertions(+), 43 deletions(-)

>>>>

>>>> diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c

>>>> index fe700787bfb9..1382b16d1a09 100644

>>>> --- a/drivers/media/i2c/ov8865.c

>>>> +++ b/drivers/media/i2c/ov8865.c

>>>> @@ -21,10 +21,6 @@

>>>>  #include <media/v4l2-image-sizes.h>

>>>>  #include <media/v4l2-mediabus.h>

>>>>  

>>>> -/* Clock rate */

>>>> -

>>>> -#define OV8865_EXTCLK_RATE			24000000

>>>> -

>>>>  /* Register definitions */

>>>>  

>>>>  /* System */

>>>> @@ -567,6 +563,19 @@ struct ov8865_sclk_config {

>>>>  	unsigned int sclk_div;

>>>>  };

>>>>  

>>>> +/* Clock rate */

>>>> +

>>>> +enum extclk_rate {

>>>> +	OV8865_19_2_MHZ,

>>>> +	OV8865_24_MHZ,

>>>> +	OV8865_NUM_SUPPORTED_RATES,

>>>> +};

>>>> +

>>>> +static const unsigned long supported_extclk_rates[] = {

>>>> +	[OV8865_19_2_MHZ] = 19200000,

>>>> +	[OV8865_24_MHZ] = 24000000,

>>>> +};

>>>> +

>>>>  /*

>>>>   * General formulas for (array-centered) mode calculation:

>>>>   * - photo_array_width = 3296

>>>> @@ -665,6 +674,9 @@ struct ov8865_sensor {

>>>>  	struct regulator *avdd;

>>>>  	struct regulator *dvdd;

>>>>  	struct regulator *dovdd;

>>>> +

>>>> +	unsigned long extclk_rate;

>>>> +	enum extclk_rate extclk_rate_idx;

>>>>  	struct clk *extclk;

>>>>  

>>>>  	struct v4l2_fwnode_endpoint endpoint;

>>>> @@ -680,49 +692,83 @@ struct ov8865_sensor {

>>>>  /* Static definitions */

>>>>  

>>>>  /*

>>>> - * EXTCLK = 24 MHz

>>>>   * PHY_SCLK = 720 MHz

>>>>   * MIPI_PCLK = 90 MHz

>>>>   */

>>>> -static const struct ov8865_pll1_config ov8865_pll1_config_native = {

>>>> -	.pll_pre_div_half	= 1,

>>>> -	.pll_pre_div		= 0,

>>>> -	.pll_mul		= 30,

>>>> -	.m_div			= 1,

>>>> -	.mipi_div		= 3,

>>>> -	.pclk_div		= 1,

>>>> -	.sys_pre_div		= 1,

>>>> -	.sys_div		= 2,

>>>> +

>>>> +static const struct ov8865_pll1_config ov8865_pll1_configs_native[] = {

>>>> +	{ /* 19.2 MHz input clock */

>>>> +		.pll_pre_div_half	= 1,

>>>> +		.pll_pre_div		= 2,

>>>> +		.pll_mul		= 75,

>>>> +		.m_div			= 1,

>>>> +		.mipi_div		= 3,

>>>> +		.pclk_div		= 1,

>>>> +		.sys_pre_div		= 1,

>>>> +		.sys_div		= 2,

>>>> +	},

>>>> +	{ /* 24MHz input clock */

>>>> +		.pll_pre_div_half	= 1,

>>>> +		.pll_pre_div		= 0,

>>>> +		.pll_mul		= 30,

>>>> +		.m_div			= 1,

>>>> +		.mipi_div		= 3,

>>>> +		.pclk_div		= 1,

>>>> +		.sys_pre_div		= 1,

>>>> +		.sys_div		= 2,

>>>> +	},

>>> Could you instead add a struct specific to the clock frequency with

>>> pointers to these? See e.g. the ov8856 driver how this could otherwise end

>>> up...I thin

>>

>> You mean something like

>>

>>

>> static struct ov8865_pll_configs_19_2_mhz {

>>

>>     .pll1_config_native = &ov8865_pll1_config_native,

>>

>>     ...

>>

>> };

>>

>>

>>

>> static struct ov8865_pll_configs_24_mhz {

>>

>>     .pll1_config_native = &ov8865_pll1_config_native,

>>

>>     ...

>>

>> };

>>

>>

>> or am I misunderstanding?

> Yes, please --- ov8865_pll1_config_native above is thus the PLL

> configuration for the 24 MHz clock.


I'm not sure about this actually. There's two versions of
ov8865_pll2_config, native and binning, so it becomes something like this:


struct ov8865_pll_configs {
    struct ov8865_pll1_config *pll1_config;
    struct ov8865_pll2_config *pll2_config_native;
    struct ov8865_pll2_config *pll2_config_binning;
};

static struct ov8865_pll_configs ov8865_pll_configs_19_2mhz = {
    .pll1_config = &ov8865_pll1_config_native_19_2mhz,
    .pll2_config_native = &ov8865_pll2_config_native_19_2mhz,
    .pll2_config_binning = &ov8865_pll2_config_binning_19_2mhz,
};

static struct ov8865_pll_configs ov8865_pll_configs_24mhz = {
    .pll1_config = &ov8865_pll1_config_native_24mhz,
    .pll2_config_native = &ov8865_pll2_config_native_24mhz,
    .pll2_config_binning = &ov8865_pll2_config_binning_24mhz,
};


Now because a mode might use either the native or binning version of the
pll2 configs, currently they're actually against the struct for a
particular mode like so:


struct ov8865_mode ov8865_modes[] = {

    {

        <snip>

        .pll1_config            = &ov8865_pll1_config_native,
        .pll2_config            = &ov8865_pll2_config_binning,
        .sclk_config            = &ov8865_sclk_config_native,

    }

};


The problem I'm having is that I can't really see a clean way to store
against the _mode_ whether it should access .pll2_config_native or
.pll2_config_binning, from the new struct ov8865_pll_configs. Do you
have any ideas about a way to do that?
Sakari Ailus Sept. 8, 2021, 6:52 a.m. UTC | #7
Hi Daniel,

On Tue, Sep 07, 2021 at 11:44:12PM +0100, Daniel Scally wrote:
> Hi Sakari

> 

> On 10/08/2021 22:49, Sakari Ailus wrote:

> > On Tue, Aug 10, 2021 at 10:37:35PM +0100, Daniel Scally wrote:

> >> Hi Sakari - thanks for all the comments

> > You're welcome!

> >

> > Nice patches btw.

> 

> 

> Thanks!

> 

> >

> >> On 10/08/2021 14:34, Sakari Ailus wrote:

> >>> Hi Daniel,

> >>>

> >>> Thanks for the set.

> >>>

> >>> On Mon, Aug 09, 2021 at 11:58:37PM +0100, Daniel Scally wrote:

> >>>> The ov8865 driver as written expects a 24MHz input clock, but the sensor

> >>>> is sometimes found on x86 platforms with a 19.2MHz input clock supplied.

> >>>> Add a set of PLL configurations to the driver to support that rate too.

> >>>> As ACPI doesn't auto-configure the clock rate, check for a clock-frequency

> >>>> during probe and set that rate if one is found.

> >>>>

> >>>> Signed-off-by: Daniel Scally <djrscally@gmail.com>

> >>>> ---

> >>>> Changes in v2:

> >>>>

> >>>> 	- Added an enum defining the possible frequency rates to index the

> >>>> 	  array (Andy)

> >>>>

> >>>>  drivers/media/i2c/ov8865.c | 164 +++++++++++++++++++++++++++----------

> >>>>  1 file changed, 121 insertions(+), 43 deletions(-)

> >>>>

> >>>> diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c

> >>>> index fe700787bfb9..1382b16d1a09 100644

> >>>> --- a/drivers/media/i2c/ov8865.c

> >>>> +++ b/drivers/media/i2c/ov8865.c

> >>>> @@ -21,10 +21,6 @@

> >>>>  #include <media/v4l2-image-sizes.h>

> >>>>  #include <media/v4l2-mediabus.h>

> >>>>  

> >>>> -/* Clock rate */

> >>>> -

> >>>> -#define OV8865_EXTCLK_RATE			24000000

> >>>> -

> >>>>  /* Register definitions */

> >>>>  

> >>>>  /* System */

> >>>> @@ -567,6 +563,19 @@ struct ov8865_sclk_config {

> >>>>  	unsigned int sclk_div;

> >>>>  };

> >>>>  

> >>>> +/* Clock rate */

> >>>> +

> >>>> +enum extclk_rate {

> >>>> +	OV8865_19_2_MHZ,

> >>>> +	OV8865_24_MHZ,

> >>>> +	OV8865_NUM_SUPPORTED_RATES,

> >>>> +};

> >>>> +

> >>>> +static const unsigned long supported_extclk_rates[] = {

> >>>> +	[OV8865_19_2_MHZ] = 19200000,

> >>>> +	[OV8865_24_MHZ] = 24000000,

> >>>> +};

> >>>> +

> >>>>  /*

> >>>>   * General formulas for (array-centered) mode calculation:

> >>>>   * - photo_array_width = 3296

> >>>> @@ -665,6 +674,9 @@ struct ov8865_sensor {

> >>>>  	struct regulator *avdd;

> >>>>  	struct regulator *dvdd;

> >>>>  	struct regulator *dovdd;

> >>>> +

> >>>> +	unsigned long extclk_rate;

> >>>> +	enum extclk_rate extclk_rate_idx;

> >>>>  	struct clk *extclk;

> >>>>  

> >>>>  	struct v4l2_fwnode_endpoint endpoint;

> >>>> @@ -680,49 +692,83 @@ struct ov8865_sensor {

> >>>>  /* Static definitions */

> >>>>  

> >>>>  /*

> >>>> - * EXTCLK = 24 MHz

> >>>>   * PHY_SCLK = 720 MHz

> >>>>   * MIPI_PCLK = 90 MHz

> >>>>   */

> >>>> -static const struct ov8865_pll1_config ov8865_pll1_config_native = {

> >>>> -	.pll_pre_div_half	= 1,

> >>>> -	.pll_pre_div		= 0,

> >>>> -	.pll_mul		= 30,

> >>>> -	.m_div			= 1,

> >>>> -	.mipi_div		= 3,

> >>>> -	.pclk_div		= 1,

> >>>> -	.sys_pre_div		= 1,

> >>>> -	.sys_div		= 2,

> >>>> +

> >>>> +static const struct ov8865_pll1_config ov8865_pll1_configs_native[] = {

> >>>> +	{ /* 19.2 MHz input clock */

> >>>> +		.pll_pre_div_half	= 1,

> >>>> +		.pll_pre_div		= 2,

> >>>> +		.pll_mul		= 75,

> >>>> +		.m_div			= 1,

> >>>> +		.mipi_div		= 3,

> >>>> +		.pclk_div		= 1,

> >>>> +		.sys_pre_div		= 1,

> >>>> +		.sys_div		= 2,

> >>>> +	},

> >>>> +	{ /* 24MHz input clock */

> >>>> +		.pll_pre_div_half	= 1,

> >>>> +		.pll_pre_div		= 0,

> >>>> +		.pll_mul		= 30,

> >>>> +		.m_div			= 1,

> >>>> +		.mipi_div		= 3,

> >>>> +		.pclk_div		= 1,

> >>>> +		.sys_pre_div		= 1,

> >>>> +		.sys_div		= 2,

> >>>> +	},

> >>> Could you instead add a struct specific to the clock frequency with

> >>> pointers to these? See e.g. the ov8856 driver how this could otherwise end

> >>> up...I thin

> >>

> >> You mean something like

> >>

> >>

> >> static struct ov8865_pll_configs_19_2_mhz {

> >>

> >>     .pll1_config_native = &ov8865_pll1_config_native,

> >>

> >>     ...

> >>

> >> };

> >>

> >>

> >>

> >> static struct ov8865_pll_configs_24_mhz {

> >>

> >>     .pll1_config_native = &ov8865_pll1_config_native,

> >>

> >>     ...

> >>

> >> };

> >>

> >>

> >> or am I misunderstanding?

> > Yes, please --- ov8865_pll1_config_native above is thus the PLL

> > configuration for the 24 MHz clock.

> 

> I'm not sure about this actually. There's two versions of

> ov8865_pll2_config, native and binning, so it becomes something like this:

> 

> 

> struct ov8865_pll_configs {

>     struct ov8865_pll1_config *pll1_config;

>     struct ov8865_pll2_config *pll2_config_native;

>     struct ov8865_pll2_config *pll2_config_binning;

> };

> 

> static struct ov8865_pll_configs ov8865_pll_configs_19_2mhz = {

>     .pll1_config = &ov8865_pll1_config_native_19_2mhz,

>     .pll2_config_native = &ov8865_pll2_config_native_19_2mhz,

>     .pll2_config_binning = &ov8865_pll2_config_binning_19_2mhz,

> };

> 

> static struct ov8865_pll_configs ov8865_pll_configs_24mhz = {

>     .pll1_config = &ov8865_pll1_config_native_24mhz,

>     .pll2_config_native = &ov8865_pll2_config_native_24mhz,

>     .pll2_config_binning = &ov8865_pll2_config_binning_24mhz,

> };

> 

> 

> Now because a mode might use either the native or binning version of the

> pll2 configs, currently they're actually against the struct for a

> particular mode like so:

> 

> 

> struct ov8865_mode ov8865_modes[] = {

> 

>     {

> 

>         <snip>

> 

>         .pll1_config            = &ov8865_pll1_config_native,

>         .pll2_config            = &ov8865_pll2_config_binning,

>         .sclk_config            = &ov8865_sclk_config_native,

> 

>     }

> 

> };

> 

> 

> The problem I'm having is that I can't really see a clean way to store

> against the _mode_ whether it should access .pll2_config_native or

> .pll2_config_binning, from the new struct ov8865_pll_configs. Do you

> have any ideas about a way to do that?


Ah, yes. I agree, that's where you'll need some code to pick the right one.
So you could use a function pointer in the struct and give it the necessary
arguments. I don't think it'd be overkill, things tend to develop over
time. See e.g. the ov8856 driver as a (warning) example.

-- 
Kind regards,

Sakari Ailus
diff mbox series

Patch

diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index fe700787bfb9..1382b16d1a09 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -21,10 +21,6 @@ 
 #include <media/v4l2-image-sizes.h>
 #include <media/v4l2-mediabus.h>
 
-/* Clock rate */
-
-#define OV8865_EXTCLK_RATE			24000000
-
 /* Register definitions */
 
 /* System */
@@ -567,6 +563,19 @@  struct ov8865_sclk_config {
 	unsigned int sclk_div;
 };
 
+/* Clock rate */
+
+enum extclk_rate {
+	OV8865_19_2_MHZ,
+	OV8865_24_MHZ,
+	OV8865_NUM_SUPPORTED_RATES,
+};
+
+static const unsigned long supported_extclk_rates[] = {
+	[OV8865_19_2_MHZ] = 19200000,
+	[OV8865_24_MHZ] = 24000000,
+};
+
 /*
  * General formulas for (array-centered) mode calculation:
  * - photo_array_width = 3296
@@ -665,6 +674,9 @@  struct ov8865_sensor {
 	struct regulator *avdd;
 	struct regulator *dvdd;
 	struct regulator *dovdd;
+
+	unsigned long extclk_rate;
+	enum extclk_rate extclk_rate_idx;
 	struct clk *extclk;
 
 	struct v4l2_fwnode_endpoint endpoint;
@@ -680,49 +692,83 @@  struct ov8865_sensor {
 /* Static definitions */
 
 /*
- * EXTCLK = 24 MHz
  * PHY_SCLK = 720 MHz
  * MIPI_PCLK = 90 MHz
  */
-static const struct ov8865_pll1_config ov8865_pll1_config_native = {
-	.pll_pre_div_half	= 1,
-	.pll_pre_div		= 0,
-	.pll_mul		= 30,
-	.m_div			= 1,
-	.mipi_div		= 3,
-	.pclk_div		= 1,
-	.sys_pre_div		= 1,
-	.sys_div		= 2,
+
+static const struct ov8865_pll1_config ov8865_pll1_configs_native[] = {
+	{ /* 19.2 MHz input clock */
+		.pll_pre_div_half	= 1,
+		.pll_pre_div		= 2,
+		.pll_mul		= 75,
+		.m_div			= 1,
+		.mipi_div		= 3,
+		.pclk_div		= 1,
+		.sys_pre_div		= 1,
+		.sys_div		= 2,
+	},
+	{ /* 24MHz input clock */
+		.pll_pre_div_half	= 1,
+		.pll_pre_div		= 0,
+		.pll_mul		= 30,
+		.m_div			= 1,
+		.mipi_div		= 3,
+		.pclk_div		= 1,
+		.sys_pre_div		= 1,
+		.sys_div		= 2,
+	},
 };
 
 /*
- * EXTCLK = 24 MHz
  * DAC_CLK = 360 MHz
  * SCLK = 144 MHz
  */
 
-static const struct ov8865_pll2_config ov8865_pll2_config_native = {
-	.pll_pre_div_half	= 1,
-	.pll_pre_div		= 0,
-	.pll_mul		= 30,
-	.dac_div		= 2,
-	.sys_pre_div		= 5,
-	.sys_div		= 0,
+static const struct ov8865_pll2_config ov8865_pll2_configs_native[] = {
+	/* 19.2MHz input clock */
+	{
+		.pll_pre_div_half	= 1,
+		.pll_pre_div		= 5,
+		.pll_mul		= 75,
+		.dac_div		= 1,
+		.sys_pre_div		= 1,
+		.sys_div		= 3,
+	},
+	/* 24MHz input clock */
+	{
+		.pll_pre_div_half	= 1,
+		.pll_pre_div		= 0,
+		.pll_mul		= 30,
+		.dac_div		= 2,
+		.sys_pre_div		= 5,
+		.sys_div		= 0,
+	}
 };
 
 /*
- * EXTCLK = 24 MHz
  * DAC_CLK = 360 MHz
  * SCLK = 72 MHz
  */
 
-static const struct ov8865_pll2_config ov8865_pll2_config_binning = {
+static const struct ov8865_pll2_config ov8865_pll2_configs_binning[] = {
+	/* 19.2MHz input clock */
+	{
+	.pll_pre_div_half	= 1,
+	.pll_pre_div		= 2,
+	.pll_mul		= 75,
+	.dac_div		= 2,
+	.sys_pre_div		= 10,
+	.sys_div		= 0,
+	},
+	/* 24MHz input clock */
+	{
 	.pll_pre_div_half	= 1,
 	.pll_pre_div		= 0,
 	.pll_mul		= 30,
 	.dac_div		= 2,
 	.sys_pre_div		= 10,
 	.sys_div		= 0,
+	}
 };
 
 static const struct ov8865_sclk_config ov8865_sclk_config_native = {
@@ -934,8 +980,8 @@  static const struct ov8865_mode ov8865_modes[] = {
 		.frame_interval			= { 1, 30 },
 
 		/* PLL */
-		.pll1_config			= &ov8865_pll1_config_native,
-		.pll2_config			= &ov8865_pll2_config_native,
+		.pll1_config			= ov8865_pll1_configs_native,
+		.pll2_config			= ov8865_pll2_configs_native,
 		.sclk_config			= &ov8865_sclk_config_native,
 
 		/* Registers */
@@ -990,8 +1036,8 @@  static const struct ov8865_mode ov8865_modes[] = {
 		.frame_interval			= { 1, 30 },
 
 		/* PLL */
-		.pll1_config			= &ov8865_pll1_config_native,
-		.pll2_config			= &ov8865_pll2_config_native,
+		.pll1_config			= ov8865_pll1_configs_native,
+		.pll2_config			= ov8865_pll2_configs_native,
 		.sclk_config			= &ov8865_sclk_config_native,
 
 		/* Registers */
@@ -1050,8 +1096,8 @@  static const struct ov8865_mode ov8865_modes[] = {
 		.frame_interval			= { 1, 30 },
 
 		/* PLL */
-		.pll1_config			= &ov8865_pll1_config_native,
-		.pll2_config			= &ov8865_pll2_config_binning,
+		.pll1_config			= ov8865_pll1_configs_native,
+		.pll2_config			= ov8865_pll2_configs_binning,
 		.sclk_config			= &ov8865_sclk_config_native,
 
 		/* Registers */
@@ -1116,8 +1162,8 @@  static const struct ov8865_mode ov8865_modes[] = {
 		.frame_interval			= { 1, 90 },
 
 		/* PLL */
-		.pll1_config			= &ov8865_pll1_config_native,
-		.pll2_config			= &ov8865_pll2_config_binning,
+		.pll1_config			= ov8865_pll1_configs_native,
+		.pll2_config			= ov8865_pll2_configs_binning,
 		.sclk_config			= &ov8865_sclk_config_native,
 
 		/* Registers */
@@ -1513,12 +1559,11 @@  static int ov8865_isp_configure(struct ov8865_sensor *sensor)
 static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor,
 					   const struct ov8865_mode *mode)
 {
-	const struct ov8865_pll1_config *config = mode->pll1_config;
-	unsigned long extclk_rate;
+	const struct ov8865_pll1_config *config;
 	unsigned long pll1_rate;
 
-	extclk_rate = clk_get_rate(sensor->extclk);
-	pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half;
+	config = &mode->pll1_config[sensor->extclk_rate_idx];
+	pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half;
 
 	switch (config->pll_pre_div) {
 	case 0:
@@ -1552,10 +1597,12 @@  static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,
 				      const struct ov8865_mode *mode,
 				      u32 mbus_code)
 {
-	const struct ov8865_pll1_config *config = mode->pll1_config;
+	const struct ov8865_pll1_config *config;
 	u8 value;
 	int ret;
 
+	config = &mode->pll1_config[sensor->extclk_rate_idx];
+
 	switch (mbus_code) {
 	case MEDIA_BUS_FMT_SBGGR10_1X10:
 		value = OV8865_MIPI_BIT_SEL(10);
@@ -1622,9 +1669,11 @@  static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,
 static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor,
 				      const struct ov8865_mode *mode)
 {
-	const struct ov8865_pll2_config *config = mode->pll2_config;
+	const struct ov8865_pll2_config *config;
 	int ret;
 
+	config = &mode->pll2_config[sensor->extclk_rate_idx];
+
 	ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG,
 			   OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) |
 			   OV8865_PLL_CTRL12_DAC_DIV(config->dac_div));
@@ -2053,9 +2102,11 @@  static int ov8865_mode_configure(struct ov8865_sensor *sensor,
 static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor,
 					       const struct ov8865_mode *mode)
 {
-	const struct ov8865_pll1_config *config = mode->pll1_config;
+	const struct ov8865_pll1_config *config;
 	unsigned long pll1_rate;
 
+	config = &mode->pll1_config[sensor->extclk_rate_idx];
+
 	pll1_rate = ov8865_mode_pll1_rate(sensor, mode);
 
 	return pll1_rate / config->m_div / 2;
@@ -2783,7 +2834,8 @@  static int ov8865_probe(struct i2c_client *client)
 	struct ov8865_sensor *sensor;
 	struct v4l2_subdev *subdev;
 	struct media_pad *pad;
-	unsigned long rate;
+	unsigned int rate;
+	unsigned int i;
 	int ret;
 
 	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
@@ -2858,13 +2910,39 @@  static int ov8865_probe(struct i2c_client *client)
 		goto error_endpoint;
 	}
 
-	rate = clk_get_rate(sensor->extclk);
-	if (rate != OV8865_EXTCLK_RATE) {
-		dev_err(dev, "clock rate %lu Hz is unsupported\n", rate);
+	/*
+	 * We could have either a 24MHz or 19.2MHz clock rate. Check for a
+	 * clock-frequency property and if found, set that rate. This should
+	 * cover ACPI case. If the system uses devicetree then the configured
+	 * rate should already be set, so we'll have to check it.
+	 */
+
+	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
+				       &rate);
+	if (!ret) {
+		ret = clk_set_rate(sensor->extclk, rate);
+		if (ret) {
+			dev_err(dev, "failed to set clock rate\n");
+			return ret;
+		}
+	}
+
+	sensor->extclk_rate = clk_get_rate(sensor->extclk);
+
+	for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) {
+		if (sensor->extclk_rate == supported_extclk_rates[i])
+			break;
+	}
+
+	if (i == ARRAY_SIZE(supported_extclk_rates)) {
+		dev_err(dev, "clock rate %lu Hz is unsupported\n",
+			sensor->extclk_rate);
 		ret = -EINVAL;
 		goto error_endpoint;
 	}
 
+	sensor->extclk_rate_idx = i;
+
 	/* Subdev, entity and pad */
 
 	subdev = &sensor->subdev;