From patchwork Sun Nov 28 17:18:07 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alberto Panizzo X-Patchwork-Id: 362222 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oASHImS9022132 for ; Sun, 28 Nov 2010 17:18:48 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753626Ab0K1RSQ (ORCPT ); Sun, 28 Nov 2010 12:18:16 -0500 Received: from mail-wy0-f174.google.com ([74.125.82.174]:52222 "EHLO mail-wy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753555Ab0K1RSP (ORCPT ); Sun, 28 Nov 2010 12:18:15 -0500 Received: by wyb28 with SMTP id 28so3608384wyb.19 for ; Sun, 28 Nov 2010 09:18:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:subject:from:to:cc :content-type:date:message-id:mime-version:x-mailer :content-transfer-encoding; bh=uYaBkkPcE/LePqQWm+7j0Wp9L0UtIc33EBOo8ZS8w5M=; b=hGlG+Tsau+lg4nvifi3AAQxWbBVkWuxWZG+GI1mR7ZkQdaMPInG28DQZd/aIwivQ0q un4OdWl1XA+fLnByQIR2ZjDFsnWYm2OE1IYgp3wempGYYQHCThCoCczyt/C41iAsgv3t ModM76FP2Fp6ipvVKs62Wp+23x2C0YKrJu+Z4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=subject:from:to:cc:content-type:date:message-id:mime-version :x-mailer:content-transfer-encoding; b=btTXeOskEVpNyZn1fQeju37zCopc3r2zxL09bjJNwzk3lkrCgaMzf7eTwRUH/GoeHI JcVuGTznoZtEo6k0GlugimTFiweuISwplDke09RW+Alm+L8u+2TIWDcCEJ9I8iuzs3cK w0r9xkol2ZBiW3n1r72JVXc8XkOxvOCkJwZAI= Received: by 10.227.145.70 with SMTP id c6mr4790359wbv.106.1290964692974; Sun, 28 Nov 2010 09:18:12 -0800 (PST) Received: from [192.168.1.101] (host-9473-86-114.popwifi.it [94.73.86.114]) by mx.google.com with ESMTPS id 11sm2867087wbi.18.2010.11.28.09.18.09 (version=SSLv3 cipher=RC4-MD5); Sun, 28 Nov 2010 09:18:11 -0800 (PST) Subject: [PATCH 1/3] soc_camera: Add the ability to bind regulators to soc_camedra devices From: Alberto Panizzo To: Mauro Carvalho Chehab Cc: Guennadi Liakhovetski , Hans Verkuil , Laurent Pinchart , Magnus Damm , =?ISO-8859-1?Q?M=E1rton_N=E9meth?= , linux-media@vger.kernel.org, linux-kernel Date: Sun, 28 Nov 2010 18:18:07 +0100 Message-ID: <1290964687.3016.5.camel@realization> Mime-Version: 1.0 X-Mailer: Evolution 2.28.1 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Sun, 28 Nov 2010 17:18:48 +0000 (UTC) diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 43848a7..8fc5831 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -43,6 +43,96 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ +static int soc_camera_setup_regulators(struct soc_camera_device *icd, + struct soc_camera_link *icl) +{ + int i, ret; + + icd->soc_regulators = kzalloc(icl->num_soc_regulator_descs * + sizeof(struct regulator *), GFP_KERNEL); + if (!icd->soc_regulators) { + dev_err(icd->pdev, "Not enough memory.\n"); + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < icl->num_soc_regulator_descs; i++) { + dev_dbg(icd->pdev, "Looking for reg:'%s' bound to dev:'%s'", + icl->soc_regulator_descs[i].supply, + dev_name(icd->pdev)); + icd->soc_regulators[i] = regulator_get(icd->pdev, + icl->soc_regulator_descs[i].supply); + if (IS_ERR(icd->soc_regulators[i])) { + icd->soc_regulators[i] = NULL; + dev_err(icd->pdev, "Unable to get regulator: \"%s\".\n", + icl->soc_regulator_descs[i].supply); + ret = -ENODEV; + goto free_regs; + } + } + + icd->num_soc_regulators = icl->num_soc_regulator_descs; + + return 0; + +free_regs: + for (i--; i >= 0; i--) + regulator_put(icd->soc_regulators[i]); +err: + return ret; +} + +static int soc_camera_power_set(struct soc_camera_device *icd, + struct soc_camera_link *icl, + int power_on) +{ + int ret, i; + + for (i = 0; i < icd->num_soc_regulators; i++) { + if (power_on) { + ret = regulator_set_voltage(icd->soc_regulators[i], + icl->soc_regulator_descs[i].value_on_min, + icl->soc_regulator_descs[i].value_on_max); + if (ret) { + dev_err(icd->pdev, "Cannot set '%s' to %d:%d", + icl->soc_regulator_descs[i].supply, + icl->soc_regulator_descs[i].value_on_min, + icl->soc_regulator_descs[i].value_on_max); + goto err; + } + + ret = regulator_enable(icd->soc_regulators[i]); + if (ret < 0) { + dev_err(icd->pdev, "Cannot enable reg '%s'", + icl->soc_regulator_descs[i].supply); + goto err; + } + } else { + ret = regulator_disable(icd->soc_regulators[i]); + if (ret) { + dev_err(icd->pdev, "Cannot disable reg '%s'", + icl->soc_regulator_descs[i].supply); + goto err; + } + } + } + + if (icl->power) { + ret = icl->power(icd->pdev, power_on); + if (ret < 0) { + dev_err(icd->pdev, + "Platform failed to power-%s the camera.\n", + power_on ? "ON" : "OFF"); + goto err; + } + } + + return 0; + +err: + return ret; +} + const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) { @@ -375,11 +465,9 @@ static int soc_camera_open(struct file *file) }, }; - if (icl->power) { - ret = icl->power(icd->pdev, 1); - if (ret < 0) - goto epower; - } + ret = soc_camera_power_set(icd, icl, 1); + if (ret < 0) + goto epower; /* The camera could have been already on, try to reset */ if (icl->reset) @@ -425,8 +513,7 @@ esfmt: eresume: ici->ops->remove(icd); eiciadd: - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); epower: icd->use_count--; mutex_unlock(&icd->video_lock); @@ -450,8 +537,7 @@ static int soc_camera_close(struct file *file) ici->ops->remove(icd); - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); } if (icd->streamer == file) @@ -937,18 +1023,18 @@ static int soc_camera_probe(struct device *dev) struct device *control = NULL; struct v4l2_subdev *sd; struct v4l2_mbus_framefmt mf; - int ret; + int ret = 0, i; dev_info(dev, "Probing %s\n", dev_name(dev)); - if (icl->power) { - ret = icl->power(icd->pdev, 1); - if (ret < 0) { - dev_err(dev, - "Platform failed to power-on the camera.\n"); - goto epower; - } - } + if (icl->num_soc_regulator_descs) + ret = soc_camera_setup_regulators(icd, icl); + if (ret) + goto err; + + ret = soc_camera_power_set(icd, icl, 1); + if (ret < 0) + goto epower; /* The camera could have been already on, try to reset */ if (icl->reset) @@ -1021,8 +1107,7 @@ static int soc_camera_probe(struct device *dev) ici->ops->remove(icd); - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); mutex_unlock(&icd->video_lock); @@ -1044,9 +1129,11 @@ eadddev: evdc: ici->ops->remove(icd); eadd: - if (icl->power) - icl->power(icd->pdev, 0); + soc_camera_power_set(icd, icl, 0); epower: + for (i = icd->num_soc_regulators; i >= 0; i--) + regulator_put(icd->soc_regulators[i]); +err: return ret; } @@ -1059,6 +1146,7 @@ static int soc_camera_remove(struct device *dev) struct soc_camera_device *icd = to_soc_camera_dev(dev); struct soc_camera_link *icl = to_soc_camera_link(icd); struct video_device *vdev = icd->vdev; + int i; BUG_ON(!dev->parent); @@ -1081,6 +1169,9 @@ static int soc_camera_remove(struct device *dev) } soc_camera_free_user_formats(icd); + for (i = icd->num_soc_regulators; i >= 0; i--) + regulator_put(icd->soc_regulators[i]); + return 0; } diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 86e3631..ae589a4 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,8 @@ struct soc_camera_device { struct mutex video_lock; /* Protects device data */ struct file *streamer; /* stream owner */ struct videobuf_queue vb_vidq; + struct regulator **soc_regulators; + int num_soc_regulators; }; struct soc_camera_host { @@ -96,6 +99,15 @@ struct soc_camera_host_ops { #define SOCAM_SENSOR_INVERT_VSYNC (1 << 3) #define SOCAM_SENSOR_INVERT_DATA (1 << 4) +struct soc_camera_regulator_desc { + const char *supply; + int value_on_min; + int value_on_max; +}; + +#define SOCAM_REG_DESC(s, min, max) \ + { .supply = s , .value_on_min = min , .value_on_max = max } + struct i2c_board_info; struct soc_camera_link { @@ -108,6 +120,10 @@ struct soc_camera_link { const char *module_name; void *priv; + /* Optional regulators that have to be managed on power on/off events */ + struct soc_camera_regulator_desc *soc_regulator_descs; + int num_soc_regulator_descs; + /* * For non-I2C devices platform platform has to provide methods to * add a device to the system and to remove