diff options
Diffstat (limited to 'drivers/media/i2c/max9286.c')
-rw-r--r-- | drivers/media/i2c/max9286.c | 125 |
1 files changed, 95 insertions, 30 deletions
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index eb2b8e42335b..d2a4915ed9f7 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -15,6 +15,7 @@ #include <linux/fwnode.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/module.h> @@ -168,6 +169,8 @@ struct max9286_priv { u32 init_rev_chan_mv; u32 rev_chan_mv; + u32 gpio_poc[2]; + struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *pixelrate; @@ -846,6 +849,10 @@ static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { .open = max9286_open, }; +static const struct media_entity_operations max9286_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + static int max9286_s_ctrl(struct v4l2_ctrl *ctrl) { switch (ctrl->id) { @@ -895,6 +902,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv) goto err_async; priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &max9286_media_ops; priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE; for (i = 0; i < MAX9286_SRC_PAD; i++) @@ -1025,20 +1033,27 @@ static int max9286_setup(struct max9286_priv *priv) return 0; } -static void max9286_gpio_set(struct gpio_chip *chip, - unsigned int offset, int value) +static int max9286_gpio_set(struct max9286_priv *priv, unsigned int offset, + int value) { - struct max9286_priv *priv = gpiochip_get_data(chip); - if (value) priv->gpio_state |= BIT(offset); else priv->gpio_state &= ~BIT(offset); - max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state); + return max9286_write(priv, 0x0f, + MAX9286_0X0F_RESERVED | priv->gpio_state); +} + +static void max9286_gpiochip_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct max9286_priv *priv = gpiochip_get_data(chip); + + max9286_gpio_set(priv, offset, value); } -static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset) +static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset) { struct max9286_priv *priv = gpiochip_get_data(chip); @@ -1057,13 +1072,10 @@ static int max9286_register_gpio(struct max9286_priv *priv) gpio->owner = THIS_MODULE; gpio->ngpio = 2; gpio->base = -1; - gpio->set = max9286_gpio_set; - gpio->get = max9286_gpio_get; + gpio->set = max9286_gpiochip_set; + gpio->get = max9286_gpiochip_get; gpio->can_sleep = true; - /* GPIO values default to high */ - priv->gpio_state = BIT(0) | BIT(1); - ret = devm_gpiochip_add_data(dev, gpio, priv); if (ret) dev_err(dev, "Unable to create gpio_chip\n"); @@ -1071,6 +1083,70 @@ static int max9286_register_gpio(struct max9286_priv *priv) return ret; } +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. + */ + ret = of_property_read_u32_array(dev->of_node, "maxim,gpio-poc", + priv->gpio_poc, 2); + if (ret == -EINVAL) { + /* + * 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; + } + + /* If the property is specified make sure it is well formed. */ + if (ret || priv->gpio_poc[0] > 1 || + (priv->gpio_poc[1] != GPIO_ACTIVE_HIGH && + priv->gpio_poc[1] != GPIO_ACTIVE_LOW)) { + dev_err(dev, "Invalid 'gpio-poc' property\n"); + return -EINVAL; + } + + return 0; +} + +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); + else + ret = regulator_disable(priv->regulator); + + if (ret < 0) + dev_err(&priv->client->dev, "Unable to turn power %s\n", + enable ? "on" : "off"); + + return ret; +} + static int max9286_init(struct device *dev) { struct max9286_priv *priv; @@ -1080,17 +1156,14 @@ static int max9286_init(struct device *dev) client = to_i2c_client(dev); priv = i2c_get_clientdata(client); - /* Enable the bus power. */ - ret = regulator_enable(priv->regulator); - if (ret < 0) { - dev_err(&client->dev, "Unable to turn PoC on\n"); + ret = max9286_poc_enable(priv, true); + if (ret) return ret; - } ret = max9286_setup(priv); if (ret) { dev_err(dev, "Unable to setup max9286\n"); - goto err_regulator; + goto err_poc_disable; } /* @@ -1100,7 +1173,7 @@ static int max9286_init(struct device *dev) ret = max9286_v4l2_register(priv); if (ret) { dev_err(dev, "Failed to register with V4L2\n"); - goto err_regulator; + goto err_poc_disable; } ret = max9286_i2c_mux_init(priv); @@ -1116,8 +1189,8 @@ static int max9286_init(struct device *dev) err_v4l2_register: max9286_v4l2_unregister(priv); -err_regulator: - regulator_disable(priv->regulator); +err_poc_disable: + max9286_poc_enable(priv, false); return ret; } @@ -1288,18 +1361,10 @@ static int max9286_probe(struct i2c_client *client) */ max9286_configure_i2c(priv, false); - ret = max9286_register_gpio(priv); + ret = max9286_parse_gpios(priv); if (ret) goto err_powerdown; - priv->regulator = devm_regulator_get(&client->dev, "poc"); - if (IS_ERR(priv->regulator)) { - ret = PTR_ERR(priv->regulator); - dev_err_probe(&client->dev, ret, - "Unable to get PoC regulator\n"); - goto err_powerdown; - } - ret = max9286_parse_dt(priv); if (ret) goto err_powerdown; @@ -1326,7 +1391,7 @@ static int max9286_remove(struct i2c_client *client) max9286_v4l2_unregister(priv); - regulator_disable(priv->regulator); + max9286_poc_enable(priv, false); gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); |