From c58603900d889c15604a604ca03fb5b4af7fb7f2 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 10 May 2011 16:12:51 +0200 Subject: 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 Conflicts: arch/arm/mach-ux500/board-mop500-mcde.c drivers/misc/dispdev/dispdev.c include/linux/dispdev.h --- arch/arm/mach-ux500/board-mop500-mcde.c | 148 +++++++++++---- drivers/video/av8100/av8100.c | 21 ++- drivers/video/av8100/hdmi.c | 84 ++++++++- drivers/video/mcde/Kconfig | 9 + drivers/video/mcde/display-av8100.c | 309 +++++++++++++++++++++++++++++++- drivers/video/mcde/mcde_fb.c | 24 ++- include/video/hdmi.h | 3 + include/video/mcde_display-av8100.h | 14 ++ include/video/mcde_display.h | 6 +- include/video/mcde_fb.h | 4 +- 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); -- cgit v1.2.3