summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilippe Langlais <philippe.langlais@linaro.org>2011-05-10 16:12:51 +0200
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:03:53 +0200
commitc58603900d889c15604a604ca03fb5b4af7fb7f2 (patch)
tree23555a669f53c21777fa3ca3a16c154ecdc40126
parent6eb1637d89faefd90837acb5e9e368b9fae65ef1 (diff)
video: mcde_hdmi: Support for HDMI user space service
Add changes needed by HDMI service in user space. HDMI service is a user space service that provide functionality for applications using HDMI. ST-Ericsson ID: 335747 ST-Ericsson Linux next: Not tested, ER 282779 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I7c2ffd5ae61310f9a152ca984c4f62152fc1e2e0 Signed-off-by: Per Persson <per.xb.persson@stericsson.com> Conflicts: arch/arm/mach-ux500/board-mop500-mcde.c drivers/misc/dispdev/dispdev.c include/linux/dispdev.h
-rw-r--r--arch/arm/mach-ux500/board-mop500-mcde.c148
-rw-r--r--drivers/video/av8100/av8100.c21
-rw-r--r--drivers/video/av8100/hdmi.c84
-rw-r--r--drivers/video/mcde/Kconfig9
-rw-r--r--drivers/video/mcde/display-av8100.c309
-rw-r--r--drivers/video/mcde/mcde_fb.c24
-rw-r--r--include/video/hdmi.h3
-rw-r--r--include/video/mcde_display-av8100.h14
-rw-r--r--include/video/mcde_display.h6
-rw-r--r--include/video/mcde_fb.h4
10 files changed, 569 insertions, 53 deletions
diff --git a/arch/arm/mach-ux500/board-mop500-mcde.c b/arch/arm/mach-ux500/board-mop500-mcde.c
index 5d8f0f9058b..3826fc5a337 100644
--- a/arch/arm/mach-ux500/board-mop500-mcde.c
+++ b/arch/arm/mach-ux500/board-mop500-mcde.c
@@ -42,8 +42,8 @@ static struct delayed_work work_dispreg_hdmi;
#define DISPREG_HDMI_DELAY 6000
#endif
-static struct fb_info *fbs[3] = { NULL, NULL, NULL };
-static struct mcde_display_device *displays[3] = { NULL, NULL, NULL };
+#define MCDE_NR_OF_DISPLAYS 3
+static struct mcde_display_device *displays[MCDE_NR_OF_DISPLAYS] = { NULL };
static int display_initialized_during_boot;
static int __init startup_graphics_setup(char *str)
@@ -91,7 +91,7 @@ static struct mcde_port port0 = {
},
};
-struct mcde_display_generic_platform_data mop500_generic_display0_pdata = {
+struct mcde_display_generic_platform_data generic_display0_pdata = {
.reset_delay = 1,
#ifdef CONFIG_REGULATOR
.regulator_id = "vaux12v5",
@@ -100,7 +100,7 @@ struct mcde_display_generic_platform_data mop500_generic_display0_pdata = {
#endif
};
-struct mcde_display_device mop500_generic_display0 = {
+struct mcde_display_device generic_display0 = {
.name = "mcde_disp_generic",
.id = PRIMARY_DISPLAY_ID,
.port = &port0,
@@ -118,7 +118,7 @@ struct mcde_display_device mop500_generic_display0 = {
.rotbuf1 = U8500_ESRAM_BASE + 0x20000 * 4,
.rotbuf2 = U8500_ESRAM_BASE + 0x20000 * 4 + 0x10000,
.dev = {
- .platform_data = &mop500_generic_display0_pdata,
+ .platform_data = &generic_display0_pdata,
},
};
#endif /* CONFIG_DISPLAY_GENERIC_DSI_PRIMARY */
@@ -195,7 +195,7 @@ static struct mcde_port port0 = {
},
};
-struct mcde_display_dpi_platform_data mop500_generic_display0_pdata = {0};
+struct mcde_display_dpi_platform_data generic_display0_pdata = {0};
static struct ux500_pins *dpi_pins;
static int dpi_display_platform_enable(struct mcde_display_device *ddev)
@@ -230,7 +230,7 @@ static int dpi_display_platform_disable(struct mcde_display_device *ddev)
}
-struct mcde_display_device mop500_generic_display0 = {
+struct mcde_display_device generic_display0 = {
.name = "mcde_display_dpi",
.id = 0,
.port = &port0,
@@ -245,7 +245,7 @@ struct mcde_display_device mop500_generic_display0 = {
.native_y_res = 480,
/* .synchronized_update: Don't care: port is set to update_auto_trig */
.dev = {
- .platform_data = &mop500_generic_display0_pdata,
+ .platform_data = &generic_display0_pdata,
},
.platform_enable = dpi_display_platform_enable,
.platform_disable = dpi_display_platform_disable,
@@ -413,12 +413,9 @@ struct mcde_display_device av8100_hdmi = {
static void delayed_work_dispreg_hdmi(struct work_struct *ptr)
{
- int ret;
-
- ret = mcde_display_device_register(&av8100_hdmi);
- if (ret)
+ if (mcde_display_device_register(&av8100_hdmi))
pr_warning("Failed to register av8100_hdmi\n");
- displays[2] = &av8100_hdmi;
+ displays[TERTIARY_DISPLAY_ID] = &av8100_hdmi;
}
#endif /* CONFIG_DISPLAY_AV8100_TERTIARY */
@@ -432,11 +429,12 @@ static int display_postregistered_callback(struct notifier_block *nb,
u16 width, height;
u16 virtual_width, virtual_height;
u32 rotate = FB_ROTATE_UR;
+ struct fb_info *fbi;
if (event != MCDE_DSS_EVENT_DISPLAY_REGISTERED)
return 0;
- if (ddev->id < PRIMARY_DISPLAY_ID || ddev->id >= ARRAY_SIZE(fbs))
+ if (ddev->id < PRIMARY_DISPLAY_ID || ddev->id >= MCDE_NR_OF_DISPLAYS)
return 0;
mcde_dss_get_native_resolution(ddev, &width, &height);
@@ -474,17 +472,26 @@ static int display_postregistered_callback(struct notifier_block *nb,
virtual_height = height;
#endif
- /* Create frame buffer */
- fbs[ddev->id] = mcde_fb_create(ddev,
- width, height,
- virtual_width, virtual_height,
- ddev->default_pixel_format,
- rotate);
-
- if (IS_ERR(fbs[ddev->id]))
- pr_warning("Failed to create fb for display %s\n", ddev->name);
- else
- pr_info("Framebuffer created (%s)\n", ddev->name);
+ if (ddev->id == TERTIARY_DISPLAY_ID) {
+#ifdef CONFIG_MCDE_DISPLAY_HDMI_FB_AUTO_CREATE
+ hdmi_fb_onoff(ddev, 1, 0, 0);
+#endif /* CONFIG_MCDE_DISPLAY_HDMI_FB_AUTO_CREATE */
+ } else {
+ /* Create frame buffer */
+ fbi = mcde_fb_create(ddev,
+ width, height,
+ virtual_width, virtual_height,
+ ddev->default_pixel_format,
+ rotate);
+
+ if (IS_ERR(fbi))
+ dev_warn(&ddev->dev,
+ "Failed to create fb for display %s\n",
+ ddev->name);
+ else
+ dev_info(&ddev->dev, "Framebuffer created (%s)\n",
+ ddev->name);
+ }
return 0;
}
@@ -606,9 +613,9 @@ int __init init_display_devices(void)
#ifdef CONFIG_DISPLAY_GENERIC_PRIMARY
if (machine_is_hrefv60())
- mop500_generic_display0_pdata.reset_gpio = HREFV60_DISP1_RST_GPIO;
+ generic_display0_pdata.reset_gpio = HREFV60_DISP1_RST_GPIO;
else
- mop500_generic_display0_pdata.reset_gpio = MOP500_DISP1_RST_GPIO;
+ generic_display0_pdata.reset_gpio = MOP500_DISP1_RST_GPIO;
#ifdef CONFIG_DISPLAY_GENERIC_DSI_PRIMARY_VSYNC
i2c0 = i2c_get_adapter(0);
@@ -622,16 +629,16 @@ int __init init_display_devices(void)
i2c_put_adapter(i2c0);
/* ret == 0 => U8500 UIB connected */
- mop500_generic_display0.synchronized_update = (ret == 0);
+ generic_display0.synchronized_update = (ret == 0);
}
#endif
if (display_initialized_during_boot)
- mop500_generic_display0.power_mode = MCDE_DISPLAY_PM_STANDBY;
- ret = mcde_display_device_register(&mop500_generic_display0);
+ generic_display0.power_mode = MCDE_DISPLAY_PM_STANDBY;
+ ret = mcde_display_device_register(&generic_display0);
if (ret)
pr_warning("Failed to register generic display device 0\n");
- displays[0] = &mop500_generic_display0;
+ displays[0] = &generic_display0;
#endif
#ifdef CONFIG_DISPLAY_GENERIC_DSI_SECONDARY
@@ -662,6 +669,85 @@ int __init init_display_devices(void)
return ret;
}
+struct mcde_display_device *mcde_get_main_display(void)
+{
+#if defined(CONFIG_DISPLAY_GENERIC_DSI_PRIMARY)
+ return &generic_display0;
+#elif defined(CONFIG_DISPLAY_GENERIC_DSI_SECONDARY)
+ return &generic_subdisplay;
+#elif defined(CONFIG_DISPLAY_AV8100_TERTIARY)
+ return &av8100_hdmi;
+#elif defined(CONFIG_DISPLAY_AB8500_TERTIARY)
+ return &tvout_ab8500_display;
+#else
+ return NULL;
+#endif
+}
+EXPORT_SYMBOL(mcde_get_main_display);
+
+void hdmi_fb_onoff(struct mcde_display_device *ddev,
+ bool enable, u8 cea, u8 vesa_cea_nr)
+{
+ struct fb_info *fbi;
+ u16 w, h;
+ u16 vw, vh;
+ u32 rotate = FB_ROTATE_UR;
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+
+ dev_dbg(&ddev->dev, "%s\n", __func__);
+ dev_dbg(&ddev->dev, "en:%d cea:%d nr:%d\n", enable, cea, vesa_cea_nr);
+
+ if (enable) {
+ if (ddev->enabled) {
+ dev_dbg(&ddev->dev, "Display is already enabled.\n");
+ return;
+ }
+
+ /* Create fb */
+ if (ddev->fbi == NULL) {
+ /* Note: change when dynamic buffering is available */
+ int buffering = 2;
+
+ /* Get default values */
+ mcde_dss_get_native_resolution(ddev, &w, &h);
+ vw = w;
+ vh = h * buffering;
+
+ if (vesa_cea_nr != 0)
+ ddev->ceanr_convert(ddev, cea, vesa_cea_nr,
+ buffering, &w, &h, &vw, &vh);
+
+ fbi = mcde_fb_create(ddev, w, h, vw, vh,
+ ddev->default_pixel_format, rotate);
+
+ if (IS_ERR(fbi)) {
+ dev_warn(&ddev->dev,
+ "Failed to create fb for display %s\n",
+ ddev->name);
+ goto hdmi_fb_onoff_end;
+ } else {
+ dev_info(&ddev->dev,
+ "Framebuffer created (%s)\n",
+ ddev->name);
+ }
+ driver_data->fbdevname = (char *)dev_name(fbi->dev);
+ }
+ } else {
+ if (!ddev->enabled) {
+ dev_dbg(&ddev->dev, "Display %s is already disabled.\n",
+ ddev->name);
+ return;
+ }
+
+ mcde_fb_destroy(ddev);
+ }
+
+hdmi_fb_onoff_end:
+ return;
+}
+EXPORT_SYMBOL(hdmi_fb_onoff);
+
+
module_init(init_display_devices);
#endif
diff --git a/drivers/video/av8100/av8100.c b/drivers/video/av8100/av8100.c
index f2be10dc15f..3523aaa3e70 100644
--- a/drivers/video/av8100/av8100.c
+++ b/drivers/video/av8100/av8100.c
@@ -1059,10 +1059,7 @@ static void av8100_set_state(enum av8100_operating_mode state)
{
g_av8100_status.av8100_state = state;
- if (state == AV8100_OPMODE_UNDEFINED)
- g_av8100_status.hdmi_on = false;
-
- if (state == AV8100_OPMODE_STANDBY) {
+ if (state <= AV8100_OPMODE_STANDBY) {
clr_plug_status(AV8100_HDMI_PLUGIN);
clr_plug_status(AV8100_CVBS_PLUGIN);
g_av8100_status.hdmi_on = false;
@@ -1929,15 +1926,23 @@ static int av8100_powerup2(void)
int av8100_powerup(void)
{
+ int ret = 0;
+
if (av8100_status_get().av8100_state == AV8100_OPMODE_UNDEFINED)
return -EINVAL;
- if (av8100_powerup1()) {
- dev_err(av8100dev, "av8100_powerup1 fail\n");
- return -EFAULT;
+ if (av8100_status_get().av8100_state < AV8100_OPMODE_STANDBY) {
+ ret = av8100_powerup1();
+ if (ret) {
+ dev_err(av8100dev, "av8100_powerup1 fail\n");
+ return -EFAULT;
+ }
}
- return av8100_powerup2();
+ if (av8100_status_get().av8100_state < AV8100_OPMODE_SCAN)
+ ret = av8100_powerup2();
+
+ return ret;
}
EXPORT_SYMBOL(av8100_powerup);
diff --git a/drivers/video/av8100/hdmi.c b/drivers/video/av8100/hdmi.c
index 66ba66769b8..d502b878024 100644
--- a/drivers/video/av8100/hdmi.c
+++ b/drivers/video/av8100/hdmi.c
@@ -83,6 +83,10 @@ static ssize_t show_plugstatus(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t store_poweronoff(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_poweronoff(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_evwakeup(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
static DEVICE_ATTR(storeastext, S_IWUSR, NULL, store_storeastext);
static DEVICE_ATTR(plugdeten, S_IWUSR, NULL, store_plugdeten);
@@ -104,7 +108,9 @@ static DEVICE_ATTR(evread, S_IRUGO, show_evread, NULL);
static DEVICE_ATTR(evclr, S_IWUSR, NULL, store_evclr);
static DEVICE_ATTR(audiocfg, S_IWUSR, NULL, store_audiocfg);
static DEVICE_ATTR(plugstatus, S_IRUGO, show_plugstatus, NULL);
-static DEVICE_ATTR(poweronoff, S_IWUSR, NULL, store_poweronoff);
+static DEVICE_ATTR(poweronoff, S_IRUGO | S_IWUSR, show_poweronoff,
+ store_poweronoff);
+static DEVICE_ATTR(evwakeup, S_IWUSR, NULL, store_evwakeup);
/* Hex to int conversion */
static unsigned int htoi(const char *ptr)
@@ -609,6 +615,24 @@ static int events_clear(u8 ev)
return 0;
}
+static int event_wakeup(void)
+{
+ struct kobject *kobj = &hdmidev->kobj;
+
+ dev_dbg(hdmidev, "%s", __func__);
+
+ LOCK_HDMI_EVENTS;
+ events |= HDMI_EVENT_WAKEUP;
+ events_received = true;
+ UNLOCK_HDMI_EVENTS;
+
+ /* Wake up application waiting for event via call to poll() */
+ sysfs_notify(kobj, NULL, SYSFS_EVENT_FILENAME);
+ wake_up_interruptible(&hdmi_event_wq);
+
+ return 0;
+}
+
static int audiocfg(struct audio_cfg *cfg)
{
union av8100_configuration config;
@@ -1615,6 +1639,44 @@ static ssize_t store_poweronoff(struct device *dev,
return count;
}
+static ssize_t show_poweronoff(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hdmi_driver_data *hdmi_driver_data;
+ int index = 0;
+ struct av8100_status status;
+ u8 power_state;
+
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ hdmi_driver_data = dev_get_drvdata(dev);
+
+ status = av8100_status_get();
+ if (status.av8100_state < AV8100_OPMODE_SCAN)
+ power_state = 0;
+ else
+ power_state = 1;
+
+ if (hdmi_driver_data->store_as_hextext) {
+ snprintf(buf + index, 3, "%02x", power_state);
+ index += 3;
+ } else {
+ *(buf + index++) = power_state;
+ }
+
+ return index;
+}
+
+static ssize_t store_evwakeup(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ dev_dbg(hdmidev, "%s\n", __func__);
+
+ event_wakeup();
+
+ return count;
+}
+
static int hdmi_open(struct inode *inode, struct file *filp)
{
if (device_open)
@@ -1916,6 +1978,21 @@ ioc_hdcploadaes_err:
}
break;
+ case IOC_EVENT_WAKEUP:
+ /* Trigger event */
+ event_wakeup();
+ break;
+
+ case IOC_POWERSTATE:
+ status = av8100_status_get();
+ value = status.av8100_state >= AV8100_OPMODE_SCAN;
+
+ if (copy_to_user((void *)arg, (void *)&value,
+ sizeof(u8))) {
+ return -EINVAL;
+ }
+ break;
+
/* Internal */
case IOC_HDMI_ENABLE_INTERRUPTS:
av8100_disable_interrupt();
@@ -2087,10 +2164,12 @@ void hdmi_event(enum av8100_hdmi_event ev)
/* Set event */
switch (ev) {
case AV8100_HDMI_EVENT_HDMI_PLUGIN:
+ events &= ~HDMI_EVENT_HDMI_PLUGOUT;
events |= events_mask & HDMI_EVENT_HDMI_PLUGIN;
break;
case AV8100_HDMI_EVENT_HDMI_PLUGOUT:
+ events &= ~HDMI_EVENT_HDMI_PLUGIN;
events |= events_mask & HDMI_EVENT_HDMI_PLUGOUT;
break;
@@ -2190,6 +2269,8 @@ int __init hdmi_init(void)
dev_info(hdmidev, "Unable to create plugstatus attribute\n");
if (device_create_file(hdmidev, &dev_attr_poweronoff))
dev_info(hdmidev, "Unable to create poweronoff attribute\n");
+ if (device_create_file(hdmidev, &dev_attr_evwakeup))
+ dev_info(hdmidev, "Unable to create evwakeup attribute\n");
/* Register event callback */
av8100_hdmi_event_cb_set(hdmi_event);
@@ -2224,6 +2305,7 @@ void hdmi_exit(void)
device_remove_file(hdmidev, &dev_attr_audiocfg);
device_remove_file(hdmidev, &dev_attr_plugstatus);
device_remove_file(hdmidev, &dev_attr_poweronoff);
+ device_remove_file(hdmidev, &dev_attr_evwakeup);
hdmi_driver_data = dev_get_drvdata(hdmidev);
kfree(hdmi_driver_data);
diff --git a/drivers/video/mcde/Kconfig b/drivers/video/mcde/Kconfig
index c97387a97ed..a1310332cf9 100644
--- a/drivers/video/mcde/Kconfig
+++ b/drivers/video/mcde/Kconfig
@@ -44,6 +44,15 @@ config MCDE_DISPLAY_AV8100
depends on FB_MCDE
select AV8100
+config MCDE_DISPLAY_HDMI_FB_AUTO_CREATE
+ bool "HDMI_FB_AUTO_CREATE"
+ default y
+ depends on MCDE_DISPLAY_AV8100
+ ---help---
+ Say Y if you want the HDMI frame buffer to be created on start
+ Say N if you want the HDMI frame buffer to be created when HDMI
+ cable is plugged (needs user space HDMIservice)
+
config MCDE_DISPLAY_AB8500_DENC
tristate "AB8500 CVBS display driver"
depends on FB_MCDE
diff --git a/drivers/video/mcde/display-av8100.c b/drivers/video/mcde/display-av8100.c
index 84818a558fc..5d400a9958e 100644
--- a/drivers/video/mcde/display-av8100.c
+++ b/drivers/video/mcde/display-av8100.c
@@ -24,10 +24,10 @@
#define SWITCH_HELPSTR ", 0=HDMI, 1=SDTV, 2=DVI\n"
-struct display_driver_data {
- struct regulator *cvbs_regulator;
- bool cvbs_regulator_enabled;
- bool update_port_pixel_format;
+struct cea_vesa_video_mode {
+ u32 cea;
+ u32 vesa_cea_nr;
+ struct mcde_video_mode *video_mode;
};
static int hdmi_try_video_mode(
@@ -36,6 +36,8 @@ static int hdmi_set_video_mode(
struct mcde_display_device *ddev, struct mcde_video_mode *video_mode);
static int hdmi_set_pixel_format(
struct mcde_display_device *ddev, enum mcde_ovly_pix_fmt format);
+static struct mcde_video_mode *video_mode_get(struct mcde_display_device *ddev,
+ u8 cea, u8 vesa_cea_nr);
static ssize_t show_hdmisdtvswitch(struct device *dev,
struct device_attribute *attr, char *buf);
@@ -45,6 +47,23 @@ static ssize_t show_input_pixel_format(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t store_input_pixel_format(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_disponoff(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_disponoff(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_vesacea(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_timing(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_timing(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_stayalive(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+static DEVICE_ATTR(disponoff, S_IRUGO | S_IWUSR, show_disponoff,
+ store_disponoff);
+static DEVICE_ATTR(vesacea, S_IRUGO, show_vesacea, NULL);
+static DEVICE_ATTR(timing, S_IRUGO | S_IWUSR, show_timing, store_timing);
+static DEVICE_ATTR(stayalive, S_IWUSR, NULL, store_stayalive);
static DEVICE_ATTR(hdmisdtvswitch, S_IRUGO | S_IWUSR, show_hdmisdtvswitch,
store_hdmisdtvswitch);
@@ -137,6 +156,134 @@ static ssize_t store_input_pixel_format(struct device *dev,
return count;
}
+static ssize_t show_disponoff(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (ddev->fbi && driver_data->fbdevname) {
+ dev_dbg(dev, "name:%s\n", driver_data->fbdevname);
+ strcpy(buf, driver_data->fbdevname);
+ return strlen(driver_data->fbdevname) + 1;
+ }
+ return 0;
+}
+
+static ssize_t store_disponoff(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *mdev = to_mcde_display_device(dev);
+ bool enable = false;
+ u8 cea = 0;
+ u8 vesa_cea_nr = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if ((count != DISPONOFF_SIZE) && (count != DISPONOFF_SIZE + 1))
+ return -EINVAL;
+
+ if ((*buf == '0') && (*(buf + 1) == '1'))
+ enable = true;
+ cea = (hex_to_bin(buf[2]) << 4) + hex_to_bin(buf[3]);
+ vesa_cea_nr = (hex_to_bin(buf[4]) << 4) + hex_to_bin(buf[5]);
+ dev_dbg(dev, "enable:%d cea:%d nr:%d\n", enable, cea, vesa_cea_nr);
+
+ hdmi_fb_onoff(mdev, enable, cea, vesa_cea_nr);
+
+ return count;
+}
+
+static ssize_t show_timing(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+ struct mcde_video_mode *video_mode;
+ int index;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ index = 0;
+ if (driver_data->video_mode) {
+ video_mode = driver_data->video_mode;
+ memcpy(buf + index, &video_mode->xres, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->yres, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->pixclock, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->hbp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->hfp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->vbp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->vfp, sizeof(u32));
+ index += sizeof(u32);
+ memcpy(buf + index, &video_mode->interlaced, sizeof(u32));
+ index += sizeof(u32);
+ }
+ return index;
+}
+
+static ssize_t store_timing(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+ struct display_driver_data *driver_data = dev_get_drvdata(&ddev->dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (count != TIMING_SIZE)
+ return -EINVAL;
+
+ driver_data->video_mode = video_mode_get(ddev, *buf, *(buf + 1));
+
+ return count;
+}
+
+static ssize_t store_stayalive(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mcde_display_device *ddev = to_mcde_display_device(dev);
+
+ if (count != STAYALIVE_SIZE)
+ return -EINVAL;
+
+ if ((*buf == 1) || (*buf == '1'))
+ ddev->stay_alive = true;
+ else
+ ddev->stay_alive = false;
+
+ dev_dbg(dev, "%s %d\n", __func__, ddev->stay_alive);
+
+ return count;
+}
+
+static int ceanr_convert(struct mcde_display_device *ddev,
+ u8 cea, u8 vesa_cea_nr, int buffering,
+ u16 *w, u16 *h, u16 *vw, u16 *vh)
+{
+ struct mcde_video_mode *video_mode;
+
+ dev_dbg(&ddev->dev, "%s\n", __func__);
+ video_mode = video_mode_get(ddev, cea, vesa_cea_nr);
+ if (video_mode) {
+ *w = video_mode->xres;
+ *h = video_mode->yres;
+ *vw = video_mode->xres;
+ *vh = video_mode->yres * buffering;
+ dev_dbg(&ddev->dev, "cea:%d nr:%d found\n",
+ cea, vesa_cea_nr);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
/* Supported HDMI modes */
static struct mcde_video_mode video_modes_supp_hdmi[] = {
/* 640_480_60_P */
@@ -249,6 +396,124 @@ static struct mcde_video_mode video_modes_supp_sdtv[] = {
},
};
+static struct cea_vesa_video_mode cea_vesa_video_mode[] = {
+ /* 640_480_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 1,
+ .video_mode = &video_modes_supp_hdmi[0],
+ },
+ /* 720_480_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 2,
+ .video_mode = &video_modes_supp_hdmi[1],
+ },
+ /* 720_480_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 3,
+ .video_mode = &video_modes_supp_hdmi[1],
+ },
+ /* 720_576_50_P */
+ {
+ .cea = 1, .vesa_cea_nr = 17,
+ .video_mode = &video_modes_supp_hdmi[2],
+ },
+ /* 720_576_50_P */
+ {
+ .cea = 1, .vesa_cea_nr = 18,
+ .video_mode = &video_modes_supp_hdmi[2],
+ },
+ /* 1280_720_60_P */
+ {
+ .cea = 1, .vesa_cea_nr = 4,
+ .video_mode = &video_modes_supp_hdmi[3],
+ },
+ /* 1280_720_50_P */
+ {
+ .cea = 1, .vesa_cea_nr = 19,
+ .video_mode = &video_modes_supp_hdmi[4],
+ },
+ /* 1920_1080_30_P */
+ {
+ .cea = 1, .vesa_cea_nr = 34,
+ .video_mode = &video_modes_supp_hdmi[5],
+ },
+ /* 1920_1080_24_P */
+ {
+ .cea = 1, .vesa_cea_nr = 32,
+ .video_mode = &video_modes_supp_hdmi[6],
+ },
+ /* 1920_1080_25_P */
+ {
+ .cea = 1, .vesa_cea_nr = 33,
+ .video_mode = &video_modes_supp_hdmi[7],
+ },
+ /* 720_480_60_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 6,
+ .video_mode = &video_modes_supp_hdmi[8],
+ },
+ /* 720_480_60_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 7,
+ .video_mode = &video_modes_supp_hdmi[8],
+ },
+ /* 720_576_50_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 21,
+ .video_mode = &video_modes_supp_hdmi[9],
+ },
+ /* 720_576_50_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 22,
+ .video_mode = &video_modes_supp_hdmi[9],
+ },
+ /* 1920_1080_50_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 20,
+ .video_mode = &video_modes_supp_hdmi[10],
+ },
+ /* 1920_1080_60_I) */
+ {
+ .cea = 1, .vesa_cea_nr = 5,
+ .video_mode = &video_modes_supp_hdmi[11],
+ },
+};
+
+static ssize_t show_vesacea(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int findex;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ for (findex = 0; findex < ARRAY_SIZE(cea_vesa_video_mode); findex++) {
+ *(buf + findex * 2) = cea_vesa_video_mode[findex].cea;
+ *(buf + findex * 2 + 1) =
+ cea_vesa_video_mode[findex].vesa_cea_nr;
+ }
+ *(buf + findex * 2) = '\0';
+
+ return findex * 2 + 1;
+}
+
+static struct mcde_video_mode *video_mode_get(struct mcde_display_device *ddev,
+ u8 cea, u8 vesa_cea_nr)
+{
+ int findex;
+
+ dev_dbg(&ddev->dev, "%s\n", __func__);
+
+ for (findex = 0; findex < ARRAY_SIZE(cea_vesa_video_mode); findex++)
+ if ((cea == cea_vesa_video_mode[findex].cea) &&
+ (vesa_cea_nr ==
+ cea_vesa_video_mode[findex].vesa_cea_nr)) {
+ dev_dbg(&ddev->dev, "cea:%d nr:%d\n", cea, vesa_cea_nr);
+ return cea_vesa_video_mode[findex].video_mode;
+ }
+
+ return NULL;
+}
+
#define AV8100_MAX_LEVEL 255
static int hdmi_try_video_mode(
@@ -445,17 +710,20 @@ static int hdmi_set_video_mode(
dev_err(&dev->dev, "av8100_powerup failed\n");
return ret;
}
+ }
+ if (status.av8100_state < AV8100_OPMODE_IDLE) {
ret = av8100_download_firmware(NULL, 0, I2C_INTERFACE);
if (ret) {
dev_err(&dev->dev, "av8100_download_firmware failed\n");
av8100_powerdown();
return ret;
}
- } else if (av8100_disable_interrupt()) {
- return -EFAULT;
}
+ if (av8100_disable_interrupt())
+ return -EFAULT;
+
/*
* Don't look at dev->port->hdmi_sdtv_switch; it states only which
* one should be started, not which one is currently working
@@ -817,6 +1085,14 @@ static int hdmi_set_power_mode(struct mcde_display_device *ddev,
if (ret)
return ret;
}
+
+ ret = av8100_powerup();
+ if (ret) {
+ dev_err(&ddev->dev, "av8100_powerup failed\n");
+ return ret;
+ }
+ av8100_enable_interrupt();
+
/*
* the regulator for analog TV out is only enabled here,
* this means that one needs to switch to the OFF state
@@ -907,14 +1183,27 @@ static int __devinit hdmi_probe(struct mcde_display_device *dev)
dev->apply_config = hdmi_apply_config;
dev->set_pixel_format = hdmi_set_pixel_format;
dev->set_power_mode = hdmi_set_power_mode;
+ dev->ceanr_convert = ceanr_convert;
- /* Create sysfs file for switching between hdmi and sdtv */
+ /* Create sysfs files */
if (device_create_file(&dev->dev, &dev_attr_hdmisdtvswitch))
dev_info(&dev->dev,
"Unable to create hdmisdtvswitch attr\n");
if (device_create_file(&dev->dev, &dev_attr_input_pixel_format))
dev_info(&dev->dev,
"Unable to create input_pixel_format attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_disponoff))
+ dev_info(&dev->dev,
+ "Unable to create disponoff attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_vesacea))
+ dev_info(&dev->dev,
+ "Unable to create ceavesa attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_timing))
+ dev_info(&dev->dev,
+ "Unable to create timing attr\n");
+ if (device_create_file(&dev->dev, &dev_attr_stayalive))
+ dev_info(&dev->dev,
+ "Unable to create stayalive attr\n");
if (pdata->cvbs_regulator_id) {
driver_data->cvbs_regulator = regulator_get(&dev->dev,
@@ -944,9 +1233,13 @@ static int __devexit hdmi_remove(struct mcde_display_device *dev)
struct mcde_display_hdmi_platform_data *pdata =
dev->dev.platform_data;
- /* Remove sysfs file */
+ /* Remove sysfs files */
device_remove_file(&dev->dev, &dev_attr_input_pixel_format);
device_remove_file(&dev->dev, &dev_attr_hdmisdtvswitch);
+ device_remove_file(&dev->dev, &dev_attr_disponoff);
+ device_remove_file(&dev->dev, &dev_attr_vesacea);
+ device_remove_file(&dev->dev, &dev_attr_timing);
+ device_remove_file(&dev->dev, &dev_attr_stayalive);
dev->set_power_mode(dev, MCDE_DISPLAY_PM_OFF);
diff --git a/drivers/video/mcde/mcde_fb.c b/drivers/video/mcde/mcde_fb.c
index a0c7d21a3fc..039a60ad05c 100644
--- a/drivers/video/mcde/mcde_fb.c
+++ b/drivers/video/mcde/mcde_fb.c
@@ -203,6 +203,7 @@ static int init_var_fmt(struct fb_var_screeninfo *var,
var->yoffset = 0;
var->activate = FB_ACTIVATE_NOW;
var->rotate = rotate;
+
return 0;
};
@@ -668,6 +669,8 @@ struct fb_info *mcde_fb_create(struct mcde_display_device *ddev,
if (ret)
goto fb_register_failed;
+ ddev->fbi = fbi;
+
#ifdef CONFIG_HAS_EARLYSUSPEND
mfb->early_suspend.level =
EARLY_SUSPEND_LEVEL_DISABLE_FB;
@@ -702,9 +705,26 @@ int mcde_fb_attach_overlay(struct fb_info *fb_info, struct mcde_overlay *ovl)
return -EINVAL;
}
-void mcde_fb_destroy(struct fb_info *fb_info)
+void mcde_fb_destroy(struct mcde_display_device *dev)
{
- /* TODO: clean up */
+ struct mcde_fb *mfb;
+ int i;
+
+ dev_vdbg(&dev->dev, "%s\n", __func__);
+
+ mcde_dss_disable_display(dev);
+ mcde_dss_close_channel(dev);
+
+ mfb = to_mcde_fb(dev->fbi);
+ for (i = 0; i < mfb->num_ovlys; i++) {
+ if (mfb->ovlys[i])
+ mcde_dss_destroy_overlay(mfb->ovlys[i]);
+ }
+
+ unregister_framebuffer(dev->fbi);
+ free_fb_mem(dev->fbi);
+ framebuffer_release(dev->fbi);
+ dev->fbi = NULL;
}
/* Overlay fbs' platform device */
diff --git a/include/video/hdmi.h b/include/video/hdmi.h
index 18a8e34bd67..0a4dd723486 100644
--- a/include/video/hdmi.h
+++ b/include/video/hdmi.h
@@ -86,6 +86,8 @@
#define IOC_AUDIO_CFG _IOWR(HDMI_IOC_MAGIC, 15, int)
#define IOC_PLUG_STATUS _IOWR(HDMI_IOC_MAGIC, 16, int)
#define IOC_POWERONOFF _IOWR(HDMI_IOC_MAGIC, 17, int)
+#define IOC_EVENT_WAKEUP _IOWR(HDMI_IOC_MAGIC, 18, int)
+#define IOC_POWERSTATE _IOWR(HDMI_IOC_MAGIC, 19, int)
/* HDMI driver */
@@ -100,6 +102,7 @@ enum hdmi_event {
HDMI_EVENT_CEC = 0x4,
HDMI_EVENT_HDCP = 0x8,
HDMI_EVENT_CECTXERR = 0x10,
+ HDMI_EVENT_WAKEUP = 0x20,
};
enum hdmi_hdcp_auth_type {
diff --git a/include/video/mcde_display-av8100.h b/include/video/mcde_display-av8100.h
index c508ebf1f17..b0bbc02c1f8 100644
--- a/include/video/mcde_display-av8100.h
+++ b/include/video/mcde_display-av8100.h
@@ -20,6 +20,9 @@
#define NATIVE_YRES_HDMI 720
#define NATIVE_XRES_SDTV 720
#define NATIVE_YRES_SDTV 576
+#define DISPONOFF_SIZE 6
+#define TIMING_SIZE 2
+#define STAYALIVE_SIZE 1
struct mcde_display_hdmi_platform_data {
/* Platform info */
@@ -36,4 +39,15 @@ struct mcde_display_hdmi_platform_data {
struct regulator *regulator;
};
+struct display_driver_data {
+ struct regulator *cvbs_regulator;
+ bool cvbs_regulator_enabled;
+ bool update_port_pixel_format;
+ char *fbdevname;
+ struct mcde_video_mode *video_mode;
+};
+
+void hdmi_fb_onoff(struct mcde_display_device *ddev, bool enable,
+ u8 cea, u8 vesa_cea_nr);
+
#endif /* __DISPLAY_AV8100__H__ */
diff --git a/include/video/mcde_display.h b/include/video/mcde_display.h
index aa184cb6162..69ff1de4ef2 100644
--- a/include/video/mcde_display.h
+++ b/include/video/mcde_display.h
@@ -29,6 +29,7 @@ struct mcde_display_device {
const char *name;
int id;
struct mcde_port *port;
+ struct fb_info *fbi;
/* MCDE dss driver internal */
bool initialized;
@@ -56,6 +57,7 @@ struct mcde_display_device {
bool synchronized_update;
struct mcde_video_mode video_mode;
int update_flags;
+ bool stay_alive;
/* Driver API */
void (*get_native_resolution)(struct mcde_display_device *dev,
@@ -76,7 +78,6 @@ struct mcde_display_device {
struct mcde_video_mode *video_mode);
void (*get_video_mode)(struct mcde_display_device *dev,
struct mcde_video_mode *video_mode);
-
int (*set_pixel_format)(struct mcde_display_device *dev,
enum mcde_ovly_pix_fmt pix_fmt);
enum mcde_ovly_pix_fmt (*get_pixel_format)(
@@ -102,6 +103,9 @@ struct mcde_display_device {
int (*on_first_update)(struct mcde_display_device *dev);
int (*platform_enable)(struct mcde_display_device *dev);
int (*platform_disable)(struct mcde_display_device *dev);
+ int (*ceanr_convert)(struct mcde_display_device *ddev,
+ u8 cea, u8 vesa_cea_nr, int buffering,
+ u16 *w, u16 *h, u16 *vw, u16 *vh);
};
struct mcde_display_driver {
diff --git a/include/video/mcde_fb.h b/include/video/mcde_fb.h
index 60d8b244688..17556414aa0 100644
--- a/include/video/mcde_fb.h
+++ b/include/video/mcde_fb.h
@@ -50,11 +50,11 @@ struct mcde_fb {
/* MCDE fbdev API */
struct fb_info *mcde_fb_create(struct mcde_display_device *ddev,
uint16_t w, uint16_t h, uint16_t vw, uint16_t vh,
- enum mcde_ovly_pix_fmt pix_fmt, uint32_t rotate);
+ enum mcde_ovly_pix_fmt pix_fmt, uint32_t rotate);
int mcde_fb_attach_overlay(struct fb_info *fb_info,
struct mcde_overlay *ovl);
-void mcde_fb_destroy(struct fb_info *fb_info);
+void mcde_fb_destroy(struct mcde_display_device *ddev);
/* MCDE fb driver */
int mcde_fb_init(void);