diff options
-rw-r--r-- | arch/arm/mach-ux500/board-mop500-mcde.c | 8 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500-mcde.h | 24 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500-regulators.c | 17 | ||||
-rw-r--r-- | drivers/mfd/ab8500-denc.c | 25 | ||||
-rw-r--r-- | drivers/video/mcde/display-ab8500.c | 86 | ||||
-rw-r--r-- | drivers/video/mcde/display-av8100.c | 55 | ||||
-rw-r--r-- | include/video/mcde_display-ab8500.h | 3 | ||||
-rw-r--r-- | include/video/mcde_display-av8100.h | 1 |
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; |