summaryrefslogtreecommitdiff
path: root/drivers/video/mxc/mxc_ipuv3_fb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/mxc/mxc_ipuv3_fb.c')
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c510
1 files changed, 370 insertions, 140 deletions
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
index 76c6d523c291..cc48e62637ca 100644
--- a/drivers/video/mxc/mxc_ipuv3_fb.c
+++ b/drivers/video/mxc/mxc_ipuv3_fb.c
@@ -22,7 +22,6 @@
*
* @ingroup Framebuffer
*/
-
/*!
* Include files
*/
@@ -44,9 +43,13 @@
#include <linux/io.h>
#include <linux/ipu.h>
#include <linux/mxcfb.h>
+#include <linux/earlysuspend.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
+#include <linux/suspend.h>
+
+static int vt_switch;
/*
* Driver name
@@ -56,11 +59,14 @@
* Structure containing the MXC specific framebuffer information.
*/
struct mxcfb_info {
+ char *fb_mode_str;
+ int default_bpp;
int cur_blank;
int next_blank;
ipu_channel_t ipu_ch;
int ipu_di;
u32 ipu_di_pix_fmt;
+ bool ipu_ext_clk;
bool overlay;
bool alpha_chan_en;
dma_addr_t alpha_phy_addr0;
@@ -74,6 +80,8 @@ struct mxcfb_info {
u32 pseudo_palette[16];
+ bool wait4vsync;
+ uint32_t waitcnt;
struct semaphore flip_sem;
struct semaphore alpha_flip_sem;
struct completion vsync_complete;
@@ -93,12 +101,10 @@ enum {
BOTH_OFF
};
-static char *fb_mode;
-static unsigned long default_bpp = 16;
+#define FB_DEVICE_NUM 3
static bool g_dp_in_use;
LIST_HEAD(fb_alloc_list);
-static struct fb_info *mxcfb_info[3];
-static int ext_clk_used;
+static struct fb_info *mxcfb_info[FB_DEVICE_NUM];
static uint32_t bpp_to_pixfmt(struct fb_info *fbi)
{
@@ -125,6 +131,7 @@ static irqreturn_t mxcfb_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);
+static int mxcfb_option_setup(struct fb_info *info, char *options);
/*
* Set fixed framebuffer parameters based on variable settings.
@@ -165,30 +172,42 @@ static int _setup_disp_channel1(struct fb_info *fbi)
for (i = 0; i < num_registered_fb; i++) {
mxc_fbi_tmp = (struct mxcfb_info *)
- (registered_fb[i]->par);
+ (registered_fb[i]->par);
if (mxc_fbi_tmp->ipu_ch == MEM_BG_SYNC) {
- fbi->var.vmode =
- registered_fb[i]->var.vmode;
+ fbi->var.vmode = registered_fb[i]->var.vmode;
mxc_fbi->ipu_di_pix_fmt =
- mxc_fbi_tmp->ipu_di_pix_fmt;
+ mxc_fbi_tmp->ipu_di_pix_fmt;
break;
}
}
}
- if (fbi->var.vmode & FB_VMODE_INTERLACED) {
- params.mem_dp_bg_sync.interlaced = true;
- params.mem_dp_bg_sync.out_pixel_fmt =
- IPU_PIX_FMT_YUV444;
+ if (mxc_fbi->ipu_ch == MEM_DC_SYNC) {
+ if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+ params.mem_dc_sync.interlaced = true;
+ params.mem_dc_sync.out_pixel_fmt =
+ IPU_PIX_FMT_YUV444;
+ } else {
+ if (mxc_fbi->ipu_di_pix_fmt)
+ params.mem_dc_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+ else
+ params.mem_dc_sync.out_pixel_fmt = IPU_PIX_FMT_RGB666;
+ }
+ params.mem_dc_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
} else {
- if (mxc_fbi->ipu_di_pix_fmt)
- params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
- else
- params.mem_dp_bg_sync.out_pixel_fmt = IPU_PIX_FMT_RGB666;
+ if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+ params.mem_dp_bg_sync.interlaced = true;
+ params.mem_dp_bg_sync.out_pixel_fmt =
+ IPU_PIX_FMT_YUV444;
+ } else {
+ if (mxc_fbi->ipu_di_pix_fmt)
+ params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+ else
+ params.mem_dp_bg_sync.out_pixel_fmt = IPU_PIX_FMT_RGB666;
+ }
+ params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
+ if (mxc_fbi->alpha_chan_en)
+ params.mem_dp_bg_sync.alpha_chan_en = true;
}
- params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
- if (mxc_fbi->alpha_chan_en)
- params.mem_dp_bg_sync.alpha_chan_en = true;
-
ipu_init_channel(mxc_fbi->ipu_ch, &params);
return 0;
@@ -201,10 +220,12 @@ static int _setup_disp_channel2(struct fb_info *fbi)
int fb_stride;
switch (bpp_to_pixfmt(fbi)) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YVU420P:
+ case IPU_PIX_FMT_NV12:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV420P:
fb_stride = fbi->var.xres_virtual;
break;
default:
@@ -212,7 +233,7 @@ static int _setup_disp_channel2(struct fb_info *fbi)
}
mxc_fbi->cur_ipu_buf = 1;
- sema_init(&mxc_fbi->flip_sem, 1);
+ sema_init(&mxc_fbi->flip_sem, 0);
if (mxc_fbi->alpha_chan_en) {
mxc_fbi->cur_ipu_alpha_buf = 1;
sema_init(&mxc_fbi->alpha_flip_sem, 1);
@@ -226,8 +247,7 @@ static int _setup_disp_channel2(struct fb_info *fbi)
IPU_ROTATE_NONE,
fbi->fix.smem_start +
(fbi->fix.line_length * fbi->var.yres),
- fbi->fix.smem_start,
- 0, 0);
+ fbi->fix.smem_start, 0, 0);
if (retval) {
dev_err(fbi->device,
"ipu_init_channel_buffer error %d\n", retval);
@@ -297,38 +317,47 @@ static int mxcfb_set_par(struct fb_info *fbi)
mxc_fbi->alpha_phy_addr1);
mxc_fbi->alpha_virt_addr0 =
- dma_alloc_coherent(fbi->device,
- alpha_mem_len,
- &mxc_fbi->alpha_phy_addr0,
- GFP_DMA | GFP_KERNEL);
+ dma_alloc_coherent(fbi->device,
+ alpha_mem_len,
+ &mxc_fbi->alpha_phy_addr0,
+ GFP_DMA | GFP_KERNEL);
mxc_fbi->alpha_virt_addr1 =
- dma_alloc_coherent(fbi->device,
- alpha_mem_len,
- &mxc_fbi->alpha_phy_addr1,
- GFP_DMA | GFP_KERNEL);
+ dma_alloc_coherent(fbi->device,
+ alpha_mem_len,
+ &mxc_fbi->alpha_phy_addr1,
+ GFP_DMA | GFP_KERNEL);
if (mxc_fbi->alpha_virt_addr0 == NULL ||
mxc_fbi->alpha_virt_addr1 == NULL) {
dev_err(fbi->device, "mxcfb: dma alloc for"
" alpha buffer failed.\n");
if (mxc_fbi->alpha_virt_addr0)
dma_free_coherent(fbi->device,
- mxc_fbi->alpha_mem_len,
- mxc_fbi->alpha_virt_addr0,
- mxc_fbi->alpha_phy_addr0);
+ mxc_fbi->
+ alpha_mem_len,
+ mxc_fbi->
+ alpha_virt_addr0,
+ mxc_fbi->
+ alpha_phy_addr0);
if (mxc_fbi->alpha_virt_addr1)
dma_free_coherent(fbi->device,
- mxc_fbi->alpha_mem_len,
- mxc_fbi->alpha_virt_addr1,
- mxc_fbi->alpha_phy_addr1);
+ mxc_fbi->
+ alpha_mem_len,
+ mxc_fbi->
+ alpha_virt_addr1,
+ mxc_fbi->
+ alpha_phy_addr1);
return -ENOMEM;
}
mxc_fbi->alpha_mem_len = alpha_mem_len;
}
}
+#if !(defined(CONFIG_CCWMX51_DISP0) && defined(CONFIG_CCWMX51_DISP1))
+ /* FIXME this lines of code doesnt allow to run the dual head... */
if (mxc_fbi->next_blank != FB_BLANK_UNBLANK)
return retval;
+#endif
_setup_disp_channel1(fbi);
@@ -345,9 +374,9 @@ static int mxcfb_set_par(struct fb_info *fbi)
else
out_pixel_fmt = IPU_PIX_FMT_RGB666;
}
- if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
+ if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
sig_cfg.odd_field_first = true;
- if ((fbi->var.sync & FB_SYNC_EXT) || ext_clk_used)
+ if ((fbi->var.sync & FB_SYNC_EXT) || mxc_fbi->ipu_ext_clk)
sig_cfg.ext_clk = true;
if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
sig_cfg.Hsync_pol = true;
@@ -453,8 +482,7 @@ static int swap_channels(struct fb_info *fbi)
ch_to = MEM_BG_SYNC;
for (i = 0; i < num_registered_fb; i++) {
- mxc_fbi_to =
- (struct mxcfb_info *)mxcfb_info[i]->par;
+ mxc_fbi_to = (struct mxcfb_info *)mxcfb_info[i]->par;
if (mxc_fbi_to->ipu_ch == ch_to) {
fbi_to = mxcfb_info[i];
break;
@@ -519,14 +547,14 @@ static int swap_channels(struct fb_info *fbi)
}
if (ipu_request_irq(mxc_fbi_from->ipu_ch_irq, mxcfb_irq_handler, 0,
- MXCFB_NAME, fbi) != 0) {
+ MXCFB_NAME, fbi) != 0) {
dev_err(fbi->device, "Error registering irq %d\n",
mxc_fbi_from->ipu_ch_irq);
return -EBUSY;
}
ipu_disable_irq(mxc_fbi_from->ipu_ch_irq);
if (ipu_request_irq(mxc_fbi_to->ipu_ch_irq, mxcfb_irq_handler, 0,
- MXCFB_NAME, fbi_to) != 0) {
+ MXCFB_NAME, fbi_to) != 0) {
dev_err(fbi_to->device, "Error registering irq %d\n",
mxc_fbi_to->ipu_ch_irq);
return -EBUSY;
@@ -545,17 +573,48 @@ static int swap_channels(struct fb_info *fbi)
*/
static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
u32 vtotal;
u32 htotal;
+ /* fg should not bigger than bg */
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+ struct fb_info *fbi_tmp;
+ struct mxcfb_info *mxc_fbi_tmp;
+ int i, bg_xres, bg_yres;
+ int16_t pos_x, pos_y;
+
+ bg_xres = var->xres;
+ bg_yres = var->yres;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ fbi_tmp = registered_fb[i];
+ mxc_fbi_tmp = (struct mxcfb_info *)
+ (fbi_tmp->par);
+ if (mxc_fbi_tmp->ipu_ch == MEM_BG_SYNC) {
+ bg_xres = fbi_tmp->var.xres;
+ bg_yres = fbi_tmp->var.yres;
+ break;
+ }
+ }
+
+ ipu_disp_get_window_pos(mxc_fbi->ipu_ch, &pos_x, &pos_y);
+
+ if ((var->xres + pos_x) > bg_xres)
+ var->xres = bg_xres - pos_x;
+ if ((var->yres + pos_y) > bg_yres)
+ var->yres = bg_yres - pos_y;
+ }
+
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
- (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8))
- var->bits_per_pixel = default_bpp;
+ (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) &&
+ (var->bits_per_pixel != 8))
+ var->bits_per_pixel = mxc_fbi->default_bpp;
switch (var->bits_per_pixel) {
case 8:
@@ -723,7 +782,7 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
}
if (ipu_disp_set_global_alpha(mxc_fbi->ipu_ch,
- (bool)ga.enable,
+ (bool) ga.enable,
ga.alpha)) {
retval = -EINVAL;
break;
@@ -750,7 +809,7 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
}
if (ipu_disp_set_global_alpha(mxc_fbi->ipu_ch,
- !(bool)la.enable, 0)) {
+ !(bool) la.enable, 0)) {
retval = -EINVAL;
break;
}
@@ -765,8 +824,11 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
for (i = 0; i < num_registered_fb; i++) {
char *idstr = registered_fb[i]->fix.id;
- if (strcmp(idstr, video_plane_idstr) == 0) {
- ((struct mxcfb_info *)(registered_fb[i]->par))->alpha_chan_en = false;
+ if (strcmp(idstr, video_plane_idstr) ==
+ 0) {
+ ((struct mxcfb_info
+ *)(registered_fb[i]->par))->
+ alpha_chan_en = false;
break;
}
}
@@ -794,8 +856,8 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
uint32_t ipu_alp_ch_irq;
if (!(((mxc_fbi->ipu_ch == MEM_FG_SYNC) ||
- (mxc_fbi->ipu_ch == MEM_BG_SYNC)) &&
- (mxc_fbi->alpha_chan_en))) {
+ (mxc_fbi->ipu_ch == MEM_BG_SYNC)) &&
+ (mxc_fbi->alpha_chan_en))) {
dev_err(fbi->device,
"Should use background or overlay "
"framebuffer to set the alpha buffer "
@@ -822,11 +884,10 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
down(&mxc_fbi->alpha_flip_sem);
mxc_fbi->cur_ipu_alpha_buf =
- !mxc_fbi->cur_ipu_alpha_buf;
+ !mxc_fbi->cur_ipu_alpha_buf;
if (ipu_update_channel_buffer(mxc_fbi->ipu_ch,
IPU_ALPHA_IN_BUFFER,
- mxc_fbi->
- cur_ipu_alpha_buf,
+ mxc_fbi->cur_ipu_alpha_buf,
base) == 0) {
ipu_select_buffer(mxc_fbi->ipu_ch,
IPU_ALPHA_IN_BUFFER,
@@ -864,9 +925,9 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
}
retval = ipu_disp_set_gamma_correction(mxc_fbi->ipu_ch,
- gamma.enable,
- gamma.constk,
- gamma.slopek);
+ gamma.enable,
+ gamma.constk,
+ gamma.slopek);
break;
}
case MXCFB_WAIT_FOR_VSYNC:
@@ -876,7 +937,8 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
int i;
for (i = 0; i < num_registered_fb; i++) {
bg_mxcfbi =
- ((struct mxcfb_info *)(registered_fb[i]->par));
+ ((struct mxcfb_info
+ *)(registered_fb[i]->par));
if (bg_mxcfbi->ipu_ch == MEM_BG_SYNC)
break;
@@ -891,21 +953,25 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
}
- down(&mxc_fbi->flip_sem);
init_completion(&mxc_fbi->vsync_complete);
ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ mxc_fbi->wait4vsync = 1;
ipu_enable_irq(mxc_fbi->ipu_ch_irq);
- retval = wait_for_completion_interruptible_timeout(
- &mxc_fbi->vsync_complete, 1 * HZ);
+ retval =
+ wait_for_completion_interruptible_timeout(&mxc_fbi->
+ vsync_complete,
+ 1 * HZ);
if (retval == 0) {
dev_err(fbi->device,
"MXCFB_WAIT_FOR_VSYNC: timeout %d\n",
retval);
+ mxc_fbi->wait4vsync = 0;
retval = -ETIME;
} else if (retval > 0) {
retval = 0;
}
+
break;
}
case FBIO_ALLOC:
@@ -986,7 +1052,8 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
for (i = 0; i < num_registered_fb; i++) {
bg_mxcfbi =
- ((struct mxcfb_info *)(registered_fb[i]->par));
+ ((struct mxcfb_info *)(registered_fb[i]->
+ par));
if (bg_mxcfbi->ipu_ch == MEM_BG_SYNC) {
bg_fbi = registered_fb[i];
@@ -1005,13 +1072,15 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
if (bg_fbi->var.xres < fbi->var.xres)
pos.x = 0;
else
- pos.x = bg_fbi->var.xres - fbi->var.xres;
+ pos.x =
+ bg_fbi->var.xres - fbi->var.xres;
}
if (fbi->var.yres + pos.y > bg_fbi->var.yres) {
if (bg_fbi->var.yres < fbi->var.yres)
pos.y = 0;
else
- pos.y = bg_fbi->var.yres - fbi->var.yres;
+ pos.y =
+ bg_fbi->var.yres - fbi->var.yres;
}
retval = ipu_disp_set_window_pos(mxc_fbi->ipu_ch,
@@ -1026,12 +1095,39 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
case MXCFB_GET_FB_IPU_CHAN:
{
struct mxcfb_info *mxc_fbi =
- (struct mxcfb_info *)fbi->par;
+ (struct mxcfb_info *)fbi->par;
if (put_user(mxc_fbi->ipu_ch, argp))
return -EFAULT;
break;
}
+ case MXCFB_GET_DIFMT:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_di_pix_fmt, argp))
+ return -EFAULT;
+ break;
+ }
+ case MXCFB_GET_FB_IPU_DI:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_di, argp))
+ return -EFAULT;
+ break;
+ }
+ case MXCFB_GET_FB_BLANK:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->cur_blank, argp))
+ return -EFAULT;
+ break;
+ }
default:
retval = -EINVAL;
}
@@ -1081,7 +1177,7 @@ static int
mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par,
- *mxc_graphic_fbi = NULL;
+ *mxc_graphic_fbi = NULL;
u_int y_bottom;
unsigned long base, active_alpha_phy_addr = 0;
bool loc_alpha_en = false;
@@ -1102,7 +1198,7 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
int j;
for (j = 0; j < num_registered_fb; j++) {
bg_mxcfbi =
- ((struct mxcfb_info *)(registered_fb[j]->par));
+ ((struct mxcfb_info *)(registered_fb[j]->par));
if (bg_mxcfbi->ipu_ch == MEM_BG_SYNC)
break;
@@ -1122,12 +1218,9 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
return -EINVAL;
base = (var->yoffset * var->xres_virtual + var->xoffset);
- base *= (var->bits_per_pixel) / 8;
+ base = (var->bits_per_pixel) * base / 8;
base += info->fix.smem_start;
- dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n",
- info->fix.id, mxc_fbi->cur_ipu_buf, base);
-
/* Check if DP local alpha is enabled and find the graphic fb */
if (mxc_fbi->ipu_ch == MEM_BG_SYNC || mxc_fbi->ipu_ch == MEM_FG_SYNC) {
for (i = 0; i < num_registered_fb; i++) {
@@ -1135,13 +1228,13 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
if ((strcmp(idstr, "DISP3 BG") == 0 ||
strcmp(idstr, "DISP3 FG") == 0) &&
((struct mxcfb_info *)
- (registered_fb[i]->par))->alpha_chan_en) {
+ (registered_fb[i]->par))->alpha_chan_en) {
loc_alpha_en = true;
mxc_graphic_fbi = (struct mxcfb_info *)
- (registered_fb[i]->par);
+ (registered_fb[i]->par);
active_alpha_phy_addr = mxc_fbi->cur_ipu_buf ?
- mxc_graphic_fbi->alpha_phy_addr1 :
- mxc_graphic_fbi->alpha_phy_addr0;
+ mxc_graphic_fbi->alpha_phy_addr1 :
+ mxc_graphic_fbi->alpha_phy_addr0;
dev_dbg(info->device, "Updating SDC graphic "
"buf %d address=0x%08lX\n",
mxc_fbi->cur_ipu_buf,
@@ -1151,10 +1244,13 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
}
}
- down(&mxc_fbi->flip_sem);
init_completion(&mxc_fbi->vsync_complete);
mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+
+ dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n",
+ info->fix.id, mxc_fbi->cur_ipu_buf, base);
+
if (ipu_update_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
mxc_fbi->cur_ipu_buf, base) == 0) {
/* Update the DP local alpha buffer only for graphic plane */
@@ -1176,8 +1272,13 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
dev_err(info->device,
"Error updating SDC buf %d to address=0x%08lX\n",
mxc_fbi->cur_ipu_buf, base);
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu_ch_irq);
+ return -EBUSY;
}
+ down(&mxc_fbi->flip_sem);
dev_dbg(info->device, "Update complete\n");
info->var.xoffset = var->xoffset;
@@ -1211,9 +1312,9 @@ static int mxcfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
len = fbi->fix.smem_len - offset;
vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT;
} else if ((vma->vm_pgoff ==
- (mxc_fbi->alpha_phy_addr0 >> PAGE_SHIFT)) ||
+ (mxc_fbi->alpha_phy_addr0 >> PAGE_SHIFT)) ||
(vma->vm_pgoff ==
- (mxc_fbi->alpha_phy_addr1 >> PAGE_SHIFT))) {
+ (mxc_fbi->alpha_phy_addr1 >> PAGE_SHIFT))) {
len = mxc_fbi->alpha_mem_len;
} else {
list_for_each_entry(mem, &fb_alloc_list, list) {
@@ -1269,9 +1370,14 @@ static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id)
struct fb_info *fbi = dev_id;
struct mxcfb_info *mxc_fbi = fbi->par;
- complete(&mxc_fbi->vsync_complete);
- up(&mxc_fbi->flip_sem);
- ipu_disable_irq(irq);
+ if (mxc_fbi->wait4vsync) {
+ complete(&mxc_fbi->vsync_complete);
+ ipu_disable_irq(irq);
+ mxc_fbi->wait4vsync = 0;
+ } else {
+ up(&mxc_fbi->flip_sem);
+ ipu_disable_irq(irq);
+ }
return IRQ_HANDLED;
}
@@ -1288,14 +1394,10 @@ 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 void mxcfb_suspend_one(struct fb_info *fbi)
{
- struct fb_info *fbi = platform_get_drvdata(pdev);
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
int saved_blank;
-#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
- void *fbmem;
-#endif
acquire_console_sem();
fb_set_suspend(fbi, 1);
@@ -1303,25 +1405,43 @@ static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
mxc_fbi->next_blank = saved_blank;
release_console_sem();
-
- return 0;
}
/*
* Resumes the framebuffer and unblanks the screen. Power management support
*/
-static int mxcfb_resume(struct platform_device *pdev)
+static void mxcfb_resume_one(struct fb_info *fbi)
{
- struct fb_info *fbi = platform_get_drvdata(pdev);
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
acquire_console_sem();
mxcfb_blank(mxc_fbi->next_blank, fbi);
fb_set_suspend(fbi, 0);
release_console_sem();
+}
+
+#ifndef CONFIG_HAS_EARLYSUSPEND
+
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+
+ if (fbi && (strcmp(fbi->fix.id, "DISP3 FG") == 0))
+ mxcfb_suspend_one(fbi);
+
+ return 0;
+}
+
+static int mxcfb_resume(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+
+ if (fbi && (strcmp(fbi->fix.id, "DISP3 FG") == 0))
+ mxcfb_resume_one(fbi);
return 0;
}
+#endif
/*
* Main framebuffer functions
@@ -1341,12 +1461,12 @@ static int mxcfb_map_video_memory(struct fb_info *fbi)
{
if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length)
fbi->fix.smem_len = fbi->var.yres_virtual *
- fbi->fix.line_length;
+ fbi->fix.line_length;
fbi->screen_base = dma_alloc_writecombine(fbi->device,
- fbi->fix.smem_len,
- (dma_addr_t *)&fbi->fix.smem_start,
- GFP_DMA);
+ fbi->fix.smem_len,
+ (dma_addr_t *) & fbi->fix.
+ smem_start, GFP_DMA);
if (fbi->screen_base == 0) {
dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
fbi->fix.smem_len = 0;
@@ -1443,6 +1563,7 @@ static ssize_t swap_disp_chan(struct device *dev,
struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
struct mxcfb_info *fg_mxcfbi = NULL;
+ acquire_console_sem();
/* swap only happen between DP-BG and DC, while DP-FG disable */
if (((mxcfbi->ipu_ch == MEM_BG_SYNC) &&
(strstr(buf, "1-layer-fb") != NULL)) ||
@@ -1451,17 +1572,16 @@ static ssize_t swap_disp_chan(struct device *dev,
int i;
for (i = 0; i < num_registered_fb; i++) {
- fg_mxcfbi =
- (struct mxcfb_info *)mxcfb_info[i]->par;
+ fg_mxcfbi = (struct mxcfb_info *)mxcfb_info[i]->par;
if (fg_mxcfbi->ipu_ch == MEM_FG_SYNC)
break;
else
fg_mxcfbi = NULL;
}
- if (!fg_mxcfbi ||
- fg_mxcfbi->cur_blank == FB_BLANK_UNBLANK) {
+ if (!fg_mxcfbi || fg_mxcfbi->cur_blank == FB_BLANK_UNBLANK) {
dev_err(dev,
"Can not switch while fb2(fb-fg) is on.\n");
+ release_console_sem();
return count;
}
@@ -1469,8 +1589,10 @@ static ssize_t swap_disp_chan(struct device *dev,
dev_err(dev, "Swap display channel failed.\n");
}
+ release_console_sem();
return count;
}
+
DEVICE_ATTR(fsl_disp_property, 644, show_disp_chan, swap_disp_chan);
/*!
@@ -1487,6 +1609,8 @@ static int mxcfb_probe(struct platform_device *pdev)
struct mxcfb_info *mxcfbi;
struct mxc_fb_platform_data *plat_data = pdev->dev.platform_data;
struct resource *res;
+ char *options, *mstr;
+ char name[] = "mxcdi0fb";
int ret = 0;
/*
@@ -1499,6 +1623,13 @@ static int mxcfb_probe(struct platform_device *pdev)
}
mxcfbi = (struct mxcfb_info *)fbi->par;
+ name[5] += pdev->id;
+ if (fb_get_options(name, &options))
+ return -ENODEV;
+
+ if (options)
+ mxcfb_option_setup(fbi, options);
+
if (!g_dp_in_use) {
mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
mxcfbi->ipu_ch = MEM_BG_SYNC;
@@ -1521,7 +1652,7 @@ static int mxcfb_probe(struct platform_device *pdev)
mxcfb_alpha_irq_handler, 0,
MXCFB_NAME, fbi) != 0) {
dev_err(&pdev->dev, "Error registering BG "
- "alpha irq handler.\n");
+ "alpha irq handler.\n");
ret = -EBUSY;
goto err1;
}
@@ -1534,7 +1665,7 @@ static int mxcfb_probe(struct platform_device *pdev)
mxcfb_alpha_irq_handler, 0,
MXCFB_NAME, fbi) != 0) {
dev_err(&pdev->dev, "Error registering BG "
- "alpha irq handler.\n");
+ "alpha irq handler.\n");
ret = -EBUSY;
goto err1;
}
@@ -1552,7 +1683,7 @@ static int mxcfb_probe(struct platform_device *pdev)
mxcfb_alpha_irq_handler, 0,
MXCFB_NAME, fbi) != 0) {
dev_err(&pdev->dev, "Error registering FG alpha irq "
- "handler.\n");
+ "handler.\n");
ret = -EBUSY;
goto err1;
}
@@ -1572,31 +1703,63 @@ static int mxcfb_probe(struct platform_device *pdev)
if (res && res->end) {
fbi->fix.smem_len = res->end - res->start + 1;
fbi->fix.smem_start = res->start;
- fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len);
+ fbi->screen_base =
+ ioremap(fbi->fix.smem_start, fbi->fix.smem_len);
}
/* Need dummy values until real panel is configured */
fbi->var.xres = 240;
fbi->var.yres = 320;
- if (!fb_mode && plat_data && plat_data->mode_str)
- fb_find_mode(&fbi->var, fbi, plat_data->mode_str, NULL, 0, NULL,
- default_bpp);
-
- if (fb_mode)
- fb_find_mode(&fbi->var, fbi, fb_mode, NULL, 0, NULL,
- default_bpp);
+ if (!mxcfbi->default_bpp)
+#ifdef CONFIG_CCWMX51_DEFAULT_VIDEO_BPP
+ mxcfbi->default_bpp = CONFIG_CCWMX51_DEFAULT_VIDEO_BPP;
+#else
+ mxcfbi->default_bpp = 16;
+#endif
- if (plat_data) {
+ if (plat_data && !mxcfbi->ipu_di_pix_fmt)
mxcfbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
- if (!fb_mode && plat_data->mode)
- fb_videomode_to_var(&fbi->var, plat_data->mode);
+
+ if (plat_data && plat_data->mode && plat_data->num_modes)
+ fb_videomode_to_modelist(plat_data->mode, plat_data->num_modes,
+ &fbi->modelist);
+
+ if (!mxcfbi->fb_mode_str && plat_data && plat_data->mode_str)
+ mxcfbi->fb_mode_str = plat_data->mode_str;
+
+ if (mxcfbi->fb_mode_str) {
+
+#ifdef CONFIG_MODULE_CCXMX51
+ if ((mstr = strstr(mxcfbi->fb_mode_str, "VGA@")) != NULL)
+ mxcfbi->fb_mode_str = mstr + 4;
+#endif
+
+ ret =
+ fb_find_mode(&fbi->var, fbi, mxcfbi->fb_mode_str, NULL, 0,
+ NULL, mxcfbi->default_bpp);
+ if ((!ret || (ret > 2)) && plat_data && plat_data->mode
+ && plat_data->num_modes)
+ fb_find_mode(&fbi->var, fbi, mxcfbi->fb_mode_str,
+ plat_data->mode, plat_data->num_modes,
+ NULL, mxcfbi->default_bpp);
+
+#ifdef CONFIG_MODULE_CCXMX51
+ /* This improves the VGA modes on the CCWi-i.MX51 */
+ if (mstr != NULL) {
+ mxcfbi->ipu_ext_clk = true;
+ fbi->var.sync |= FB_SYNC_CLK_LAT_FALL;
+ }
+#endif
}
mxcfb_check_var(&fbi->var, fbi);
+ pm_set_vt_switch(vt_switch);
+
/* Default Y virtual size is 2x panel size */
- fbi->var.yres_virtual = fbi->var.yres * 3;
+ fbi->var.yres_virtual = ((fbi->var.yres + 127) & ~127) * 2;
+ fbi->var.xres_virtual = (fbi->var.xres + 31) & ~31;
mxcfb_set_fix(fbi);
@@ -1617,12 +1780,12 @@ static int mxcfb_probe(struct platform_device *pdev)
return 0;
-err2:
+ err2:
ipu_free_irq(mxcfbi->ipu_ch_irq, fbi);
-err1:
+ err1:
fb_dealloc_cmap(&fbi->cmap);
framebuffer_release(fbi);
-err0:
+ err0:
return ret;
}
@@ -1655,38 +1818,108 @@ static struct platform_driver mxcfb_driver = {
},
.probe = mxcfb_probe,
.remove = mxcfb_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = mxcfb_suspend,
.resume = mxcfb_resume,
+#endif
};
/*
* Parse user specified options (`video=trident:')
* example:
- * video=trident:800x600,bpp=16,noaccel
+ * video=mxcdi0fb:RGB24, 1024x768M-16@60,bpp=16,noaccel
*/
-int mxcfb_setup(char *options)
+static int mxcfb_option_setup(struct fb_info *info, char *options)
{
+ struct mxcfb_info *mxcfbi = info->par;
char *opt;
+
if (!options || !*options)
return 0;
+
while ((opt = strsep(&options, ",")) != NULL) {
if (!*opt)
continue;
+
+ if (!strncmp(opt, "RGB24", 5)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_RGB24;
+ continue;
+ }
+ if (!strncmp(opt, "BGR24", 5)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_BGR24;
+ continue;
+ }
+ if (!strncmp(opt, "RGB565", 6)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_RGB565;
+ continue;
+ }
+ if (!strncmp(opt, "RGB666", 6)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_RGB666;
+ continue;
+ }
+ if (!strncmp(opt, "YUV444", 6)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_YUV444;
+ continue;
+ }
+ if (!strncmp(opt, "LVDS666", 7)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_LVDS666;
+ continue;
+ }
+ if (!strncmp(opt, "YUYV16", 6)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_YUYV;
+ continue;
+ }
+ if (!strncmp(opt, "UYVY16", 6)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_UYVY;
+ continue;
+ }
+ if (!strncmp(opt, "YVYU16", 6)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_YVYU;
+ continue;
+ }
+ if (!strncmp(opt, "VYUY16", 6)) {
+ mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_VYUY;
+ continue;
+ }
if (!strncmp(opt, "ext_clk", 7)) {
- ext_clk_used = true;
+ mxcfbi->ipu_ext_clk = true;
continue;
- } else
- ext_clk_used = false;
-
+ }
if (!strncmp(opt, "bpp=", 4))
- default_bpp = simple_strtoul(opt + 4, NULL, 0);
+ mxcfbi->default_bpp = simple_strtoul(opt + 4, NULL, 0);
else
- fb_mode = opt;
-
+ mxcfbi->fb_mode_str = opt;
}
+
return 0;
}
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mxcfb_early_suspend(struct early_suspend *h)
+{
+ int i;
+ for (i = FB_DEVICE_NUM - 1; i >= 0; i--) {
+ if (mxcfb_info[i])
+ mxcfb_suspend_one(mxcfb_info[i]);
+ }
+}
+
+static void mxcfb_later_resume(struct early_suspend *h)
+{
+ int i;
+ for (i = 0; i < FB_DEVICE_NUM; i++) {
+ if (mxcfb_info[i])
+ mxcfb_resume_one(mxcfb_info[i]);
+ }
+}
+
+struct early_suspend fbdrv_earlysuspend = {
+ .level = EARLY_SUSPEND_LEVEL_DISABLE_FB,
+ .suspend = mxcfb_early_suspend,
+ .resume = mxcfb_later_resume,
+};
+#endif
+
/*!
* Main entry function for the framebuffer. The function registers the power
* management callback functions with the kernel and also registers the MXCFB
@@ -1696,26 +1929,23 @@ int mxcfb_setup(char *options)
*/
int __init mxcfb_init(void)
{
- int ret = 0;
-#ifndef MODULE
- char *option = NULL;
-#endif
-
-#ifndef MODULE
- if (fb_get_options("mxcfb", &option))
- return -ENODEV;
- mxcfb_setup(option);
-#endif
+ int ret;
ret = platform_driver_register(&mxcfb_driver);
+ if (!ret)
+ register_early_suspend(&fbdrv_earlysuspend);
return ret;
}
void mxcfb_exit(void)
{
+ unregister_early_suspend(&fbdrv_earlysuspend);
platform_driver_unregister(&mxcfb_driver);
}
+module_param(vt_switch, int, 0);
+MODULE_PARM_DESC(vt_switch, "enable VT switch during suspend/resume");
+
module_init(mxcfb_init);
module_exit(mxcfb_exit);