Commit 817660f4 authored by Thomas Nizan's avatar Thomas Nizan Committed by Mauro Carvalho Chehab
Browse files

media: i2c: max9286: Add support for port regulators



Allow users to use one PoC regulator per port, instead of a global
regulator.

The properties '^port[0-3]-poc-supply$' in the DT node are used to
indicate the regulators for individual ports.

Signed-off-by: default avatarThomas Nizan <tnizan@witekio.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: default avatarJacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent e3435af9
Loading
Loading
Loading
Loading
+112 −23
Original line number Diff line number Diff line
@@ -139,6 +139,7 @@
struct max9286_source {
	struct v4l2_subdev *sd;
	struct fwnode_handle *fwnode;
	struct regulator *regulator;
};

struct max9286_asd {
@@ -169,6 +170,7 @@ struct max9286_priv {
	u32 init_rev_chan_mv;
	u32 rev_chan_mv;

	bool use_gpio_poc;
	u32 gpio_poc[2];

	struct v4l2_ctrl_handler ctrls;
@@ -1088,9 +1090,6 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
	struct device *dev = &priv->client->dev;
	int ret;

	/* GPIO values default to high */
	priv->gpio_state = BIT(0) | BIT(1);

	/*
	 * Parse the "gpio-poc" vendor property. If the property is not
	 * specified the camera power is controlled by a regulator.
@@ -1102,18 +1101,7 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
		 * If gpio lines are not used for the camera power, register
		 * a gpio controller for consumers.
		 */
		ret = max9286_register_gpio(priv);
		if (ret)
			return ret;

		priv->regulator = devm_regulator_get(dev, "poc");
		if (IS_ERR(priv->regulator)) {
			return dev_err_probe(dev, PTR_ERR(priv->regulator),
					     "Unable to get PoC regulator (%ld)\n",
					     PTR_ERR(priv->regulator));
		}

		return 0;
		return max9286_register_gpio(priv);
	}

	/* If the property is specified make sure it is well formed. */
@@ -1124,21 +1112,75 @@ static int max9286_parse_gpios(struct max9286_priv *priv)
		return -EINVAL;
	}

	priv->use_gpio_poc = true;
	return 0;
}

static int max9286_poc_power_on(struct max9286_priv *priv)
{
	struct max9286_source *source;
	unsigned int enabled = 0;
	int ret;

	/* Enable the global regulator if available. */
	if (priv->regulator)
		return regulator_enable(priv->regulator);

	if (priv->use_gpio_poc)
		return max9286_gpio_set(priv, priv->gpio_poc[0],
					!priv->gpio_poc[1]);

	/* Otherwise use the per-port regulators. */
	for_each_source(priv, source) {
		ret = regulator_enable(source->regulator);
		if (ret < 0)
			goto error;

		enabled |= BIT(to_index(priv, source));
	}

	return 0;

error:
	for_each_source(priv, source) {
		if (enabled & BIT(to_index(priv, source)))
			regulator_disable(source->regulator);
	}

	return ret;
}

static int max9286_poc_power_off(struct max9286_priv *priv)
{
	struct max9286_source *source;
	int ret = 0;

	if (priv->regulator)
		return regulator_disable(priv->regulator);

	if (priv->use_gpio_poc)
		return max9286_gpio_set(priv, priv->gpio_poc[0],
					priv->gpio_poc[1]);

	for_each_source(priv, source) {
		int err;

		err = regulator_disable(source->regulator);
		if (!ret)
			ret = err;
	}

	return ret;
}

static int max9286_poc_enable(struct max9286_priv *priv, bool enable)
{
	int ret;

	/* If the regulator is not available, use gpio to control power. */
	if (!priv->regulator)
		ret = max9286_gpio_set(priv, priv->gpio_poc[0],
				       enable ^ priv->gpio_poc[1]);
	else if (enable)
		ret = regulator_enable(priv->regulator);
	if (enable)
		ret = max9286_poc_power_on(priv);
	else
		ret = regulator_disable(priv->regulator);
		ret = max9286_poc_power_off(priv);

	if (ret < 0)
		dev_err(&priv->client->dev, "Unable to turn power %s\n",
@@ -1317,6 +1359,44 @@ static int max9286_parse_dt(struct max9286_priv *priv)
	return 0;
}

static int max9286_get_poc_supplies(struct max9286_priv *priv)
{
	struct device *dev = &priv->client->dev;
	struct max9286_source *source;
	int ret;

	/* Start by getting the global regulator. */
	priv->regulator = devm_regulator_get_optional(dev, "poc");
	if (!IS_ERR(priv->regulator))
		return 0;

	if (PTR_ERR(priv->regulator) != -ENODEV)
		return dev_err_probe(dev, PTR_ERR(priv->regulator),
				     "Unable to get PoC regulator\n");

	/* If there's no global regulator, get per-port regulators. */
	dev_dbg(dev,
		"No global PoC regulator, looking for per-port regulators\n");
	priv->regulator = NULL;

	for_each_source(priv, source) {
		unsigned int index = to_index(priv, source);
		char name[10];

		snprintf(name, sizeof(name), "port%u-poc", index);
		source->regulator = devm_regulator_get(dev, name);
		if (IS_ERR(source->regulator)) {
			ret = PTR_ERR(source->regulator);
			dev_err_probe(dev, ret,
				      "Unable to get port %u PoC regulator\n",
				      index);
			return ret;
		}
	}

	return 0;
}

static int max9286_probe(struct i2c_client *client)
{
	struct max9286_priv *priv;
@@ -1330,6 +1410,9 @@ static int max9286_probe(struct i2c_client *client)

	priv->client = client;

	/* GPIO values default to high */
	priv->gpio_state = BIT(0) | BIT(1);

	priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
						   GPIOD_OUT_HIGH);
	if (IS_ERR(priv->gpiod_pwdn))
@@ -1362,7 +1445,13 @@ static int max9286_probe(struct i2c_client *client)

	ret = max9286_parse_dt(priv);
	if (ret)
		goto err_powerdown;
		goto err_cleanup_dt;

	if (!priv->use_gpio_poc) {
		ret = max9286_get_poc_supplies(priv);
		if (ret)
			goto err_cleanup_dt;
	}

	ret = max9286_init(priv);
	if (ret < 0)