aboutsummaryrefslogtreecommitdiff
path: root/drivers/video/mcde
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@linaro.org>2011-05-04 17:19:21 +0200
committerPhilippe Langlais <philippe.langlais@linaro.org>2011-07-22 15:42:04 +0200
commite075f965ffeeee86221f88c88b0c43fb1c5b7a7d (patch)
tree431a2e90ed4091c17e7fb9ab28888a796638ab6d /drivers/video/mcde
parentf9fb4cbd4bfb4b9b0d3e7c07e0802187803e9786 (diff)
drivers: video: add regulator for CVBS out
- Adds a regulator that is used for CVBS TV out by AB8500 DENC and AV8100 DENC. The regulator is for the switch at the AV connector that switches between video out and mic in. ST-Ericsson ID: AP 322391 Linux-next: ST-Ericsson ID: ER 282779 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie7a821b8d3965aa65384b4393e3083ef406c8282 Signed-off-by: Marcel Tunnissen <Marcel.Tuennissen@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/16819 Tested-by: Marcel TUNNISSEN <marcel.tuennissen@stericsson.com> Reviewed-by: Per PERSSON <per.xb.persson@stericsson.com> Reviewed-by: Robert FEKETE <robert.fekete@stericsson.com> Conflicts: arch/arm/mach-ux500/board-mop500-regulators.c arch/arm/mach-ux500/board-pdp-mcde.c
Diffstat (limited to 'drivers/video/mcde')
-rw-r--r--drivers/video/mcde/display-ab8500.c86
-rw-r--r--drivers/video/mcde/display-av8100.c55
2 files changed, 118 insertions, 23 deletions
diff --git a/drivers/video/mcde/display-ab8500.c b/drivers/video/mcde/display-ab8500.c
index fa6b3208781..538a0e2346d 100644
--- a/drivers/video/mcde/display-ab8500.c
+++ b/drivers/video/mcde/display-ab8500.c
@@ -23,7 +23,8 @@
struct display_driver_data {
struct ab8500_denc_conf denc_conf;
struct platform_device *denc_dev;
- struct regulator *denc_regulator;
+ int nr_regulators;
+ struct regulator **regulator;
};
static int try_video_mode(struct mcde_display_device *ddev,
@@ -38,6 +39,7 @@ static int display_update(struct mcde_display_device *ddev);
static int __devinit ab8500_probe(struct mcde_display_device *ddev)
{
int ret = 0;
+ int i;
struct ab8500_display_platform_data *pdata = ddev->dev.platform_data;
struct display_driver_data *driver_data;
@@ -66,17 +68,24 @@ static int __devinit ab8500_probe(struct mcde_display_device *ddev)
goto dev_get_failed;
}
- if (pdata->denc_regulator_id) {
- driver_data->denc_regulator = regulator_get(&ddev->dev,
- pdata->denc_regulator_id);
- if (IS_ERR(driver_data->denc_regulator)) {
- ret = PTR_ERR(driver_data->denc_regulator);
+ driver_data->regulator = kzalloc(pdata->nr_regulators *
+ sizeof(struct regulator *), GFP_KERNEL);
+ if (!driver_data->regulator) {
+ dev_err(&ddev->dev, "Failed to allocate regulator list\n");
+ ret = -ENOMEM;
+ goto reg_alloc_failed;
+ }
+ for (i = 0; i < pdata->nr_regulators; i++) {
+ driver_data->regulator[i] = regulator_get(&ddev->dev,
+ pdata->regulator_id[i]);
+ if (IS_ERR(driver_data->regulator[i])) {
+ ret = PTR_ERR(driver_data->regulator[i]);
dev_warn(&ddev->dev, "%s:Failed to get regulator %s\n",
- __func__, pdata->denc_regulator_id);
- driver_data->denc_regulator = NULL;
+ __func__, pdata->regulator_id[i]);
goto regulator_get_failed;
}
}
+ driver_data->nr_regulators = pdata->nr_regulators;
dev_set_drvdata(&ddev->dev, driver_data);
@@ -90,9 +99,14 @@ static int __devinit ab8500_probe(struct mcde_display_device *ddev)
return 0;
regulator_get_failed:
+ for (i--; i >= 0; i--)
+ regulator_put(driver_data->regulator[i]);
+ kfree(driver_data->regulator);
+ driver_data->regulator = NULL;
+reg_alloc_failed:
ab8500_denc_put_device(driver_data->denc_dev);
dev_get_failed:
- kzfree(driver_data);
+ kfree(driver_data);
return ret;
}
@@ -103,10 +117,16 @@ static int __devexit ab8500_remove(struct mcde_display_device *ddev)
ddev->set_power_mode(ddev, MCDE_DISPLAY_PM_OFF);
- if (driver_data->denc_regulator)
- regulator_put(driver_data->denc_regulator);
+ if (driver_data->regulator) {
+ int i;
+ for (i = driver_data->nr_regulators - 1; i >= 0; i--)
+ regulator_put(driver_data->regulator[i]);
+ kfree(driver_data->regulator);
+ driver_data->regulator = NULL;
+ driver_data->nr_regulators = 0;
+ }
ab8500_denc_put_device(driver_data->denc_dev);
- kzfree(driver_data);
+ kfree(driver_data);
return 0;
}
@@ -306,6 +326,7 @@ static int set_power_mode(struct mcde_display_device *ddev,
enum mcde_display_power_mode power_mode)
{
int ret;
+ int i;
struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
AB8500_DISP_TRACE;
@@ -318,10 +339,14 @@ static int set_power_mode(struct mcde_display_device *ddev,
if (ret)
goto error;
}
- if (driver_data->denc_regulator) {
- ret = regulator_enable(driver_data->denc_regulator);
- if (ret != 0)
- goto error;
+ if (driver_data->regulator) {
+ for (i = 0; i < driver_data->nr_regulators; i++) {
+ ret = regulator_enable(
+ driver_data->regulator[i]);
+ if (ret)
+ goto off_to_standby_failed;
+ dev_dbg(&ddev->dev, "regulator %d on\n", i);
+ }
}
ab8500_denc_power_up(driver_data->denc_dev);
ab8500_denc_reset(driver_data->denc_dev, true);
@@ -343,23 +368,38 @@ static int set_power_mode(struct mcde_display_device *ddev,
/* STANDBY -> OFF */
if (ddev->power_mode == MCDE_DISPLAY_PM_STANDBY &&
power_mode == MCDE_DISPLAY_PM_OFF) {
+ bool error = false;
dev_dbg(&ddev->dev, "standby -> off\n");
- memset(&(ddev->video_mode), 0, sizeof(struct mcde_video_mode));
- if (driver_data->denc_regulator) {
- ret = regulator_disable(driver_data->denc_regulator);
- if (ret != 0)
- goto error;
+ if (driver_data->regulator) {
+ for (i = 0; i < driver_data->nr_regulators; i++) {
+ ret = regulator_disable(
+ driver_data->regulator[i]);
+ /* continue in case of an error */
+ error |= (ret != 0);
+ dev_dbg(&ddev->dev, "regulator %d off\n", i);
+ }
}
if (ddev->platform_disable) {
ret = ddev->platform_disable(ddev);
- if (ret)
- goto error;
+ error |= (ret != 0);
+ }
+ if (error) {
+ /* the latest error code is returned */
+ goto error;
}
+ memset(&(ddev->video_mode), 0, sizeof(struct mcde_video_mode));
ab8500_denc_power_down(driver_data->denc_dev);
ddev->power_mode = MCDE_DISPLAY_PM_OFF;
}
return 0;
+
+ /* In case of an error, try to leave in off-state */
+off_to_standby_failed:
+ for (i--; i >= 0; i--)
+ regulator_disable(driver_data->regulator[i]);
+ ddev->platform_disable(ddev);
+
error:
dev_err(&ddev->dev, "Failed to set power mode");
return ret;
diff --git a/drivers/video/mcde/display-av8100.c b/drivers/video/mcde/display-av8100.c
index 009ff6e9696..567a7d0d1f4 100644
--- a/drivers/video/mcde/display-av8100.c
+++ b/drivers/video/mcde/display-av8100.c
@@ -12,14 +12,21 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <video/mcde_display.h>
#include <video/mcde_display-av8100.h>
#include <video/av8100.h>
#include <video/hdmi.h>
+struct display_driver_data {
+ struct regulator *cvbs_regulator;
+ bool cvbs_regulator_enabled;
+};
+
static int hdmi_try_video_mode(
struct mcde_display_device *ddev, struct mcde_video_mode *video_mode);
static int hdmi_set_video_mode(
@@ -734,6 +741,7 @@ static int hdmi_on_first_update(struct mcde_display_device *dev)
static int hdmi_set_power_mode(struct mcde_display_device *ddev,
enum mcde_display_power_mode power_mode)
{
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
int ret = 0;
/* OFF -> STANDBY */
@@ -744,6 +752,17 @@ static int hdmi_set_power_mode(struct mcde_display_device *ddev,
if (ret)
return ret;
}
+ /*
+ * the regulator for analog TV out is only enabled here,
+ * this means that one needs to switch to the OFF state
+ * to be able to switch from HDMI to CVBS.
+ */
+ if (ddev->port->hdmi_sdtv_switch == SDTV_SWITCH) {
+ ret = regulator_enable(driver_data->cvbs_regulator);
+ if (ret)
+ return ret;
+ driver_data->cvbs_regulator_enabled = true;
+ }
ddev->power_mode = MCDE_DISPLAY_PM_STANDBY;
}
/* STANDBY -> ON */
@@ -771,6 +790,12 @@ static int hdmi_set_power_mode(struct mcde_display_device *ddev,
if (ret)
return ret;
}
+ if (driver_data->cvbs_regulator_enabled) {
+ ret = regulator_disable(driver_data->cvbs_regulator);
+ if (ret)
+ return ret;
+ driver_data->cvbs_regulator_enabled = false;
+ }
ddev->power_mode = MCDE_DISPLAY_PM_OFF;
}
@@ -782,6 +807,8 @@ set_power_and_exit:
static int __devinit hdmi_probe(struct mcde_display_device *dev)
{
+ int ret = 0;
+ struct display_driver_data *driver_data;
struct mcde_display_hdmi_platform_data *pdata =
dev->dev.platform_data;
@@ -796,6 +823,13 @@ static int __devinit hdmi_probe(struct mcde_display_device *dev)
return -EINVAL;
}
+ driver_data = (struct display_driver_data *)
+ kzalloc(sizeof(struct display_driver_data), GFP_KERNEL);
+ if (!driver_data) {
+ dev_err(&dev->dev, "Failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
/* DSI use clock continous mode if AV8100_CHIPVER_1 > 1 */
if (av8100_ver_get() > AV8100_CHIPVER_1)
dev->port->phy.dsi.clk_cont = true;
@@ -813,13 +847,31 @@ static int __devinit hdmi_probe(struct mcde_display_device *dev)
dev_info(&dev->dev,
"Unable to create hdmisdtvswitch attr\n");
+ if (pdata->cvbs_regulator_id) {
+ driver_data->cvbs_regulator = regulator_get(&dev->dev,
+ pdata->cvbs_regulator_id);
+ if (IS_ERR(driver_data->cvbs_regulator)) {
+ ret = PTR_ERR(driver_data->cvbs_regulator);
+ dev_warn(&dev->dev, "%s:Failed to get regulator %s\n",
+ __func__, pdata->cvbs_regulator_id);
+ driver_data->cvbs_regulator = NULL;
+ goto av_regulator_get_failed;
+ }
+ }
+
+ dev_set_drvdata(&dev->dev, driver_data);
dev_info(&dev->dev, "HDMI display probed\n");
return 0;
+
+av_regulator_get_failed:
+ kfree(driver_data);
+ return ret;
}
static int __devexit hdmi_remove(struct mcde_display_device *dev)
{
+ struct display_driver_data *driver_data = dev_get_drvdata(&dev->dev);
struct mcde_display_hdmi_platform_data *pdata =
dev->dev.platform_data;
@@ -828,6 +880,9 @@ static int __devexit hdmi_remove(struct mcde_display_device *dev)
dev->set_power_mode(dev, MCDE_DISPLAY_PM_OFF);
+ if (driver_data->cvbs_regulator)
+ regulator_put(driver_data->cvbs_regulator);
+ kfree(driver_data);
if (pdata->hdmi_platform_enable) {
if (pdata->regulator)
regulator_put(pdata->regulator);