summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;