mbox series

[v6,v6,0/3] media: ov8856: Add devicetree support

Message ID 20200429162437.2025699-1-robert.foss@linaro.org
Headers show
Series media: ov8856: Add devicetree support | expand

Message

Robert Foss April 29, 2020, 4:24 p.m. UTC
This adds devicetree support to the ov8856 driver.
In order to to aid debugging and enable future sensor
modes to be supported, module revision detection is also added.


Dongchun Zhu (1):
  media: dt-bindings: ov8856: Document YAML bindings

Robert Foss (2):
  media: ov8856: Add devicetree support
  media: ov8856: Implement sensor module revision identification

 .../devicetree/bindings/media/i2c/ov8856.yaml | 140 +++++++++++++
 MAINTAINERS                                   |   1 +
 drivers/media/i2c/ov8856.c                    | 190 ++++++++++++++++--
 3 files changed, 317 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov8856.yaml

Comments

Marco Felsch April 30, 2020, 9:35 a.m. UTC | #1
Hi Robert,

thnakf for the patch but pls keep in mind to do one thing per patch.
IMHO this patch do a lot more. Anyway below are my comment :)

On 20-04-29 18:24, Robert Foss wrote:
> Add match table, enable ov8856_probe() to support
> both ACPI and DT modes.

You are also adding the support for reset-gpios and regualtors. IMHO the
commit message don't belong to the changes you made anymore.

> ACPI and DT modes are primarily distinguished from
> each other by relying on devm_XXX_get_optional()
> will return NULL instead of a reference for the
> desired managed resource.
> 
> Signed-off-by: Robert Foss <robert.foss@linaro.org>
> ---
> 
> - Changes since v5:
>   * Maxime & Sakari: Replaced clock tolerance check with warning
> 
> - Changes since v4:
>   * Maxime & Sakari: Switch to clock-frequency
> 
> - Changes since v3:
>   * Remove redundant {}-brackets
>   * Compare xvclk_rate to 5% tolerance
>   * Andy: Use dev_fwnode()
>   * Andy: Use %pe instead of %ld + PTR_ERR()
>   * Andy: Invert reset_gpio logic
>   * Andy: Remove dev_dbg() from failing reset_gpio setup
>   * Andy: Use dev_err for logging for failures
>   * Andy: Remove dev_warn from EDEFER/regulator error path
>   * Andy & Sakari: Replaced GPIOD_OUT_XXX with 0/1
>   * Maxime & Sakari: Verify clock frequency from DT
>   * Sakari: Verify the 'xvclk_rate' is set correctly for ACPI/DT devices
>   * Sakari: Remove duplicate ov8856->dev assignment
> 
> - Changes since v2:
>   * Added "struct device *dev" member to struct ov8856
>   * Andy: Switch to optional version of devm_gpiod_get
>   * Andy: Switch to optional version of devm_clk_get
>   * Fabio: Add reset sleep period
>   * Sakari: Unify defines for 19.2Mhz
>   * Sakari: Remove 24Mhz clock, since it isn't needed for supported modes
>   * Sakari: Replace dev_info() with dev_dbg()
>   * Sakari: Switch induction variable type to unsigned
>   * Sakari: Don't wait for reset_gpio when in ACPI mode
>   * Sakari: Pull reset GPIO high on power on failure
>   * Sakari: Add power on/off to resume/suspend
>   * Sakari: Fix indentation
>   * Sakari: Power off during ov8856_remove()
>   * Sakari: Don't sleep during power-on in ACPI mode
>   * Sakari: Switch to getting xvclk from clk_get_rate
> 
> - Changes since v1:
>   * Andy & Sakari: Make XVCLK optional since to not break ACPI
>   * Fabio: Change n_shutdown_gpio name to reset_gpio
>   * Fabio: Invert reset_gpio due to GPIO_ACTIVE_HIGH -> GPIO_ACTIVE_LOW change
>   * Fabio: Remove empty line
>   * Fabio: Remove real error from devm_gpiod_get() failures
>   * Sakari: ARRAY_SIZE() directly instead of through OV8856_NUM_SUPPLIES
>   * Sakari: Use XVCLK rate as provided by DT
> 
>  drivers/media/i2c/ov8856.c | 137 +++++++++++++++++++++++++++++++++----
>  1 file changed, 123 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
> index 8655842af275..4749dc74d5ad 100644
> --- a/drivers/media/i2c/ov8856.c
> +++ b/drivers/media/i2c/ov8856.c
> @@ -3,10 +3,13 @@
>  
>  #include <asm/unaligned.h>
>  #include <linux/acpi.h>
> +#include <linux/clk.h>
>  #include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-device.h>
>  #include <media/v4l2-fwnode.h>
> @@ -18,7 +21,7 @@
>  #define OV8856_LINK_FREQ_360MHZ		360000000ULL
>  #define OV8856_LINK_FREQ_180MHZ		180000000ULL
>  #define OV8856_SCLK			144000000ULL
> -#define OV8856_MCLK			19200000
> +#define OV8856_XVCLK_19_2		19200000
>  #define OV8856_DATA_LANES		4
>  #define OV8856_RGB_DEPTH		10
>  
> @@ -64,6 +67,12 @@
>  
>  #define to_ov8856(_sd)			container_of(_sd, struct ov8856, sd)
>  
> +static const char * const ov8856_supply_names[] = {
> +	"dovdd",	/* Digital I/O power */
> +	"avdd",		/* Analog power */
> +	"dvdd",		/* Digital core power */
> +};
> +
>  enum {
>  	OV8856_LINK_FREQ_720MBPS,
>  	OV8856_LINK_FREQ_360MBPS,
> @@ -566,6 +575,11 @@ struct ov8856 {
>  	struct media_pad pad;
>  	struct v4l2_ctrl_handler ctrl_handler;
>  
> +	struct device		*dev;
> +	struct clk		*xvclk;
> +	struct gpio_desc	*reset_gpio;
> +	struct regulator_bulk_data supplies[ARRAY_SIZE(ov8856_supply_names)];
> +
>  	/* V4L2 Controls */
>  	struct v4l2_ctrl *link_freq;
>  	struct v4l2_ctrl *pixel_rate;
> @@ -908,6 +922,52 @@ static int ov8856_set_stream(struct v4l2_subdev *sd, int enable)
>  	return ret;
>  }
>  
> +static int __ov8856_power_on(struct ov8856 *ov8856)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
> +	int ret;
> +
> +	ret = clk_prepare_enable(ov8856->xvclk);

You're request the clk only in DT case or do I miss something? If so you
have to check if the clk is available.

> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to enable xvclk\n");
> +		return ret;
> +	}
> +
> +	if (is_acpi_node(dev_fwnode(ov8856->dev)))
> +		return 0;
> +
> +	if (ov8856->reset_gpio) {
> +		gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> +		usleep_range(1000, 2000);
> +	}
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(ov8856_supply_names),
> +				    ov8856->supplies);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to enable regulators\n");
> +		goto disable_clk;
> +	}
> +
> +	gpiod_set_value_cansleep(ov8856->reset_gpio, 0);

You need to check the existance of the gpio here too.

> +	usleep_range(1500, 1800);
> +
> +	return 0;
> +
> +disable_clk:
> +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);

And here.. pls check the whole patch.

> +	clk_disable_unprepare(ov8856->xvclk);
> +
> +	return ret;
> +}
> +
> +static void __ov8856_power_off(struct ov8856 *ov8856)
> +{
> +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> +	regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names),
> +			       ov8856->supplies);
> +	clk_disable_unprepare(ov8856->xvclk);

Clk is only availabel in DT use-case.

> +}
> +
>  static int __maybe_unused ov8856_suspend(struct device *dev)
>  {
>  	struct i2c_client *client = to_i2c_client(dev);
> @@ -918,6 +978,7 @@ static int __maybe_unused ov8856_suspend(struct device *dev)
>  	if (ov8856->streaming)
>  		ov8856_stop_streaming(ov8856);
>  
> +	__ov8856_power_off(ov8856);
>  	mutex_unlock(&ov8856->mutex);
>  
>  	return 0;
> @@ -931,6 +992,8 @@ static int __maybe_unused ov8856_resume(struct device *dev)
>  	int ret;
>  
>  	mutex_lock(&ov8856->mutex);
> +
> +	__ov8856_power_on(ov8856);
>  	if (ov8856->streaming) {
>  		ret = ov8856_start_streaming(ov8856);
>  		if (ret) {
> @@ -1092,29 +1155,54 @@ static int ov8856_identify_module(struct ov8856 *ov8856)
>  	return 0;
>  }
>  
> -static int ov8856_check_hwcfg(struct device *dev)
> +static int ov8856_get_hwcfg(struct ov8856 *ov8856)
>  {
> +	struct device *dev = ov8856->dev;
>  	struct fwnode_handle *ep;
>  	struct fwnode_handle *fwnode = dev_fwnode(dev);
>  	struct v4l2_fwnode_endpoint bus_cfg = {
>  		.bus_type = V4L2_MBUS_CSI2_DPHY
>  	};
> -	u32 mclk;
> +	u32 xvclk_rate;
>  	int ret;
>  	unsigned int i, j;
>  
>  	if (!fwnode)
>  		return -ENXIO;
>  
> -	ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
> +	ret = fwnode_property_read_u32(fwnode, "clock-frequency",
> +		&xvclk_rate);
>  	if (ret)
>  		return ret;
>  
> -	if (mclk != OV8856_MCLK) {
> -		dev_err(dev, "external clock %d is not supported", mclk);
> -		return -EINVAL;
> +	if (!is_acpi_node(fwnode)) {
> +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> +		if (IS_ERR(ov8856->xvclk)) {
> +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> +					ov8856->xvclk);
> +			return PTR_ERR(ov8856->xvclk);
> +		}
> +
> +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> +		xvclk_rate = clk_get_rate(ov8856->xvclk);
>  	}

Why do we handle the clock only in DT case? Is there a problem with the
clock handling and ACPI?

> +	if (xvclk_rate != OV8856_XVCLK_19_2)
> +		dev_warn(dev, "external clock rate %d is unsupported", xvclk_rate);
> +
> +	ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset",
> +		GPIOD_OUT_LOW);

Pls align this.

Regards,
  Marco

> +	if (IS_ERR(ov8856->reset_gpio))
> +		return PTR_ERR(ov8856->reset_gpio);
> +
> +	for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++)
> +		ov8856->supplies[i].supply = ov8856_supply_names[i];
> +
> +	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov8856_supply_names),
> +				      ov8856->supplies);
> +	if (ret)
> +		return ret;
> +
>  	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
>  	if (!ep)
>  		return -ENXIO;
> @@ -1169,6 +1257,8 @@ static int ov8856_remove(struct i2c_client *client)
>  	pm_runtime_disable(&client->dev);
>  	mutex_destroy(&ov8856->mutex);
>  
> +	__ov8856_power_off(ov8856);
> +
>  	return 0;
>  }
>  
> @@ -1177,22 +1267,31 @@ static int ov8856_probe(struct i2c_client *client)
>  	struct ov8856 *ov8856;
>  	int ret;
>  
> -	ret = ov8856_check_hwcfg(&client->dev);
> +	ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL);
> +	if (!ov8856)
> +		return -ENOMEM;
> +
> +	ov8856->dev = &client->dev;
> +
> +	ret = ov8856_get_hwcfg(ov8856);
>  	if (ret) {
> -		dev_err(&client->dev, "failed to check HW configuration: %d",
> +		dev_err(&client->dev, "failed to get HW configuration: %d",
>  			ret);
>  		return ret;
>  	}
>  
> -	ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL);
> -	if (!ov8856)
> -		return -ENOMEM;
> -
>  	v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops);
> +
> +	ret = __ov8856_power_on(ov8856);
> +	if (ret) {
> +		dev_err(&client->dev, "failed to power on\n");
> +		return ret;
> +	}
> +
>  	ret = ov8856_identify_module(ov8856);
>  	if (ret) {
>  		dev_err(&client->dev, "failed to find sensor: %d", ret);
> -		return ret;
> +		goto probe_power_off;
>  	}
>  
>  	mutex_init(&ov8856->mutex);
> @@ -1238,6 +1337,9 @@ static int ov8856_probe(struct i2c_client *client)
>  	v4l2_ctrl_handler_free(ov8856->sd.ctrl_handler);
>  	mutex_destroy(&ov8856->mutex);
>  
> +probe_power_off:
> +	__ov8856_power_off(ov8856);
> +
>  	return ret;
>  }
>  
> @@ -1254,11 +1356,18 @@ static const struct acpi_device_id ov8856_acpi_ids[] = {
>  MODULE_DEVICE_TABLE(acpi, ov8856_acpi_ids);
>  #endif
>  
> +static const struct of_device_id ov8856_of_match[] = {
> +	{ .compatible = "ovti,ov8856" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, ov8856_of_match);
> +
>  static struct i2c_driver ov8856_i2c_driver = {
>  	.driver = {
>  		.name = "ov8856",
>  		.pm = &ov8856_pm_ops,
>  		.acpi_match_table = ACPI_PTR(ov8856_acpi_ids),
> +		.of_match_table = ov8856_of_match,
>  	},
>  	.probe_new = ov8856_probe,
>  	.remove = ov8856_remove,
> -- 
> 2.25.1
>
Sakari Ailus April 30, 2020, 9:45 a.m. UTC | #2
Hi Marco,

On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> Hi Robert,
> 
> thnakf for the patch but pls keep in mind to do one thing per patch.
> IMHO this patch do a lot more. Anyway below are my comment :)
> 
> On 20-04-29 18:24, Robert Foss wrote:
> > Add match table, enable ov8856_probe() to support
> > both ACPI and DT modes.
> 
> You are also adding the support for reset-gpios and regualtors. IMHO the
> commit message don't belong to the changes you made anymore.
> 
> > ACPI and DT modes are primarily distinguished from
> > each other by relying on devm_XXX_get_optional()
> > will return NULL instead of a reference for the
> > desired managed resource.
> > 
> > Signed-off-by: Robert Foss <robert.foss@linaro.org>
> > ---
> > 
> > - Changes since v5:
> >   * Maxime & Sakari: Replaced clock tolerance check with warning
> > 
> > - Changes since v4:
> >   * Maxime & Sakari: Switch to clock-frequency
> > 
> > - Changes since v3:
> >   * Remove redundant {}-brackets
> >   * Compare xvclk_rate to 5% tolerance
> >   * Andy: Use dev_fwnode()
> >   * Andy: Use %pe instead of %ld + PTR_ERR()
> >   * Andy: Invert reset_gpio logic
> >   * Andy: Remove dev_dbg() from failing reset_gpio setup
> >   * Andy: Use dev_err for logging for failures
> >   * Andy: Remove dev_warn from EDEFER/regulator error path
> >   * Andy & Sakari: Replaced GPIOD_OUT_XXX with 0/1
> >   * Maxime & Sakari: Verify clock frequency from DT
> >   * Sakari: Verify the 'xvclk_rate' is set correctly for ACPI/DT devices
> >   * Sakari: Remove duplicate ov8856->dev assignment
> > 
> > - Changes since v2:
> >   * Added "struct device *dev" member to struct ov8856
> >   * Andy: Switch to optional version of devm_gpiod_get
> >   * Andy: Switch to optional version of devm_clk_get
> >   * Fabio: Add reset sleep period
> >   * Sakari: Unify defines for 19.2Mhz
> >   * Sakari: Remove 24Mhz clock, since it isn't needed for supported modes
> >   * Sakari: Replace dev_info() with dev_dbg()
> >   * Sakari: Switch induction variable type to unsigned
> >   * Sakari: Don't wait for reset_gpio when in ACPI mode
> >   * Sakari: Pull reset GPIO high on power on failure
> >   * Sakari: Add power on/off to resume/suspend
> >   * Sakari: Fix indentation
> >   * Sakari: Power off during ov8856_remove()
> >   * Sakari: Don't sleep during power-on in ACPI mode
> >   * Sakari: Switch to getting xvclk from clk_get_rate
> > 
> > - Changes since v1:
> >   * Andy & Sakari: Make XVCLK optional since to not break ACPI
> >   * Fabio: Change n_shutdown_gpio name to reset_gpio
> >   * Fabio: Invert reset_gpio due to GPIO_ACTIVE_HIGH -> GPIO_ACTIVE_LOW change
> >   * Fabio: Remove empty line
> >   * Fabio: Remove real error from devm_gpiod_get() failures
> >   * Sakari: ARRAY_SIZE() directly instead of through OV8856_NUM_SUPPLIES
> >   * Sakari: Use XVCLK rate as provided by DT
> > 
> >  drivers/media/i2c/ov8856.c | 137 +++++++++++++++++++++++++++++++++----
> >  1 file changed, 123 insertions(+), 14 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
> > index 8655842af275..4749dc74d5ad 100644
> > --- a/drivers/media/i2c/ov8856.c
> > +++ b/drivers/media/i2c/ov8856.c
> > @@ -3,10 +3,13 @@
> >  
> >  #include <asm/unaligned.h>
> >  #include <linux/acpi.h>
> > +#include <linux/clk.h>
> >  #include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> >  #include <linux/i2c.h>
> >  #include <linux/module.h>
> >  #include <linux/pm_runtime.h>
> > +#include <linux/regulator/consumer.h>
> >  #include <media/v4l2-ctrls.h>
> >  #include <media/v4l2-device.h>
> >  #include <media/v4l2-fwnode.h>
> > @@ -18,7 +21,7 @@
> >  #define OV8856_LINK_FREQ_360MHZ		360000000ULL
> >  #define OV8856_LINK_FREQ_180MHZ		180000000ULL
> >  #define OV8856_SCLK			144000000ULL
> > -#define OV8856_MCLK			19200000
> > +#define OV8856_XVCLK_19_2		19200000
> >  #define OV8856_DATA_LANES		4
> >  #define OV8856_RGB_DEPTH		10
> >  
> > @@ -64,6 +67,12 @@
> >  
> >  #define to_ov8856(_sd)			container_of(_sd, struct ov8856, sd)
> >  
> > +static const char * const ov8856_supply_names[] = {
> > +	"dovdd",	/* Digital I/O power */
> > +	"avdd",		/* Analog power */
> > +	"dvdd",		/* Digital core power */
> > +};
> > +
> >  enum {
> >  	OV8856_LINK_FREQ_720MBPS,
> >  	OV8856_LINK_FREQ_360MBPS,
> > @@ -566,6 +575,11 @@ struct ov8856 {
> >  	struct media_pad pad;
> >  	struct v4l2_ctrl_handler ctrl_handler;
> >  
> > +	struct device		*dev;
> > +	struct clk		*xvclk;
> > +	struct gpio_desc	*reset_gpio;
> > +	struct regulator_bulk_data supplies[ARRAY_SIZE(ov8856_supply_names)];
> > +
> >  	/* V4L2 Controls */
> >  	struct v4l2_ctrl *link_freq;
> >  	struct v4l2_ctrl *pixel_rate;
> > @@ -908,6 +922,52 @@ static int ov8856_set_stream(struct v4l2_subdev *sd, int enable)
> >  	return ret;
> >  }
> >  
> > +static int __ov8856_power_on(struct ov8856 *ov8856)
> > +{
> > +	struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(ov8856->xvclk);
> 
> You're request the clk only in DT case or do I miss something? If so you
> have to check if the clk is available.
> 
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "failed to enable xvclk\n");
> > +		return ret;
> > +	}
> > +
> > +	if (is_acpi_node(dev_fwnode(ov8856->dev)))
> > +		return 0;
> > +
> > +	if (ov8856->reset_gpio) {
> > +		gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > +		usleep_range(1000, 2000);
> > +	}
> > +
> > +	ret = regulator_bulk_enable(ARRAY_SIZE(ov8856_supply_names),
> > +				    ov8856->supplies);
> > +	if (ret < 0) {
> > +		dev_err(&client->dev, "failed to enable regulators\n");
> > +		goto disable_clk;
> > +	}
> > +
> > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 0);
> 
> You need to check the existance of the gpio here too.

No need to; the GPIO framework can handle this internally.

> 
> > +	usleep_range(1500, 1800);
> > +
> > +	return 0;
> > +
> > +disable_clk:
> > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> 
> And here.. pls check the whole patch.
> 
> > +	clk_disable_unprepare(ov8856->xvclk);
> > +
> > +	return ret;
> > +}
> > +
> > +static void __ov8856_power_off(struct ov8856 *ov8856)
> > +{
> > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > +	regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names),
> > +			       ov8856->supplies);
> > +	clk_disable_unprepare(ov8856->xvclk);
> 
> Clk is only availabel in DT use-case.
> 
> > +}
> > +
> >  static int __maybe_unused ov8856_suspend(struct device *dev)
> >  {
> >  	struct i2c_client *client = to_i2c_client(dev);
> > @@ -918,6 +978,7 @@ static int __maybe_unused ov8856_suspend(struct device *dev)
> >  	if (ov8856->streaming)
> >  		ov8856_stop_streaming(ov8856);
> >  
> > +	__ov8856_power_off(ov8856);
> >  	mutex_unlock(&ov8856->mutex);
> >  
> >  	return 0;
> > @@ -931,6 +992,8 @@ static int __maybe_unused ov8856_resume(struct device *dev)
> >  	int ret;
> >  
> >  	mutex_lock(&ov8856->mutex);
> > +
> > +	__ov8856_power_on(ov8856);
> >  	if (ov8856->streaming) {
> >  		ret = ov8856_start_streaming(ov8856);
> >  		if (ret) {
> > @@ -1092,29 +1155,54 @@ static int ov8856_identify_module(struct ov8856 *ov8856)
> >  	return 0;
> >  }
> >  
> > -static int ov8856_check_hwcfg(struct device *dev)
> > +static int ov8856_get_hwcfg(struct ov8856 *ov8856)
> >  {
> > +	struct device *dev = ov8856->dev;
> >  	struct fwnode_handle *ep;
> >  	struct fwnode_handle *fwnode = dev_fwnode(dev);
> >  	struct v4l2_fwnode_endpoint bus_cfg = {
> >  		.bus_type = V4L2_MBUS_CSI2_DPHY
> >  	};
> > -	u32 mclk;
> > +	u32 xvclk_rate;
> >  	int ret;
> >  	unsigned int i, j;
> >  
> >  	if (!fwnode)
> >  		return -ENXIO;
> >  
> > -	ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
> > +	ret = fwnode_property_read_u32(fwnode, "clock-frequency",
> > +		&xvclk_rate);
> >  	if (ret)
> >  		return ret;
> >  
> > -	if (mclk != OV8856_MCLK) {
> > -		dev_err(dev, "external clock %d is not supported", mclk);
> > -		return -EINVAL;
> > +	if (!is_acpi_node(fwnode)) {
> > +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > +		if (IS_ERR(ov8856->xvclk)) {
> > +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> > +					ov8856->xvclk);
> > +			return PTR_ERR(ov8856->xvclk);
> > +		}
> > +
> > +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> > +		xvclk_rate = clk_get_rate(ov8856->xvclk);
> >  	}
> 
> Why do we handle the clock only in DT case? Is there a problem with the
> clock handling and ACPI?

Not really, it's just that ACPI does not provide an interface to the clocks
as such.
Marco Felsch April 30, 2020, 9:53 a.m. UTC | #3
Hi Sakari,

On 20-04-30 12:45, Sakari Ailus wrote:
> Hi Marco,
> 
> On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> > Hi Robert,
> > 
> > thnakf for the patch but pls keep in mind to do one thing per patch.
> > IMHO this patch do a lot more. Anyway below are my comment :)
> > 
> > On 20-04-29 18:24, Robert Foss wrote:
> > > Add match table, enable ov8856_probe() to support
> > > both ACPI and DT modes.
> > 
> > You are also adding the support for reset-gpios and regualtors. IMHO the
> > commit message don't belong to the changes you made anymore.
> > 
> > > ACPI and DT modes are primarily distinguished from
> > > each other by relying on devm_XXX_get_optional()
> > > will return NULL instead of a reference for the
> > > desired managed resource.
> > > 
> > > Signed-off-by: Robert Foss <robert.foss@linaro.org>
> > > ---
> > > 
> > > - Changes since v5:
> > >   * Maxime & Sakari: Replaced clock tolerance check with warning
> > > 
> > > - Changes since v4:
> > >   * Maxime & Sakari: Switch to clock-frequency
> > > 
> > > - Changes since v3:
> > >   * Remove redundant {}-brackets
> > >   * Compare xvclk_rate to 5% tolerance
> > >   * Andy: Use dev_fwnode()
> > >   * Andy: Use %pe instead of %ld + PTR_ERR()
> > >   * Andy: Invert reset_gpio logic
> > >   * Andy: Remove dev_dbg() from failing reset_gpio setup
> > >   * Andy: Use dev_err for logging for failures
> > >   * Andy: Remove dev_warn from EDEFER/regulator error path
> > >   * Andy & Sakari: Replaced GPIOD_OUT_XXX with 0/1
> > >   * Maxime & Sakari: Verify clock frequency from DT
> > >   * Sakari: Verify the 'xvclk_rate' is set correctly for ACPI/DT devices
> > >   * Sakari: Remove duplicate ov8856->dev assignment
> > > 
> > > - Changes since v2:
> > >   * Added "struct device *dev" member to struct ov8856
> > >   * Andy: Switch to optional version of devm_gpiod_get
> > >   * Andy: Switch to optional version of devm_clk_get
> > >   * Fabio: Add reset sleep period
> > >   * Sakari: Unify defines for 19.2Mhz
> > >   * Sakari: Remove 24Mhz clock, since it isn't needed for supported modes
> > >   * Sakari: Replace dev_info() with dev_dbg()
> > >   * Sakari: Switch induction variable type to unsigned
> > >   * Sakari: Don't wait for reset_gpio when in ACPI mode
> > >   * Sakari: Pull reset GPIO high on power on failure
> > >   * Sakari: Add power on/off to resume/suspend
> > >   * Sakari: Fix indentation
> > >   * Sakari: Power off during ov8856_remove()
> > >   * Sakari: Don't sleep during power-on in ACPI mode
> > >   * Sakari: Switch to getting xvclk from clk_get_rate
> > > 
> > > - Changes since v1:
> > >   * Andy & Sakari: Make XVCLK optional since to not break ACPI
> > >   * Fabio: Change n_shutdown_gpio name to reset_gpio
> > >   * Fabio: Invert reset_gpio due to GPIO_ACTIVE_HIGH -> GPIO_ACTIVE_LOW change
> > >   * Fabio: Remove empty line
> > >   * Fabio: Remove real error from devm_gpiod_get() failures
> > >   * Sakari: ARRAY_SIZE() directly instead of through OV8856_NUM_SUPPLIES
> > >   * Sakari: Use XVCLK rate as provided by DT
> > > 
> > >  drivers/media/i2c/ov8856.c | 137 +++++++++++++++++++++++++++++++++----
> > >  1 file changed, 123 insertions(+), 14 deletions(-)
> > > 
> > > diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
> > > index 8655842af275..4749dc74d5ad 100644
> > > --- a/drivers/media/i2c/ov8856.c
> > > +++ b/drivers/media/i2c/ov8856.c
> > > @@ -3,10 +3,13 @@
> > >  
> > >  #include <asm/unaligned.h>
> > >  #include <linux/acpi.h>
> > > +#include <linux/clk.h>
> > >  #include <linux/delay.h>
> > > +#include <linux/gpio/consumer.h>
> > >  #include <linux/i2c.h>
> > >  #include <linux/module.h>
> > >  #include <linux/pm_runtime.h>
> > > +#include <linux/regulator/consumer.h>
> > >  #include <media/v4l2-ctrls.h>
> > >  #include <media/v4l2-device.h>
> > >  #include <media/v4l2-fwnode.h>
> > > @@ -18,7 +21,7 @@
> > >  #define OV8856_LINK_FREQ_360MHZ		360000000ULL
> > >  #define OV8856_LINK_FREQ_180MHZ		180000000ULL
> > >  #define OV8856_SCLK			144000000ULL
> > > -#define OV8856_MCLK			19200000
> > > +#define OV8856_XVCLK_19_2		19200000
> > >  #define OV8856_DATA_LANES		4
> > >  #define OV8856_RGB_DEPTH		10
> > >  
> > > @@ -64,6 +67,12 @@
> > >  
> > >  #define to_ov8856(_sd)			container_of(_sd, struct ov8856, sd)
> > >  
> > > +static const char * const ov8856_supply_names[] = {
> > > +	"dovdd",	/* Digital I/O power */
> > > +	"avdd",		/* Analog power */
> > > +	"dvdd",		/* Digital core power */
> > > +};
> > > +
> > >  enum {
> > >  	OV8856_LINK_FREQ_720MBPS,
> > >  	OV8856_LINK_FREQ_360MBPS,
> > > @@ -566,6 +575,11 @@ struct ov8856 {
> > >  	struct media_pad pad;
> > >  	struct v4l2_ctrl_handler ctrl_handler;
> > >  
> > > +	struct device		*dev;
> > > +	struct clk		*xvclk;
> > > +	struct gpio_desc	*reset_gpio;
> > > +	struct regulator_bulk_data supplies[ARRAY_SIZE(ov8856_supply_names)];
> > > +
> > >  	/* V4L2 Controls */
> > >  	struct v4l2_ctrl *link_freq;
> > >  	struct v4l2_ctrl *pixel_rate;
> > > @@ -908,6 +922,52 @@ static int ov8856_set_stream(struct v4l2_subdev *sd, int enable)
> > >  	return ret;
> > >  }
> > >  
> > > +static int __ov8856_power_on(struct ov8856 *ov8856)
> > > +{
> > > +	struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
> > > +	int ret;
> > > +
> > > +	ret = clk_prepare_enable(ov8856->xvclk);
> > 
> > You're request the clk only in DT case or do I miss something? If so you
> > have to check if the clk is available.
> > 
> > > +	if (ret < 0) {
> > > +		dev_err(&client->dev, "failed to enable xvclk\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	if (is_acpi_node(dev_fwnode(ov8856->dev)))
> > > +		return 0;
> > > +
> > > +	if (ov8856->reset_gpio) {
> > > +		gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > > +		usleep_range(1000, 2000);
> > > +	}
> > > +
> > > +	ret = regulator_bulk_enable(ARRAY_SIZE(ov8856_supply_names),
> > > +				    ov8856->supplies);
> > > +	if (ret < 0) {
> > > +		dev_err(&client->dev, "failed to enable regulators\n");
> > > +		goto disable_clk;
> > > +	}
> > > +
> > > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 0);
> > 
> > You need to check the existance of the gpio here too.
> 
> No need to; the GPIO framework can handle this internally.

Ahh, I said nothing :) so all my comments about that can be dropped. 

> > > +	usleep_range(1500, 1800);
> > > +
> > > +	return 0;
> > > +
> > > +disable_clk:
> > > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > 
> > And here.. pls check the whole patch.
> > 
> > > +	clk_disable_unprepare(ov8856->xvclk);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void __ov8856_power_off(struct ov8856 *ov8856)
> > > +{
> > > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > > +	regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names),
> > > +			       ov8856->supplies);
> > > +	clk_disable_unprepare(ov8856->xvclk);
> > 
> > Clk is only availabel in DT use-case.
> > 
> > > +}
> > > +
> > >  static int __maybe_unused ov8856_suspend(struct device *dev)
> > >  {
> > >  	struct i2c_client *client = to_i2c_client(dev);
> > > @@ -918,6 +978,7 @@ static int __maybe_unused ov8856_suspend(struct device *dev)
> > >  	if (ov8856->streaming)
> > >  		ov8856_stop_streaming(ov8856);
> > >  
> > > +	__ov8856_power_off(ov8856);
> > >  	mutex_unlock(&ov8856->mutex);
> > >  
> > >  	return 0;
> > > @@ -931,6 +992,8 @@ static int __maybe_unused ov8856_resume(struct device *dev)
> > >  	int ret;
> > >  
> > >  	mutex_lock(&ov8856->mutex);
> > > +
> > > +	__ov8856_power_on(ov8856);
> > >  	if (ov8856->streaming) {
> > >  		ret = ov8856_start_streaming(ov8856);
> > >  		if (ret) {
> > > @@ -1092,29 +1155,54 @@ static int ov8856_identify_module(struct ov8856 *ov8856)
> > >  	return 0;
> > >  }
> > >  
> > > -static int ov8856_check_hwcfg(struct device *dev)
> > > +static int ov8856_get_hwcfg(struct ov8856 *ov8856)
> > >  {
> > > +	struct device *dev = ov8856->dev;
> > >  	struct fwnode_handle *ep;
> > >  	struct fwnode_handle *fwnode = dev_fwnode(dev);
> > >  	struct v4l2_fwnode_endpoint bus_cfg = {
> > >  		.bus_type = V4L2_MBUS_CSI2_DPHY
> > >  	};
> > > -	u32 mclk;
> > > +	u32 xvclk_rate;
> > >  	int ret;
> > >  	unsigned int i, j;
> > >  
> > >  	if (!fwnode)
> > >  		return -ENXIO;
> > >  
> > > -	ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
> > > +	ret = fwnode_property_read_u32(fwnode, "clock-frequency",
> > > +		&xvclk_rate);
> > >  	if (ret)
> > >  		return ret;
> > >  
> > > -	if (mclk != OV8856_MCLK) {
> > > -		dev_err(dev, "external clock %d is not supported", mclk);
> > > -		return -EINVAL;
> > > +	if (!is_acpi_node(fwnode)) {
> > > +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > +		if (IS_ERR(ov8856->xvclk)) {
> > > +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > +					ov8856->xvclk);
> > > +			return PTR_ERR(ov8856->xvclk);
> > > +		}
> > > +
> > > +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > +		xvclk_rate = clk_get_rate(ov8856->xvclk);
> > >  	}
> > 
> > Why do we handle the clock only in DT case? Is there a problem with the
> > clock handling and ACPI?
> 
> Not really, it's just that ACPI does not provide an interface to the clocks
> as such.

But you will get a clk by devm_clk_get()?

> 
> -- 
> Regards,
> 
> Sakari Ailus
>
Sakari Ailus April 30, 2020, 9:59 a.m. UTC | #4
Hi Marco,

On Thu, Apr 30, 2020 at 11:53:32AM +0200, Marco Felsch wrote:
> Hi Sakari,
> 
> On 20-04-30 12:45, Sakari Ailus wrote:
> > Hi Marco,
> > 
> > On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> > > Hi Robert,
> > > 
> > > thnakf for the patch but pls keep in mind to do one thing per patch.
> > > IMHO this patch do a lot more. Anyway below are my comment :)
> > > 
> > > On 20-04-29 18:24, Robert Foss wrote:
> > > > Add match table, enable ov8856_probe() to support
> > > > both ACPI and DT modes.
> > > 
> > > You are also adding the support for reset-gpios and regualtors. IMHO the
> > > commit message don't belong to the changes you made anymore.
> > > 
> > > > ACPI and DT modes are primarily distinguished from
> > > > each other by relying on devm_XXX_get_optional()
> > > > will return NULL instead of a reference for the
> > > > desired managed resource.
> > > > 
> > > > Signed-off-by: Robert Foss <robert.foss@linaro.org>
> > > > ---
> > > > 
> > > > - Changes since v5:
> > > >   * Maxime & Sakari: Replaced clock tolerance check with warning
> > > > 
> > > > - Changes since v4:
> > > >   * Maxime & Sakari: Switch to clock-frequency
> > > > 
> > > > - Changes since v3:
> > > >   * Remove redundant {}-brackets
> > > >   * Compare xvclk_rate to 5% tolerance
> > > >   * Andy: Use dev_fwnode()
> > > >   * Andy: Use %pe instead of %ld + PTR_ERR()
> > > >   * Andy: Invert reset_gpio logic
> > > >   * Andy: Remove dev_dbg() from failing reset_gpio setup
> > > >   * Andy: Use dev_err for logging for failures
> > > >   * Andy: Remove dev_warn from EDEFER/regulator error path
> > > >   * Andy & Sakari: Replaced GPIOD_OUT_XXX with 0/1
> > > >   * Maxime & Sakari: Verify clock frequency from DT
> > > >   * Sakari: Verify the 'xvclk_rate' is set correctly for ACPI/DT devices
> > > >   * Sakari: Remove duplicate ov8856->dev assignment
> > > > 
> > > > - Changes since v2:
> > > >   * Added "struct device *dev" member to struct ov8856
> > > >   * Andy: Switch to optional version of devm_gpiod_get
> > > >   * Andy: Switch to optional version of devm_clk_get
> > > >   * Fabio: Add reset sleep period
> > > >   * Sakari: Unify defines for 19.2Mhz
> > > >   * Sakari: Remove 24Mhz clock, since it isn't needed for supported modes
> > > >   * Sakari: Replace dev_info() with dev_dbg()
> > > >   * Sakari: Switch induction variable type to unsigned
> > > >   * Sakari: Don't wait for reset_gpio when in ACPI mode
> > > >   * Sakari: Pull reset GPIO high on power on failure
> > > >   * Sakari: Add power on/off to resume/suspend
> > > >   * Sakari: Fix indentation
> > > >   * Sakari: Power off during ov8856_remove()
> > > >   * Sakari: Don't sleep during power-on in ACPI mode
> > > >   * Sakari: Switch to getting xvclk from clk_get_rate
> > > > 
> > > > - Changes since v1:
> > > >   * Andy & Sakari: Make XVCLK optional since to not break ACPI
> > > >   * Fabio: Change n_shutdown_gpio name to reset_gpio
> > > >   * Fabio: Invert reset_gpio due to GPIO_ACTIVE_HIGH -> GPIO_ACTIVE_LOW change
> > > >   * Fabio: Remove empty line
> > > >   * Fabio: Remove real error from devm_gpiod_get() failures
> > > >   * Sakari: ARRAY_SIZE() directly instead of through OV8856_NUM_SUPPLIES
> > > >   * Sakari: Use XVCLK rate as provided by DT
> > > > 
> > > >  drivers/media/i2c/ov8856.c | 137 +++++++++++++++++++++++++++++++++----
> > > >  1 file changed, 123 insertions(+), 14 deletions(-)
> > > > 
> > > > diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
> > > > index 8655842af275..4749dc74d5ad 100644
> > > > --- a/drivers/media/i2c/ov8856.c
> > > > +++ b/drivers/media/i2c/ov8856.c
> > > > @@ -3,10 +3,13 @@
> > > >  
> > > >  #include <asm/unaligned.h>
> > > >  #include <linux/acpi.h>
> > > > +#include <linux/clk.h>
> > > >  #include <linux/delay.h>
> > > > +#include <linux/gpio/consumer.h>
> > > >  #include <linux/i2c.h>
> > > >  #include <linux/module.h>
> > > >  #include <linux/pm_runtime.h>
> > > > +#include <linux/regulator/consumer.h>
> > > >  #include <media/v4l2-ctrls.h>
> > > >  #include <media/v4l2-device.h>
> > > >  #include <media/v4l2-fwnode.h>
> > > > @@ -18,7 +21,7 @@
> > > >  #define OV8856_LINK_FREQ_360MHZ		360000000ULL
> > > >  #define OV8856_LINK_FREQ_180MHZ		180000000ULL
> > > >  #define OV8856_SCLK			144000000ULL
> > > > -#define OV8856_MCLK			19200000
> > > > +#define OV8856_XVCLK_19_2		19200000
> > > >  #define OV8856_DATA_LANES		4
> > > >  #define OV8856_RGB_DEPTH		10
> > > >  
> > > > @@ -64,6 +67,12 @@
> > > >  
> > > >  #define to_ov8856(_sd)			container_of(_sd, struct ov8856, sd)
> > > >  
> > > > +static const char * const ov8856_supply_names[] = {
> > > > +	"dovdd",	/* Digital I/O power */
> > > > +	"avdd",		/* Analog power */
> > > > +	"dvdd",		/* Digital core power */
> > > > +};
> > > > +
> > > >  enum {
> > > >  	OV8856_LINK_FREQ_720MBPS,
> > > >  	OV8856_LINK_FREQ_360MBPS,
> > > > @@ -566,6 +575,11 @@ struct ov8856 {
> > > >  	struct media_pad pad;
> > > >  	struct v4l2_ctrl_handler ctrl_handler;
> > > >  
> > > > +	struct device		*dev;
> > > > +	struct clk		*xvclk;
> > > > +	struct gpio_desc	*reset_gpio;
> > > > +	struct regulator_bulk_data supplies[ARRAY_SIZE(ov8856_supply_names)];
> > > > +
> > > >  	/* V4L2 Controls */
> > > >  	struct v4l2_ctrl *link_freq;
> > > >  	struct v4l2_ctrl *pixel_rate;
> > > > @@ -908,6 +922,52 @@ static int ov8856_set_stream(struct v4l2_subdev *sd, int enable)
> > > >  	return ret;
> > > >  }
> > > >  
> > > > +static int __ov8856_power_on(struct ov8856 *ov8856)
> > > > +{
> > > > +	struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd);
> > > > +	int ret;
> > > > +
> > > > +	ret = clk_prepare_enable(ov8856->xvclk);
> > > 
> > > You're request the clk only in DT case or do I miss something? If so you
> > > have to check if the clk is available.
> > > 
> > > > +	if (ret < 0) {
> > > > +		dev_err(&client->dev, "failed to enable xvclk\n");
> > > > +		return ret;
> > > > +	}
> > > > +
> > > > +	if (is_acpi_node(dev_fwnode(ov8856->dev)))
> > > > +		return 0;
> > > > +
> > > > +	if (ov8856->reset_gpio) {
> > > > +		gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > > > +		usleep_range(1000, 2000);
> > > > +	}
> > > > +
> > > > +	ret = regulator_bulk_enable(ARRAY_SIZE(ov8856_supply_names),
> > > > +				    ov8856->supplies);
> > > > +	if (ret < 0) {
> > > > +		dev_err(&client->dev, "failed to enable regulators\n");
> > > > +		goto disable_clk;
> > > > +	}
> > > > +
> > > > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 0);
> > > 
> > > You need to check the existance of the gpio here too.
> > 
> > No need to; the GPIO framework can handle this internally.
> 
> Ahh, I said nothing :) so all my comments about that can be dropped. 
> 
> > > > +	usleep_range(1500, 1800);
> > > > +
> > > > +	return 0;
> > > > +
> > > > +disable_clk:
> > > > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > > 
> > > And here.. pls check the whole patch.
> > > 
> > > > +	clk_disable_unprepare(ov8856->xvclk);
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static void __ov8856_power_off(struct ov8856 *ov8856)
> > > > +{
> > > > +	gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
> > > > +	regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names),
> > > > +			       ov8856->supplies);
> > > > +	clk_disable_unprepare(ov8856->xvclk);
> > > 
> > > Clk is only availabel in DT use-case.
> > > 
> > > > +}
> > > > +
> > > >  static int __maybe_unused ov8856_suspend(struct device *dev)
> > > >  {
> > > >  	struct i2c_client *client = to_i2c_client(dev);
> > > > @@ -918,6 +978,7 @@ static int __maybe_unused ov8856_suspend(struct device *dev)
> > > >  	if (ov8856->streaming)
> > > >  		ov8856_stop_streaming(ov8856);
> > > >  
> > > > +	__ov8856_power_off(ov8856);
> > > >  	mutex_unlock(&ov8856->mutex);
> > > >  
> > > >  	return 0;
> > > > @@ -931,6 +992,8 @@ static int __maybe_unused ov8856_resume(struct device *dev)
> > > >  	int ret;
> > > >  
> > > >  	mutex_lock(&ov8856->mutex);
> > > > +
> > > > +	__ov8856_power_on(ov8856);
> > > >  	if (ov8856->streaming) {
> > > >  		ret = ov8856_start_streaming(ov8856);
> > > >  		if (ret) {
> > > > @@ -1092,29 +1155,54 @@ static int ov8856_identify_module(struct ov8856 *ov8856)
> > > >  	return 0;
> > > >  }
> > > >  
> > > > -static int ov8856_check_hwcfg(struct device *dev)
> > > > +static int ov8856_get_hwcfg(struct ov8856 *ov8856)
> > > >  {
> > > > +	struct device *dev = ov8856->dev;
> > > >  	struct fwnode_handle *ep;
> > > >  	struct fwnode_handle *fwnode = dev_fwnode(dev);
> > > >  	struct v4l2_fwnode_endpoint bus_cfg = {
> > > >  		.bus_type = V4L2_MBUS_CSI2_DPHY
> > > >  	};
> > > > -	u32 mclk;
> > > > +	u32 xvclk_rate;
> > > >  	int ret;
> > > >  	unsigned int i, j;
> > > >  
> > > >  	if (!fwnode)
> > > >  		return -ENXIO;
> > > >  
> > > > -	ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
> > > > +	ret = fwnode_property_read_u32(fwnode, "clock-frequency",
> > > > +		&xvclk_rate);
> > > >  	if (ret)
> > > >  		return ret;
> > > >  
> > > > -	if (mclk != OV8856_MCLK) {
> > > > -		dev_err(dev, "external clock %d is not supported", mclk);
> > > > -		return -EINVAL;
> > > > +	if (!is_acpi_node(fwnode)) {
> > > > +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > > +		if (IS_ERR(ov8856->xvclk)) {
> > > > +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > > +					ov8856->xvclk);
> > > > +			return PTR_ERR(ov8856->xvclk);
> > > > +		}
> > > > +
> > > > +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > > +		xvclk_rate = clk_get_rate(ov8856->xvclk);
> > > >  	}
> > > 
> > > Why do we handle the clock only in DT case? Is there a problem with the
> > > clock handling and ACPI?
> > 
> > Not really, it's just that ACPI does not provide an interface to the clocks
> > as such.
> 
> But you will get a clk by devm_clk_get()?

No, because ACPI does not expose one to drivers. Effectively the entire
power sequences are implemented in ACPI, not in the driver.
Marco Felsch April 30, 2020, 10:11 a.m. UTC | #5
On 20-04-30 12:59, Sakari Ailus wrote:
> Hi Marco,
> 
> On Thu, Apr 30, 2020 at 11:53:32AM +0200, Marco Felsch wrote:
> > Hi Sakari,
> > 
> > On 20-04-30 12:45, Sakari Ailus wrote:
> > > Hi Marco,
> > > 
> > > On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:

...

> > > > > -	if (mclk != OV8856_MCLK) {
> > > > > -		dev_err(dev, "external clock %d is not supported", mclk);
> > > > > -		return -EINVAL;
> > > > > +	if (!is_acpi_node(fwnode)) {
> > > > > +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > > > +		if (IS_ERR(ov8856->xvclk)) {
> > > > > +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > > > +					ov8856->xvclk);
> > > > > +			return PTR_ERR(ov8856->xvclk);
> > > > > +		}
> > > > > +
> > > > > +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > > > +		xvclk_rate = clk_get_rate(ov8856->xvclk);
> > > > >  	}
> > > > 
> > > > Why do we handle the clock only in DT case? Is there a problem with the
> > > > clock handling and ACPI?
> > > 
> > > Not really, it's just that ACPI does not provide an interface to the clocks
> > > as such.
> > 
> > But you will get a clk by devm_clk_get()?
> 
> No, because ACPI does not expose one to drivers. Effectively the entire
> power sequences are implemented in ACPI, not in the driver.
> 

Ah okay, thanks for the explanation. I'm really not into the ACPI
stuff.. So this means the __power_off / power_on should only be done if
we are using DT's?

Regards,
  Marco
Sakari Ailus April 30, 2020, 10:20 a.m. UTC | #6
On Thu, Apr 30, 2020 at 12:11:57PM +0200, Marco Felsch wrote:
> On 20-04-30 12:59, Sakari Ailus wrote:
> > Hi Marco,
> > 
> > On Thu, Apr 30, 2020 at 11:53:32AM +0200, Marco Felsch wrote:
> > > Hi Sakari,
> > > 
> > > On 20-04-30 12:45, Sakari Ailus wrote:
> > > > Hi Marco,
> > > > 
> > > > On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> 
> ...
> 
> > > > > > -	if (mclk != OV8856_MCLK) {
> > > > > > -		dev_err(dev, "external clock %d is not supported", mclk);
> > > > > > -		return -EINVAL;
> > > > > > +	if (!is_acpi_node(fwnode)) {
> > > > > > +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > > > > +		if (IS_ERR(ov8856->xvclk)) {
> > > > > > +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > > > > +					ov8856->xvclk);
> > > > > > +			return PTR_ERR(ov8856->xvclk);
> > > > > > +		}
> > > > > > +
> > > > > > +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > > > > +		xvclk_rate = clk_get_rate(ov8856->xvclk);
> > > > > >  	}
> > > > > 
> > > > > Why do we handle the clock only in DT case? Is there a problem with the
> > > > > clock handling and ACPI?
> > > > 
> > > > Not really, it's just that ACPI does not provide an interface to the clocks
> > > > as such.
> > > 
> > > But you will get a clk by devm_clk_get()?
> > 
> > No, because ACPI does not expose one to drivers. Effectively the entire
> > power sequences are implemented in ACPI, not in the driver.
> > 
> 
> Ah okay, thanks for the explanation. I'm really not into the ACPI
> stuff.. So this means the __power_off / power_on should only be done if
> we are using DT's?

Correct. That's why it bails out early. It could be yet earlier though,
without doing anything.
Marco Felsch April 30, 2020, 12:07 p.m. UTC | #7
On 20-04-30 13:20, Sakari Ailus wrote:
> On Thu, Apr 30, 2020 at 12:11:57PM +0200, Marco Felsch wrote:
> > On 20-04-30 12:59, Sakari Ailus wrote:
> > > Hi Marco,
> > > 
> > > On Thu, Apr 30, 2020 at 11:53:32AM +0200, Marco Felsch wrote:
> > > > Hi Sakari,
> > > > 
> > > > On 20-04-30 12:45, Sakari Ailus wrote:
> > > > > Hi Marco,
> > > > > 
> > > > > On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> > 
> > ...
> > 
> > > > > > > -	if (mclk != OV8856_MCLK) {
> > > > > > > -		dev_err(dev, "external clock %d is not supported", mclk);
> > > > > > > -		return -EINVAL;
> > > > > > > +	if (!is_acpi_node(fwnode)) {
> > > > > > > +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > > > > > +		if (IS_ERR(ov8856->xvclk)) {
> > > > > > > +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > > > > > +					ov8856->xvclk);
> > > > > > > +			return PTR_ERR(ov8856->xvclk);
> > > > > > > +		}
> > > > > > > +
> > > > > > > +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > > > > > +		xvclk_rate = clk_get_rate(ov8856->xvclk);
> > > > > > >  	}
> > > > > > 
> > > > > > Why do we handle the clock only in DT case? Is there a problem with the
> > > > > > clock handling and ACPI?
> > > > > 
> > > > > Not really, it's just that ACPI does not provide an interface to the clocks
> > > > > as such.
> > > > 
> > > > But you will get a clk by devm_clk_get()?
> > > 
> > > No, because ACPI does not expose one to drivers. Effectively the entire
> > > power sequences are implemented in ACPI, not in the driver.
> > > 
> > 
> > Ah okay, thanks for the explanation. I'm really not into the ACPI
> > stuff.. So this means the __power_off / power_on should only be done if
> > we are using DT's?
> 
> Correct. That's why it bails out early. It could be yet earlier though,
> without doing anything.

Yes I see. For easier and error less prone handling I would prefer:

if (is_acpi_node())
	return 0;

as first instruction for __power_off/on().

Also I would refator the ov8856_check_hwcfg() so the common part can be
used by this function and by a ov8856_parse_of() function. But thats
only my opinion.

Regards,
  Marco
Sakari Ailus April 30, 2020, 1:32 p.m. UTC | #8
On Thu, Apr 30, 2020 at 02:07:41PM +0200, Marco Felsch wrote:
> On 20-04-30 13:20, Sakari Ailus wrote:
> > On Thu, Apr 30, 2020 at 12:11:57PM +0200, Marco Felsch wrote:
> > > On 20-04-30 12:59, Sakari Ailus wrote:
> > > > Hi Marco,
> > > > 
> > > > On Thu, Apr 30, 2020 at 11:53:32AM +0200, Marco Felsch wrote:
> > > > > Hi Sakari,
> > > > > 
> > > > > On 20-04-30 12:45, Sakari Ailus wrote:
> > > > > > Hi Marco,
> > > > > > 
> > > > > > On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> > > 
> > > ...
> > > 
> > > > > > > > -	if (mclk != OV8856_MCLK) {
> > > > > > > > -		dev_err(dev, "external clock %d is not supported", mclk);
> > > > > > > > -		return -EINVAL;
> > > > > > > > +	if (!is_acpi_node(fwnode)) {
> > > > > > > > +		ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > > > > > > +		if (IS_ERR(ov8856->xvclk)) {
> > > > > > > > +			dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > > > > > > +					ov8856->xvclk);
> > > > > > > > +			return PTR_ERR(ov8856->xvclk);
> > > > > > > > +		}
> > > > > > > > +
> > > > > > > > +		clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > > > > > > +		xvclk_rate = clk_get_rate(ov8856->xvclk);
> > > > > > > >  	}
> > > > > > > 
> > > > > > > Why do we handle the clock only in DT case? Is there a problem with the
> > > > > > > clock handling and ACPI?
> > > > > > 
> > > > > > Not really, it's just that ACPI does not provide an interface to the clocks
> > > > > > as such.
> > > > > 
> > > > > But you will get a clk by devm_clk_get()?
> > > > 
> > > > No, because ACPI does not expose one to drivers. Effectively the entire
> > > > power sequences are implemented in ACPI, not in the driver.
> > > > 
> > > 
> > > Ah okay, thanks for the explanation. I'm really not into the ACPI
> > > stuff.. So this means the __power_off / power_on should only be done if
> > > we are using DT's?
> > 
> > Correct. That's why it bails out early. It could be yet earlier though,
> > without doing anything.
> 
> Yes I see. For easier and error less prone handling I would prefer:
> 
> if (is_acpi_node())
> 	return 0;
> 
> as first instruction for __power_off/on().
> 
> Also I would refator the ov8856_check_hwcfg() so the common part can be
> used by this function and by a ov8856_parse_of() function. But thats
> only my opinion.

I guess it could be the same function, up to you.
Robert Foss April 30, 2020, 4:11 p.m. UTC | #9
Hey Marco,

On Thu, 30 Apr 2020 at 14:07, Marco Felsch <m.felsch@pengutronix.de> wrote:
>
> On 20-04-30 13:20, Sakari Ailus wrote:
> > On Thu, Apr 30, 2020 at 12:11:57PM +0200, Marco Felsch wrote:
> > > On 20-04-30 12:59, Sakari Ailus wrote:
> > > > Hi Marco,
> > > >
> > > > On Thu, Apr 30, 2020 at 11:53:32AM +0200, Marco Felsch wrote:
> > > > > Hi Sakari,
> > > > >
> > > > > On 20-04-30 12:45, Sakari Ailus wrote:
> > > > > > Hi Marco,
> > > > > >
> > > > > > On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> > >
> > > ...
> > >
> > > > > > > > - if (mclk != OV8856_MCLK) {
> > > > > > > > -         dev_err(dev, "external clock %d is not supported", mclk);
> > > > > > > > -         return -EINVAL;
> > > > > > > > + if (!is_acpi_node(fwnode)) {
> > > > > > > > +         ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > > > > > > +         if (IS_ERR(ov8856->xvclk)) {
> > > > > > > > +                 dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > > > > > > +                                 ov8856->xvclk);
> > > > > > > > +                 return PTR_ERR(ov8856->xvclk);
> > > > > > > > +         }
> > > > > > > > +
> > > > > > > > +         clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > > > > > > +         xvclk_rate = clk_get_rate(ov8856->xvclk);
> > > > > > > >   }
> > > > > > >
> > > > > > > Why do we handle the clock only in DT case? Is there a problem with the
> > > > > > > clock handling and ACPI?
> > > > > >
> > > > > > Not really, it's just that ACPI does not provide an interface to the clocks
> > > > > > as such.
> > > > >
> > > > > But you will get a clk by devm_clk_get()?
> > > >
> > > > No, because ACPI does not expose one to drivers. Effectively the entire
> > > > power sequences are implemented in ACPI, not in the driver.
> > > >
> > >
> > > Ah okay, thanks for the explanation. I'm really not into the ACPI
> > > stuff.. So this means the __power_off / power_on should only be done if
> > > we are using DT's?
> >
> > Correct. That's why it bails out early. It could be yet earlier though,
> > without doing anything.
>
> Yes I see. For easier and error less prone handling I would prefer:
>
> if (is_acpi_node())
>         return 0;
>
> as first instruction for __power_off/on().

__ov8856_power_on() does make a check like that, albeit only after
having run clk_prepare_enable() which won't do anything due to
ov8856->xvclk==NULL. So this should be fixed and be moved to after the
ACPI check.

__ov8856_power_off() has no ACPI check, but all of the calls it makes
are going to do nothing due to v8856->reset_gpio / v8856->reset_gpio /
ov8856->xvclk all being NULL or dummies. For the sake of clarity an
early ACPI check+return could be added, but if clarity is the goal a
comment would work too.

>
> Also I would refactor the ov8856_check_hwcfg() so the common part can be
> used by this function and by a ov8856_parse_of() function. But thats
> only my opinion.

I'm trying to grok the above paragraph. You'd like to see something in
the style of tc358743_probe_of()?

>
> Regards,
>   Marco
Marco Felsch May 4, 2020, 6:24 a.m. UTC | #10
Hi Robert,

On 20-04-30 18:11, Robert Foss wrote:
> Hey Marco,
> 
> On Thu, 30 Apr 2020 at 14:07, Marco Felsch <m.felsch@pengutronix.de> wrote:
> >
> > On 20-04-30 13:20, Sakari Ailus wrote:
> > > On Thu, Apr 30, 2020 at 12:11:57PM +0200, Marco Felsch wrote:
> > > > On 20-04-30 12:59, Sakari Ailus wrote:
> > > > > Hi Marco,
> > > > >
> > > > > On Thu, Apr 30, 2020 at 11:53:32AM +0200, Marco Felsch wrote:
> > > > > > Hi Sakari,
> > > > > >
> > > > > > On 20-04-30 12:45, Sakari Ailus wrote:
> > > > > > > Hi Marco,
> > > > > > >
> > > > > > > On Thu, Apr 30, 2020 at 11:35:24AM +0200, Marco Felsch wrote:
> > > >
> > > > ...
> > > >
> > > > > > > > > - if (mclk != OV8856_MCLK) {
> > > > > > > > > -         dev_err(dev, "external clock %d is not supported", mclk);
> > > > > > > > > -         return -EINVAL;
> > > > > > > > > + if (!is_acpi_node(fwnode)) {
> > > > > > > > > +         ov8856->xvclk = devm_clk_get(dev, "xvclk");
> > > > > > > > > +         if (IS_ERR(ov8856->xvclk)) {
> > > > > > > > > +                 dev_err(dev, "could not get xvclk clock (%pe)\n",
> > > > > > > > > +                                 ov8856->xvclk);
> > > > > > > > > +                 return PTR_ERR(ov8856->xvclk);
> > > > > > > > > +         }
> > > > > > > > > +
> > > > > > > > > +         clk_set_rate(ov8856->xvclk, xvclk_rate);
> > > > > > > > > +         xvclk_rate = clk_get_rate(ov8856->xvclk);
> > > > > > > > >   }
> > > > > > > >
> > > > > > > > Why do we handle the clock only in DT case? Is there a problem with the
> > > > > > > > clock handling and ACPI?
> > > > > > >
> > > > > > > Not really, it's just that ACPI does not provide an interface to the clocks
> > > > > > > as such.
> > > > > >
> > > > > > But you will get a clk by devm_clk_get()?
> > > > >
> > > > > No, because ACPI does not expose one to drivers. Effectively the entire
> > > > > power sequences are implemented in ACPI, not in the driver.
> > > > >
> > > >
> > > > Ah okay, thanks for the explanation. I'm really not into the ACPI
> > > > stuff.. So this means the __power_off / power_on should only be done if
> > > > we are using DT's?
> > >
> > > Correct. That's why it bails out early. It could be yet earlier though,
> > > without doing anything.
> >
> > Yes I see. For easier and error less prone handling I would prefer:
> >
> > if (is_acpi_node())
> >         return 0;
> >
> > as first instruction for __power_off/on().
> 
> __ov8856_power_on() does make a check like that, albeit only after
> having run clk_prepare_enable() which won't do anything due to
> ov8856->xvclk==NULL. So this should be fixed and be moved to after the
> ACPI check.

Yep, I saw that. I didn't checked the clk_prepare_enable() function and
just saw that we don't request the clk for the acpi case and enable it
in both cases. This doesn't sound right to me.

> __ov8856_power_off() has no ACPI check, but all of the calls it makes
> are going to do nothing due to v8856->reset_gpio / v8856->reset_gpio /
> ov8856->xvclk all being NULL or dummies. For the sake of clarity an
> early ACPI check+return could be added, but if clarity is the goal a
> comment would work too.

Thanks god that most of the library functions taking NULL into account
=) But I think we have to take the regulator count into account. Again I
don't know how the ACPI part is working. What happens if we request
regulators which aren't listed within the ACPI table? In case of DT
there will be added dummy-regulator. If this is the case for ACPI too we
are ending in an unbalanced regulator enable/disable count since you
enable it for the DT case and disable it in both cases.

> >
> > Also I would refactor the ov8856_check_hwcfg() so the common part can be
> > used by this function and by a ov8856_parse_of() function. But thats
> > only my opinion.
> 
> I'm trying to grok the above paragraph. You'd like to see something in
> the style of tc358743_probe_of()?

You don't have to if Saki is fine with the current patch. Just saying
that it would be a bit easier for the patch review.

Regards,
  Marco

> >
> > Regards,
> >   Marco
>