summaryrefslogtreecommitdiff
path: root/drivers/video/mxc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/mxc')
-rw-r--r--drivers/video/mxc/ldb.c137
-rw-r--r--drivers/video/mxc/mipi_dsi.c2
-rw-r--r--drivers/video/mxc/mxc_dispdrv.c17
-rw-r--r--drivers/video/mxc/mxc_dispdrv.h4
-rw-r--r--drivers/video/mxc/mxc_elcdif_fb.c2
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c269
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 = &regaddr,
+ }, {
+ .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, &timestamp, 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 = &blank;
+ 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