diff options
Diffstat (limited to 'drivers/video/mxc')
-rw-r--r-- | drivers/video/mxc/ldb.c | 137 | ||||
-rw-r--r-- | drivers/video/mxc/mipi_dsi.c | 2 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_dispdrv.c | 17 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_dispdrv.h | 4 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_elcdif_fb.c | 2 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_ipuv3_fb.c | 269 |
6 files changed, 415 insertions, 16 deletions
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c index 19de5c541fdd..278475c088f1 100644 --- a/drivers/video/mxc/ldb.c +++ b/drivers/video/mxc/ldb.c @@ -102,6 +102,8 @@ struct ldb_data { }; static int g_ldb_mode; +static struct i2c_client *ldb_i2c_client[2]; +static u8 g_edid[2][512]; static struct fb_videomode ldb_modedb[] = { { @@ -131,6 +133,8 @@ static struct fb_videomode ldb_modedb[] = { }; static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb); +static int mxc_ldb_edidread(struct i2c_client *client); + static int bits_per_pixel(int pixel_fmt) { switch (pixel_fmt) { @@ -412,10 +416,12 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, int ret = 0, i; struct ldb_data *ldb = mxc_dispdrv_getdata(disp); struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data; + struct i2c_client *i2c_dev; struct resource *res; uint32_t base_addr; uint32_t reg, setting_idx; uint32_t ch_mask = 0, ch_val = 0; + int lvds_channel = 0; uint32_t ipu_id, disp_id; /* if input format not valid, make RGB666 as default*/ @@ -428,7 +434,6 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, if (!ldb->inited) { char di_clk[] = "ipu1_di0_clk"; char ldb_clk[] = "ldb_di0_clk"; - int lvds_channel = 0; setting_idx = 0; res = platform_get_resource(ldb->pdev, IORESOURCE_MEM, 0); @@ -619,10 +624,12 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, } ldb->inited = true; + + i2c_dev = ldb_i2c_client[lvds_channel]; + } else { /* second time for separate mode */ char di_clk[] = "ipu1_di0_clk"; char ldb_clk[] = "ldb_di0_clk"; - int lvds_channel; if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1) || @@ -701,9 +708,16 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, return PTR_ERR(ldb->setting[setting_idx].di_clk); } + i2c_dev = ldb_i2c_client[lvds_channel]; + dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s\n", ldb_clk, di_clk); } + if (i2c_dev) + mxc_dispdrv_setdev(ldb->disp_ldb, &i2c_dev->dev); + else + mxc_dispdrv_setdev(ldb->disp_ldb, NULL); + ldb->setting[setting_idx].ch_mask = ch_mask; ldb->setting[setting_idx].ch_val = ch_val; @@ -734,6 +748,23 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, } } + /* get screen size in edid */ + if (i2c_dev) { + ret = mxc_ldb_edidread(i2c_dev); + if (ret > 0) { + fb_edid_to_monspecs(&g_edid[lvds_channel][0], + &setting->fbi->monspecs); + /* centimeter to millimeter */ + setting->fbi->var.width = + setting->fbi->monspecs.max_x * 10; + setting->fbi->var.height = + setting->fbi->monspecs.max_y * 10; + } else { + /* ignore i2c access failure */ + ret = 0; + } + } + /* save current ldb setting for fb notifier */ ldb->setting[setting_idx].active = true; ldb->setting[setting_idx].ipu = setting->dev_id; @@ -791,6 +822,108 @@ static int ldb_resume(struct platform_device *pdev) return 0; } + +static int mxc_ldb_edidread(struct i2c_client *client) +{ + int ret = 0; + unsigned char regaddr = 0; + struct i2c_adapter *adp = client->adapter; + int ldb_id = (int)client->dev.platform_data; + struct i2c_msg msg[2] = { + { + .addr = 0x50, + .flags = 0, + .len = 1, + .buf = ®addr, + }, { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 512, + .buf = &g_edid[ldb_id][0], + }, + }; + + ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + return -EIO; + } + + return ret; +} + +static ssize_t mxc_ldb_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + + ret = mxc_ldb_edidread(client); + if (ret < 0) + strcpy(buf, "plugout\n"); + else + strcpy(buf, "plugin\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(cable_state, S_IRUGO, mxc_ldb_show_state, NULL); + +static int __devinit mxc_ldb_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + int ldb_id = (int)client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -ENODEV; + + ldb_i2c_client[ldb_id] = client; + + ret = device_create_file(&client->dev, &dev_attr_cable_state); + if (ret < 0) + dev_warn(&client->dev, + "cound not create sys node for cable state\n"); + + + return 0; +} + +static int __devexit mxc_ldb_i2c_remove(struct i2c_client *client) +{ + int ldb_id = (int)client->dev.platform_data; + ldb_i2c_client[ldb_id] = NULL; + return 0; +} + +static const struct i2c_device_id mxc_ldb_i2c_id[] = { + { "mxc_ldb_i2c", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mxc_ldb_i2c_id); + +static struct i2c_driver mxc_ldb_i2c_driver = { + .driver = { + .name = "mxc_ldb_i2c", + }, + .probe = mxc_ldb_i2c_probe, + .remove = mxc_ldb_i2c_remove, + .id_table = mxc_ldb_i2c_id, +}; + +static int __init mxc_ldb_i2c_init(void) +{ + return i2c_add_driver(&mxc_ldb_i2c_driver); +} + +static void __exit mxc_ldb_i2c_exit(void) +{ + i2c_del_driver(&mxc_ldb_i2c_driver); +} + +module_init(mxc_ldb_i2c_init); +module_exit(mxc_ldb_i2c_exit); + /*! * This function is called by the driver framework to initialize the LDB * device. diff --git a/drivers/video/mxc/mipi_dsi.c b/drivers/video/mxc/mipi_dsi.c index 8434e14f9746..73c2dda062be 100644 --- a/drivers/video/mxc/mipi_dsi.c +++ b/drivers/video/mxc/mipi_dsi.c @@ -941,6 +941,8 @@ static int mipi_dsi_probe(struct platform_device *pdev) mxc_dispdrv_setdata(mipi_dsi->disp_mipi, mipi_dsi); dev_set_drvdata(&pdev->dev, mipi_dsi); + mxc_dispdrv_setdev(mipi_dsi->disp_mipi, &pdev->dev); + dev_info(&pdev->dev, "i.MX MIPI DSI driver probed\n"); return 0; diff --git a/drivers/video/mxc/mxc_dispdrv.c b/drivers/video/mxc/mxc_dispdrv.c index 3a8a8d216eb9..3d1cb43ec7ab 100644 --- a/drivers/video/mxc/mxc_dispdrv.c +++ b/drivers/video/mxc/mxc_dispdrv.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -48,8 +48,23 @@ struct mxc_dispdrv_entry { bool active; void *priv; struct list_head list; + struct device *dev; }; +void mxc_dispdrv_setdev(struct mxc_dispdrv_handle *drv_handle, struct device *dev) +{ + struct mxc_dispdrv_entry *dentry; + dentry = (struct mxc_dispdrv_entry *)drv_handle; + dentry->dev = dev; +} + +struct device *mxc_dispdrv_getdev(struct mxc_dispdrv_handle *drv_handle) +{ + struct mxc_dispdrv_entry *dentry; + dentry = (struct mxc_dispdrv_entry *)drv_handle; + return dentry->dev; +} + struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv) { struct mxc_dispdrv_entry *new; diff --git a/drivers/video/mxc/mxc_dispdrv.h b/drivers/video/mxc/mxc_dispdrv.h index c6d05fa37799..9a722176961d 100644 --- a/drivers/video/mxc/mxc_dispdrv.h +++ b/drivers/video/mxc/mxc_dispdrv.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -49,4 +49,6 @@ struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name, void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle); int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data); void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle); +void mxc_dispdrv_setdev(struct mxc_dispdrv_handle *drv_handle, struct device *dev); +struct device *mxc_dispdrv_getdev(struct mxc_dispdrv_handle *drv_handle); #endif diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c index 6624fd0e867e..0fd076537fb8 100644 --- a/drivers/video/mxc/mxc_elcdif_fb.c +++ b/drivers/video/mxc/mxc_elcdif_fb.c @@ -1164,6 +1164,8 @@ static int mxc_elcdif_fb_blank(int blank, struct fb_info *info) return ret; if (blank == FB_BLANK_UNBLANK) { + info->var.activate = (info->var.activate & ~FB_ACTIVATE_MASK) | + FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; ret = mxc_elcdif_fb_set_par(info); if (ret) return ret; diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 0818a328c4de..429b7b863c24 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -46,6 +46,8 @@ #include <linux/mxcfb.h> #include <linux/uaccess.h> #include <linux/fsl_devices.h> +#include <linux/earlysuspend.h> +#include <linux/workqueue.h> #include <asm/mach-types.h> #include <mach/ipu-v3.h> #include "mxc_dispdrv.h" @@ -61,6 +63,8 @@ * Structure containing the MXC specific framebuffer information. */ struct mxcfb_info { + spinlock_t lock; + struct fb_info *fbi; int default_bpp; int cur_blank; int next_blank; @@ -80,6 +84,7 @@ struct mxcfb_info { uint32_t alpha_mem_len; uint32_t ipu_ch_irq; uint32_t ipu_ch_nf_irq; + uint32_t ipu_vsync_pre_irq; uint32_t ipu_alp_ch_irq; uint32_t cur_ipu_buf; uint32_t cur_ipu_alpha_buf; @@ -97,6 +102,18 @@ struct mxcfb_info { struct mxc_dispdrv_handle *dispdrv; struct fb_var_screeninfo cur_var; + + int vsync_pre_report_active; + ktime_t vsync_pre_timestamp; + ktime_t vsync_nf_timestamp; + struct workqueue_struct *vsync_pre_queue; + struct work_struct vsync_pre_work; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend fbdrv_earlysuspend; +#endif + int panel_width_mm; + int panel_height_mm; }; struct mxcfb_pfmt { @@ -135,6 +152,11 @@ enum { static bool g_dp_in_use[2]; LIST_HEAD(fb_alloc_list); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mxcfb_early_suspend(struct early_suspend *h); +static void mxcfb_later_resume(struct early_suspend *h); +#endif + /* Return default standard(RGB) pixel format */ static uint32_t bpp_to_pixfmt(int bpp) { @@ -251,6 +273,7 @@ static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id) static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id); static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id); +static irqreturn_t mxcfb_vsync_pre_irq_handler(int irq, void *dev_id); static int mxcfb_blank(int blank, struct fb_info *info); static int mxcfb_map_video_memory(struct fb_info *fbi); static int mxcfb_unmap_video_memory(struct fb_info *fbi); @@ -894,8 +917,16 @@ static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->pixclock); } - var->height = -1; - var->width = -1; + if (var->height == 0 && mxc_fbi->panel_height_mm) + var->height = mxc_fbi->panel_height_mm; + else if (var->height == 0) + var->height = -1; + + if (var->width == 0 && mxc_fbi->panel_width_mm) + var->width = mxc_fbi->panel_width_mm; + else if (var->width == 0) + var->width = -1; + var->grayscale = 0; return 0; @@ -947,6 +978,35 @@ static int mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, return ret; } +static void mxcfb_vsync_pre_work(struct work_struct *work) +{ + struct mxcfb_info *mxc_fbi = + container_of(work, struct mxcfb_info, vsync_pre_work); + char *envp[2]; + char buf[64]; + unsigned long flags; + + spin_lock_irqsave(&mxc_fbi->lock, flags); + snprintf(buf, sizeof(buf), "VSYNC=%llu", + ktime_to_ns(mxc_fbi->vsync_pre_timestamp)); + spin_unlock_irqrestore(&mxc_fbi->lock, flags); + + envp[0] = buf; + envp[1] = NULL; + kobject_uevent_env(&mxc_fbi->fbi->dev->kobj, KOBJ_CHANGE, envp); +} + +static void mxcfb_enable_vsync_pre(struct mxcfb_info *mxc_fbi) +{ + ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_vsync_pre_irq); + ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_vsync_pre_irq); +} + +static void mxcfb_disable_vsync_pre(struct mxcfb_info *mxc_fbi) +{ + ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_vsync_pre_irq); +} + /* * Function to handle custom ioctls for MXC framebuffer. * @@ -1136,6 +1196,8 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) } case MXCFB_WAIT_FOR_VSYNC: { + unsigned long flags; + unsigned long long timestamp; if (mxc_fbi->ipu_ch == MEM_FG_SYNC) { /* BG should poweron */ struct mxcfb_info *bg_mxcfbi = NULL; @@ -1163,7 +1225,18 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); retval = wait_for_completion_interruptible_timeout( - &mxc_fbi->vsync_complete, 1 * HZ); + &mxc_fbi->vsync_complete, HZ/10); + + spin_lock_irqsave(&mxc_fbi->lock, flags); + timestamp = ktime_to_ns(mxc_fbi->vsync_nf_timestamp); + spin_unlock_irqrestore(&mxc_fbi->lock, flags); + dev_vdbg(fbi->device, "ts = %llu", timestamp); + + if (copy_to_user((void *)arg, ×tamp, sizeof(timestamp))) { + retval = -EFAULT; + break; + } + if (retval == 0) { dev_err(fbi->device, "MXCFB_WAIT_FOR_VSYNC: timeout %d\n", @@ -1331,6 +1404,27 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) break; } + case MXCFB_ENABLE_VSYNC_EVENT: + { + int enable; + if (get_user(enable, (int __user *)arg)) + return -EFAULT; + + if (mxc_fbi->ipu_ch == MEM_FG_SYNC) + return -EINVAL; + + /* Only can control the vsync state when screen is not blank */ + if (mxc_fbi->vsync_pre_report_active != enable && + mxc_fbi->cur_blank == FB_BLANK_UNBLANK) { + if (enable) + mxcfb_enable_vsync_pre(mxc_fbi); + else + mxcfb_disable_vsync_pre(mxc_fbi); + } + + mxc_fbi->vsync_pre_report_active = enable; + break; + } case MXCFB_CSC_UPDATE: { struct mxcfb_csc_matrix csc; @@ -1648,10 +1742,25 @@ static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id) struct fb_info *fbi = dev_id; struct mxcfb_info *mxc_fbi = fbi->par; + spin_lock(&mxc_fbi->lock); + mxc_fbi->vsync_nf_timestamp = ktime_get(); + spin_unlock(&mxc_fbi->lock); complete(&mxc_fbi->vsync_complete); return IRQ_HANDLED; } +static irqreturn_t mxcfb_vsync_pre_irq_handler(int irq, void *dev_id) +{ + struct fb_info *fbi = dev_id; + struct mxcfb_info *mxc_fbi = fbi->par; + + spin_lock(&mxc_fbi->lock); + mxc_fbi->vsync_pre_timestamp = ktime_get(); + spin_unlock(&mxc_fbi->lock); + queue_work(mxc_fbi->vsync_pre_queue, &mxc_fbi->vsync_pre_work); + return IRQ_HANDLED; +} + static irqreturn_t mxcfb_alpha_irq_handler(int irq, void *dev_id) { struct fb_info *fbi = dev_id; @@ -1664,7 +1773,7 @@ static irqreturn_t mxcfb_alpha_irq_handler(int irq, void *dev_id) /* * Suspends the framebuffer and blanks the screen. Power management support */ -static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state) +static int mxcfb_core_suspend(struct platform_device *pdev, pm_message_t state) { struct fb_info *fbi = platform_get_drvdata(pdev); struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; @@ -1696,9 +1805,23 @@ static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state) } /* + * Suspends the framebuffer and blanks the screen. Power management support + */ +static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + if (strstr(mxc_fbi->dispdrv->drv->name, "hdmi")) + return mxcfb_core_suspend(pdev, state); + + return 0; +} + +/* * Resumes the framebuffer and unblanks the screen. Power management support */ -static int mxcfb_resume(struct platform_device *pdev) +static int mxcfb_core_resume(struct platform_device *pdev) { struct fb_info *fbi = platform_get_drvdata(pdev); struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; @@ -1721,6 +1844,20 @@ static int mxcfb_resume(struct platform_device *pdev) } /* + * Resumes the framebuffer and unblanks the screen. Power management support + */ +static int mxcfb_resume(struct platform_device *pdev) +{ + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + if (strstr(mxc_fbi->dispdrv->drv->name, "hdmi")) + return mxcfb_core_resume(pdev); + + return 0; +} + +/* * Main framebuffer functions */ @@ -2066,6 +2203,18 @@ static int mxcfb_register(struct fb_info *fbi) } ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq); + if (mxcfbi->ipu_vsync_pre_irq != -1) { + if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_vsync_pre_irq, + mxcfb_vsync_pre_irq_handler, 0, + MXCFB_NAME, fbi) != 0) { + dev_err(fbi->device, "Error registering VSYNC irq " + "handler.\n"); + ret = -EBUSY; + goto err2; + } + ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_vsync_pre_irq); + } + if (mxcfbi->ipu_alp_ch_irq != -1) if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, mxcfb_alpha_irq_handler, IPU_IRQF_ONESHOT, @@ -2073,7 +2222,7 @@ static int mxcfb_register(struct fb_info *fbi) dev_err(fbi->device, "Error registering alpha irq " "handler.\n"); ret = -EBUSY; - goto err2; + goto err3; } if (!mxcfbi->late_init) { @@ -2085,7 +2234,7 @@ static int mxcfb_register(struct fb_info *fbi) console_unlock(); if (ret < 0) { dev_err(fbi->device, "Error fb_set_var ret:%d\n", ret); - goto err3; + goto err4; } if (mxcfbi->next_blank == FB_BLANK_UNBLANK) { @@ -2095,7 +2244,7 @@ static int mxcfb_register(struct fb_info *fbi) if (ret < 0) { dev_err(fbi->device, "Error fb_blank ret:%d\n", ret); - goto err4; + goto err5; } } } else { @@ -2112,13 +2261,12 @@ static int mxcfb_register(struct fb_info *fbi) } } - ret = register_framebuffer(fbi); if (ret < 0) - goto err5; + goto err6; return ret; -err5: +err6: if (mxcfbi->next_blank == FB_BLANK_UNBLANK) { console_lock(); if (!mxcfbi->late_init) @@ -2130,10 +2278,13 @@ err5: } console_unlock(); } +err5: err4: -err3: if (mxcfbi->ipu_alp_ch_irq != -1) ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi); +err3: + if (mxcfbi->ipu_vsync_pre_irq != -1) + ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_vsync_pre_irq, fbi); err2: ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi); err1: @@ -2180,6 +2331,8 @@ static int mxcfb_setup_overlay(struct platform_device *pdev, mxcfbi_fg->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF; mxcfbi_fg->ipu_ch_nf_irq = IPU_IRQ_FG_SYNC_NFACK; mxcfbi_fg->ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF; + /* Vsync-pre irq is invalid for overlay channel. */ + mxcfbi_fg->ipu_vsync_pre_irq = -1; mxcfbi_fg->ipu_ch = MEM_FG_SYNC; mxcfbi_fg->ipu_di = -1; mxcfbi_fg->ipu_di_pix_fmt = mxcfbi_bg->ipu_di_pix_fmt; @@ -2255,6 +2408,8 @@ static int mxcfb_probe(struct platform_device *pdev) struct fb_info *fbi; struct mxcfb_info *mxcfbi; struct resource *res; + struct device *disp_dev; + char buf[32]; int ret = 0; /* Initialize FB structures */ @@ -2269,9 +2424,14 @@ static int mxcfb_probe(struct platform_device *pdev) goto get_fb_option_failed; mxcfbi = (struct mxcfb_info *)fbi->par; + spin_lock_init(&mxcfbi->lock); + mxcfbi->fbi = fbi; mxcfbi->ipu_int_clk = plat_data->int_clk; mxcfbi->late_init = plat_data->late_init; mxcfbi->first_set_par = true; + mxcfbi->panel_width_mm = plat_data->panel_width_mm; + mxcfbi->panel_height_mm = plat_data->panel_height_mm; + ret = mxcfb_dispdrv_init(pdev, fbi); if (ret < 0) goto init_dispdrv_failed; @@ -2304,6 +2464,9 @@ static int mxcfb_probe(struct platform_device *pdev) mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF; mxcfbi->ipu_ch_nf_irq = IPU_IRQ_BG_SYNC_NFACK; mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF; + mxcfbi->ipu_vsync_pre_irq = mxcfbi->ipu_di ? + IPU_IRQ_VSYNC_PRE_1 : + IPU_IRQ_VSYNC_PRE_0; mxcfbi->ipu_ch = MEM_BG_SYNC; /* Unblank the primary fb only by default */ if (pdev->id == 0) @@ -2346,6 +2509,9 @@ static int mxcfb_probe(struct platform_device *pdev) mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF; mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK; mxcfbi->ipu_alp_ch_irq = -1; + mxcfbi->ipu_vsync_pre_irq = mxcfbi->ipu_di ? + IPU_IRQ_VSYNC_PRE_1 : + IPU_IRQ_VSYNC_PRE_0; mxcfbi->ipu_ch = MEM_DC_SYNC; mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN; @@ -2366,6 +2532,34 @@ static int mxcfb_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Error %d on creating file for disp " " device propety\n", ret); + disp_dev = mxc_dispdrv_getdev(mxcfbi->dispdrv); + if (disp_dev) { + ret = sysfs_create_link(&fbi->dev->kobj, + &disp_dev->kobj, "disp_dev"); + if (ret) + dev_err(&pdev->dev, + "Error %d on creating file\n", ret); + } + + INIT_WORK(&mxcfbi->vsync_pre_work, mxcfb_vsync_pre_work); + + snprintf(buf, sizeof(buf), "mxcfb%d-vsync-pre", fbi->node); + mxcfbi->vsync_pre_queue = create_singlethread_workqueue(buf); + if (mxcfbi->vsync_pre_queue == NULL) { + dev_err(fbi->device, + "Failed to alloc vsync-pre workqueue\n"); + ret = -ENOMEM; + goto workqueue_alloc_failed; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + mxcfbi->fbdrv_earlysuspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + mxcfbi->fbdrv_earlysuspend.suspend = mxcfb_early_suspend; + mxcfbi->fbdrv_earlysuspend.resume = mxcfb_later_resume; + mxcfbi->fbdrv_earlysuspend.data = pdev; + register_early_suspend(&mxcfbi->fbdrv_earlysuspend); +#endif + #ifdef CONFIG_LOGO fb_prepare_logo(fbi, 0); fb_show_logo(fbi, 0); @@ -2373,6 +2567,7 @@ static int mxcfb_probe(struct platform_device *pdev) return 0; +workqueue_alloc_failed: mxcfb_setupoverlay_failed: mxcfb_register_failed: get_ipu_failed: @@ -2394,6 +2589,10 @@ static int mxcfb_remove(struct platform_device *pdev) if (!fbi) return 0; +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&mxc_fbi->fbdrv_earlysuspend); +#endif + device_remove_file(fbi->dev, &dev_attr_fsl_disp_dev_property); device_remove_file(fbi->dev, &dev_attr_fsl_disp_property); mxcfb_blank(FB_BLANK_POWERDOWN, fbi); @@ -2430,6 +2629,52 @@ static struct platform_driver mxcfb_driver = { .resume = mxcfb_resume, }; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mxcfb_early_suspend(struct early_suspend *h) +{ + struct platform_device *pdev = (struct platform_device *)h->data; + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par; + pm_message_t state = { .event = PM_EVENT_SUSPEND }; + struct fb_event event; + int blank = FB_BLANK_POWERDOWN; + + if (mxcfbi->ipu_ch == MEM_FG_SYNC) + return; + + if (strstr(mxcfbi->dispdrv->drv->name, "hdmi")) { + /* Only black the hdmi fb due to audio dependency */ + memset(fbi->screen_base, 0, fbi->fix.smem_len); + return; + } + + mxcfb_core_suspend(pdev, state); + event.info = fbi; + event.data = ␣ + fb_notifier_call_chain(FB_EVENT_BLANK, &event); +} + +static void mxcfb_later_resume(struct early_suspend *h) +{ + struct platform_device *pdev = (struct platform_device *)h->data; + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par; + struct fb_event event; + + if (mxcfbi->ipu_ch == MEM_FG_SYNC) + return; + + /* HDMI resume function has been called */ + if (strstr(mxcfbi->dispdrv->drv->name, "hdmi")) + return; + + mxcfb_core_resume(pdev); + event.info = fbi; + event.data = &mxcfbi->next_blank; + fb_notifier_call_chain(FB_EVENT_BLANK, &event); +} +#endif + /*! * Main entry function for the framebuffer. The function registers the power * management callback functions with the kernel and also registers the MXCFB |