summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@linaro.org>2011-05-04 17:19:21 +0200
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:14:56 +0200
commitbf7498b0bc89cf33e67a7273d05642966d4e8dae (patch)
tree9bffebcba949c7e4ec710f01559c94fc72b9ec30
parent9036aa0407e86df7995c9627c53661dd12882db1 (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
-rw-r--r--arch/arm/mach-ux500/board-mop500-mcde.c8
-rw-r--r--arch/arm/mach-ux500/board-mop500-mcde.h24
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.c17
-rw-r--r--drivers/mfd/ab8500-denc.c25
-rw-r--r--drivers/video/mcde/display-ab8500.c86
-rw-r--r--drivers/video/mcde/display-av8100.c55
-rw-r--r--include/video/mcde_display-ab8500.h3
-rw-r--r--include/video/mcde_display-av8100.h1
8 files changed, 191 insertions, 28 deletions
diff --git a/arch/arm/mach-ux500/board-mop500-mcde.c b/arch/arm/mach-ux500/board-mop500-mcde.c
index ca10a7e5b92..5d8f0f9058b 100644
--- a/arch/arm/mach-ux500/board-mop500-mcde.c
+++ b/arch/arm/mach-ux500/board-mop500-mcde.c
@@ -270,7 +270,8 @@ static struct mcde_port port_tvout1 = {
};
static struct ab8500_display_platform_data ab8500_display_pdata = {
- .denc_regulator_id = "v-tvout",
+ .nr_regulators = 2,
+ .regulator_id = {"v-tvout", "v-ab8500-AV-switch"},
.rgb_2_yCbCr_transform = {
.matrix = {
{0x42, 0x81, 0x19},
@@ -321,7 +322,7 @@ failed:
return res;
}
-static struct mcde_display_device tvout_ab8500_display = {
+struct mcde_display_device tvout_ab8500_display = {
.name = "mcde_tv_ab8500",
.id = TERTIARY_DISPLAY_ID,
.port = &port_tvout1,
@@ -381,6 +382,7 @@ struct mcde_display_hdmi_platform_data av8100_hdmi_pdata = {
.reset_gpio = 0,
.reset_delay = 1,
.regulator_id = NULL, /* TODO: "display_main" */
+ .cvbs_regulator_id = "v-av8100-AV-switch",
.ddb_id = 1,
.rgb_2_yCbCr_transform = {
.matrix = {
@@ -392,7 +394,7 @@ struct mcde_display_hdmi_platform_data av8100_hdmi_pdata = {
}
};
-static struct mcde_display_device av8100_hdmi = {
+struct mcde_display_device av8100_hdmi = {
.name = "av8100_hdmi",
.id = TERTIARY_DISPLAY_ID,
.port = &port2,
diff --git a/arch/arm/mach-ux500/board-mop500-mcde.h b/arch/arm/mach-ux500/board-mop500-mcde.h
new file mode 100644
index 00000000000..38094b81b34
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-mcde.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Marcel Tunnissen <marcel.tuennissen@stericsson.com> for ST-Ericsson
+ *
+ * MOP500 board specific initialization for regulators
+ */
+
+#ifndef __BOARD_MOP500_MCDE_H
+#define __BOARD_MOP500_MCDE_H
+
+#include <video/mcde_display.h>
+
+#ifdef CONFIG_DISPLAY_AB8500_TERTIARY
+extern struct mcde_display_device tvout_ab8500_display;
+#endif
+
+#ifdef CONFIG_DISPLAY_AV8100_TERTIARY
+extern struct mcde_display_device av8100_hdmi;
+#endif
+
+#endif /* __BOARD_MOP500_MCDE_H */
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
index bdc01c0a3c5..44533ee280e 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.c
+++ b/arch/arm/mach-ux500/board-mop500-regulators.c
@@ -12,12 +12,20 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
#include "board-mop500-regulators.h"
+#include "board-mop500-mcde.h"
#ifdef CONFIG_U8500_REGULATOR_DEBUG
#define REGULATOR_SUPPLY_DEBUG REGULATOR_SUPPLY
#else
#define REGULATOR_SUPPLY_DEBUG(_name, _dev_name)
#endif
+
+#define REGULATOR_SUPPLY_DEV(_name, _dev) \
+{ \
+ .supply = _name, \
+ .dev = _dev, \
+}
+
/*
* TPS61052 regulator
*/
@@ -69,6 +77,12 @@ static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
/* AB8500 audio codec */
REGULATOR_SUPPLY("vcc-avswitch", "ab8500-codec.0"),
REGULATOR_SUPPLY("vcc-avswitch", "ab8500-acc-det.0"),
+#ifdef CONFIG_DISPLAY_AB8500_TERTIARY
+ REGULATOR_SUPPLY_DEV("v-ab8500-AV-switch", &tvout_ab8500_display.dev),
+#endif
+#ifdef CONFIG_DISPLAY_AV8100_TERTIARY
+ REGULATOR_SUPPLY_DEV("v-av8100-AV-switch", &av8100_hdmi.dev),
+#endif
REGULATOR_SUPPLY_DEBUG("aux2", "reg-virt-consumer.1")
};
@@ -91,6 +105,9 @@ static struct regulator_consumer_supply ab8500_vtvout_consumers[] = {
REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"),
/* Internal general-purpose ADC */
REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"),
+#ifdef CONFIG_DISPLAY_AB8500_TERTIARY
+ REGULATOR_SUPPLY_DEV("vtvout", &tvout_ab8500_display.dev),
+#endif
REGULATOR_SUPPLY_DEBUG("tvout", "reg-virt-consumer.4")
};
diff --git a/drivers/mfd/ab8500-denc.c b/drivers/mfd/ab8500-denc.c
index 06e4b282cbf..1bf61e41bcd 100644
--- a/drivers/mfd/ab8500-denc.c
+++ b/drivers/mfd/ab8500-denc.c
@@ -434,6 +434,20 @@ static int debugfs_ab8500_open_file(struct inode *inode, struct file *file)
#define DEBUG_BUF_SIZE 900
+#define AB8500_GPIO_DIR5 0x1014
+#define AB8500_GPIO_DIR5_35_SHIFT 2
+#define AB8500_GPIO_DIR5_35_MASK (1 << AB8500_GPIO_DIR5_35_SHIFT)
+#define AB8500_GPIO_OUT5 0x1024
+#define AB8500_GPIO_OUT5_35_SHIFT 2
+#define AB8500_GPIO_OUT5_35_MASK (1 << AB8500_GPIO_OUT5_35_SHIFT)
+#define AB8500_GPIO_OUT5_35_VIDEO 0
+#define AB8500_GPIO_OUT5_35_AUDIO 1
+#define AB8500_GPIO_NPUD5 0x1034
+#define AB8500_GPIO_NPUD5_35_SHIFT 2
+#define AB8500_GPIO_NPUD5_35_MASK (1 << AB8500_GPIO_NPUD5_35_SHIFT)
+#define AB8500_GPIO_NPUD5_35_ACTIVE 0
+#define AB8500_GPIO_NPUD5_35_INACTIVE 1
+
static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
size_t count, loff_t *f_pos)
{
@@ -444,12 +458,14 @@ static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
data_size += sprintf(buffer + data_size,
"AB8500 DENC registers:\n"
+ "------Regulators etc ----------\n"
"CTRL3 : 0x%04x = 0x%02x\n"
"SYSULPCLK_CONF: 0x%04x = 0x%02x\n"
"SYSCLK_CTRL : 0x%04x = 0x%02x\n"
"REGU_MISC1 : 0x%04x = 0x%02x\n"
"VAUX12_REGU : 0x%04x = 0x%02x\n"
"VAUX1_SEL1 : 0x%04x = 0x%02x\n"
+ "------TVout only --------------\n"
"DENC_CONF0 : 0x%04x = 0x%02x\n"
"DENC_CONF1 : 0x%04x = 0x%02x\n"
"DENC_CONF2 : 0x%04x = 0x%02x\n"
@@ -458,6 +474,10 @@ static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
"TVOUT_CTRL : 0x%04x = 0x%02x\n"
"TVOUT_CTRL2 : 0x%04x = 0x%02x\n"
"IT_MASK1 : 0x%04x = 0x%02x\n"
+ "------AV connector-------------\n"
+ "GPIO_DIR5 : 0x%04x = 0x%02x\n"
+ "GPIO_OUT5 : 0x%04x = 0x%02x\n"
+ "GPIO_NPUD5 : 0x%04x = 0x%02x\n"
,
AB8500_CTRL3, ab8500_rreg(dev, AB8500_CTRL3),
AB8500_SYS_ULP_CLK_CONF, ab8500_rreg(dev,
@@ -473,7 +493,10 @@ static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
AB8500_DENC_CONF8, ab8500_rreg(dev, AB8500_DENC_CONF8),
AB8500_TVOUT_CTRL, ab8500_rreg(dev, AB8500_TVOUT_CTRL),
AB8500_TVOUT_CTRL2, ab8500_rreg(dev, AB8500_TVOUT_CTRL2),
- AB8500_IT_MASK1, ab8500_rreg(dev, AB8500_IT_MASK1)
+ AB8500_IT_MASK1, ab8500_rreg(dev, AB8500_IT_MASK1),
+ AB8500_GPIO_DIR5, ab8500_rreg(dev, AB8500_GPIO_DIR5),
+ AB8500_GPIO_OUT5, ab8500_rreg(dev, AB8500_GPIO_OUT5),
+ AB8500_GPIO_NPUD5, ab8500_rreg(dev, AB8500_GPIO_NPUD5)
);
if (data_size >= DEBUG_BUF_SIZE) {
printk(KERN_EMERG "AB8500 DENC: Buffer overrun\n");
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);
diff --git a/include/video/mcde_display-ab8500.h b/include/video/mcde_display-ab8500.h
index ce0633313f1..336fe441f42 100644
--- a/include/video/mcde_display-ab8500.h
+++ b/include/video/mcde_display-ab8500.h
@@ -15,8 +15,9 @@
struct ab8500_display_platform_data {
/* Platform info */
- const char *denc_regulator_id;
struct mcde_col_convert rgb_2_yCbCr_transform;
+ int nr_regulators;
+ const char *regulator_id[];
};
#endif /* __DISPLAY_AB8500__H__*/
diff --git a/include/video/mcde_display-av8100.h b/include/video/mcde_display-av8100.h
index 79e806a8391..c508ebf1f17 100644
--- a/include/video/mcde_display-av8100.h
+++ b/include/video/mcde_display-av8100.h
@@ -26,6 +26,7 @@ struct mcde_display_hdmi_platform_data {
int reset_gpio;
bool reset_high;
const char *regulator_id;
+ const char *cvbs_regulator_id;
int reset_delay; /* ms */
u32 ddb_id;
struct mcde_col_convert rgb_2_yCbCr_transform;