Permalink
Browse files

msm_fb: vsync events for legacy MDP 3.00 targets

Pieced together from current code in CAF. This is very specific to
MDP 3.00 targets that use the LCDC display controller, and it might
break horribly and badly on anything else.

Change-Id: I2b7086a17fd22a04d61d7962f4ea98ab1af00442
  • Loading branch information...
1 parent 56c5944 commit ea2e287ae8b44f9e8c95bac17a2768eb5edfcaea @grigorig committed Jan 23, 2013
View
@@ -69,6 +69,8 @@ uint32 mdp_lcdc_underflow_cnt;
boolean mdp_current_clk_on = FALSE;
boolean mdp_is_in_isr = FALSE;
+struct vsync vsync_cntrl;
+
/*
* legacy mdp_in_processing is only for DMA2-MDDI
* this applies to DMA2 block only
@@ -867,6 +869,12 @@ static int mdp_do_histogram(struct fb_info *info, struct mdp_histogram *hist)
}
#endif
+/* vsync_isr_handler: Called from isr context*/
+static void vsync_isr_handler(void)
+{
+ vsync_cntrl.vsync_time = ktime_get();
+}
+
/* Returns < 0 on error, 0 on timeout, or > 0 on successful wait */
int mdp_ppp_pipe_wait(void)
@@ -1205,6 +1213,8 @@ irqreturn_t mdp_isr(int irq, void *ptr)
{
uint32 hist_interrupt, mdp_interrupt = 0;
struct mdp_dma_data *dma;
+ int vsync_isr, disabled_clocks;
+ unsigned long flag;
mdp_is_in_isr = TRUE;
do {
@@ -1221,6 +1231,31 @@ irqreturn_t mdp_isr(int irq, void *ptr)
if (!mdp_interrupt)
break;
+ /*Primary Vsync interrupt*/
+ if (mdp_interrupt & MDP_PRIM_RDPTR) {
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ vsync_isr = vsync_cntrl.vsync_irq_enabled;
+ disabled_clocks = vsync_cntrl.disabled_clocks;
+ if ((!vsync_isr && !vsync_cntrl.disabled_clocks)
+ || (!vsync_isr && vsync_cntrl.vsync_dma_enabled)) {
+ mdp_intr_mask &= ~MDP_PRIM_RDPTR;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_disable_irq_nosync(MDP_VSYNC_TERM);
+ vsync_cntrl.disabled_clocks = 1;
+ } else if (vsync_isr) {
+ vsync_isr_handler();
+ }
+ vsync_cntrl.vsync_dma_enabled = 0;
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+ complete(&vsync_cntrl.vsync_comp);
+ if (!vsync_isr && !disabled_clocks)
+ mdp_pipe_ctrl(MDP_CMD_BLOCK,
+ MDP_BLOCK_POWER_OFF, TRUE);
+
+ complete_all(&vsync_cntrl.vsync_wait);
+ }
+
/* DMA3 TV-Out Start */
if (mdp_interrupt & TV_OUT_DMA3_START) {
/* let's disable TV out interrupt */
@@ -1266,17 +1301,33 @@ irqreturn_t mdp_isr(int irq, void *ptr)
__mdp_histogram_reset();
}
+
/* LCDC Frame Start */
if (mdp_interrupt & LCDC_FRAME_START) {
- /* let's disable LCDC interrupt */
- mdp_intr_mask &= ~LCDC_FRAME_START;
- outp32(MDP_INTR_ENABLE, mdp_intr_mask);
-
dma = &dma2_data;
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ vsync_isr = vsync_cntrl.vsync_irq_enabled;
+ /* let's disable LCDC interrupt */
if (dma->waiting) {
dma->waiting = FALSE;
complete(&dma->comp);
}
+
+ if (!vsync_isr) {
+ mdp_intr_mask &= ~LCDC_FRAME_START;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_disable_irq_nosync(MDP_VSYNC_TERM);
+ vsync_cntrl.disabled_clocks = 1;
+ } else {
+ vsync_isr_handler();
+ }
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+ if (!vsync_isr)
+ mdp_pipe_ctrl(MDP_CMD_BLOCK,
+ MDP_BLOCK_POWER_OFF, TRUE);
+
+ complete_all(&vsync_cntrl.vsync_wait);
}
/* DMA2 LCD-Out Complete */
@@ -1376,6 +1427,7 @@ static void mdp_drv_init(void)
dma2_data.busy = FALSE;
dma2_data.waiting = FALSE;
init_completion(&dma2_data.comp);
+ init_completion(&vsync_cntrl.vsync_comp);
init_MUTEX(&dma2_data.mutex);
mutex_init(&dma2_data.ov_mutex);
@@ -1410,6 +1462,9 @@ static void mdp_drv_init(void)
for (i = 0; i < MDP_MAX_BLOCK; i++) {
atomic_set(&mdp_block_power_cnt[i], 0);
}
+ vsync_cntrl.disabled_clocks = 1;
+ init_completion(&vsync_cntrl.vsync_wait);
+ atomic_set(&vsync_cntrl.vsync_resume, 1);
#ifdef MSM_FB_ENABLE_DBGFS
{
@@ -1496,6 +1551,10 @@ static int mdp_off(struct platform_device *pdev)
mdp_histogram_ctrl(FALSE);
+ atomic_set(&vsync_cntrl.suspend, 1);
+ atomic_set(&vsync_cntrl.vsync_resume, 0);
+ complete_all(&vsync_cntrl.vsync_wait);
+
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
ret = panel_next_off(pdev);
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
@@ -1830,6 +1889,7 @@ static int mdp_probe(struct platform_device *pdev)
/* link to the latest pdev */
mfd->pdev = msm_fb_dev;
mfd->mdp_rev = mdp_rev;
+ mfd->vsync_init = NULL;
if (mdp_pdata) {
if (mdp_pdata->cont_splash_enabled) {
@@ -2059,6 +2119,9 @@ static int mdp_probe(struct platform_device *pdev)
}
#else
mfd->dma = &dma2_data;
+ mfd->vsync_init = mdp_dma_lcdc_vsync_init;
+ mfd->vsync_ctrl = mdp_dma_lcdc_vsync_ctrl;
+ mfd->vsync_show = mdp_dma_lcdc_show_event;
spin_lock_irqsave(&mdp_spin_lock, flag);
mdp_intr_mask &= ~MDP_DMA_P_DONE;
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
@@ -2148,6 +2211,30 @@ static int mdp_probe(struct platform_device *pdev)
pdev_list[pdev_list_cnt++] = pdev;
mdp4_extn_disp = 0;
+
+ if (mfd->vsync_init != NULL) {
+ mfd->vsync_init(0);
+
+ if (!mfd->vsync_sysfs_created) {
+ mfd->dev_attr.attr.name = "vsync_event";
+ mfd->dev_attr.attr.mode = S_IRUGO;
+ mfd->dev_attr.show = mfd->vsync_show;
+ sysfs_attr_init(&mfd->dev_attr.attr);
+
+ rc = sysfs_create_file(&mfd->fbi->dev->kobj,
+ &mfd->dev_attr.attr);
+ if (rc) {
+ pr_err("%s: sysfs creation failed, ret=%d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ kobject_uevent(&mfd->fbi->dev->kobj, KOBJ_ADD);
+ pr_debug("%s: kobject_uevent(KOBJ_ADD)\n", __func__);
+ mfd->vsync_sysfs_created = 1;
+ }
+ }
+
return 0;
mdp_probe_err:
View
@@ -105,6 +105,22 @@ extern struct mdp_ccs mdp_ccs_yuv2rgb ;
extern struct mdp_ccs mdp_ccs_rgb2yuv ;
extern unsigned char hdmi_prim_display;
+struct vsync {
+ ktime_t vsync_time;
+ struct completion vsync_comp;
+ struct device *dev;
+ struct work_struct vsync_work;
+ int vsync_irq_enabled;
+ int vsync_dma_enabled;
+ int disabled_clocks;
+ struct completion vsync_wait;
+ atomic_t suspend;
+ atomic_t vsync_resume;
+ int sysfs_created;
+};
+
+extern struct vsync vsync_cntrl;
+
/*
* MDP Image Structure
*/
@@ -268,6 +284,7 @@ struct mdp_hist_lut_info {
#endif
#define MDP_HISTOGRAM_TERM 0x80
#define MDP_OVERLAY2_TERM 0x100
+#define MDP_VSYNC_TERM 0x1000
#define ACTIVE_START_X_EN BIT(31)
#define ACTIVE_START_Y_EN BIT(31)
@@ -287,6 +304,7 @@ struct mdp_hist_lut_info {
#define MDP_PPP_DONE BIT(0)
#define TV_OUT_DMA3_DONE BIT(6)
#define TV_ENC_UNDERRUN BIT(7)
+#define MDP_PRIM_RDPTR BIT(8)
#define TV_OUT_DMA3_START BIT(13)
#define MDP_HIST_DONE BIT(20)
@@ -755,6 +773,11 @@ static inline int mdp_bus_scale_update_request(uint32_t index)
return 0;
}
#endif
+void mdp_dma_lcdc_vsync_init(int cndx);
+void mdp_dma_vsync_ctrl(int enable);
+void mdp_dma_lcdc_vsync_ctrl(int enable);
+ssize_t mdp_dma_lcdc_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf);
#ifdef MDP_HW_VSYNC
void mdp_hw_vsync_clk_enable(struct msm_fb_data_type *mfd);
@@ -61,6 +61,31 @@ int first_pixel_start_x;
int first_pixel_start_y;
static bool firstupdate = TRUE; ////LCD_LUYA_20100610_01
+ssize_t mdp_dma_lcdc_show_event(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ if (atomic_read(&vsync_cntrl.suspend) > 0 ||
+ atomic_read(&vsync_cntrl.vsync_resume) == 0)
+ return 0;
+
+ INIT_COMPLETION(vsync_cntrl.vsync_wait);
+
+ ret = wait_for_completion_interruptible(&vsync_cntrl.vsync_wait);
+ if (ret < 0) {
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
+ ktime_to_ns(ktime_get()));
+ buf[strlen(buf) + 1] = '\0';
+ return ret;
+ }
+
+ ret = snprintf(buf, PAGE_SIZE, "VSYNC=%llu",
+ ktime_to_ns(vsync_cntrl.vsync_time));
+ buf[strlen(buf) + 1] = '\0';
+ return ret;
+}
+
int mdp_lcdc_on(struct platform_device *pdev)
{
int lcdc_width;
@@ -116,6 +141,9 @@ int mdp_lcdc_on(struct platform_device *pdev)
fbi = mfd->fbi;
var = &fbi->var;
+ vsync_cntrl.dev = mfd->fbi->dev;
+ atomic_set(&vsync_cntrl.suspend, 0);
+
/* MDP cmd block enable */
mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
@@ -357,12 +385,54 @@ int mdp_lcdc_off(struct platform_device *pdev)
ret = panel_next_off(pdev);
+ atomic_set(&vsync_cntrl.suspend, 1);
+ atomic_set(&vsync_cntrl.vsync_resume, 0);
+ complete_all(&vsync_cntrl.vsync_wait);
+
/* delay to make sure the last frame finishes */
msleep(20); ////ZTE_LCD_LUYA_20100629_001
return ret;
}
+void mdp_dma_lcdc_vsync_init(int cndx)
+{
+ /* nothing to do */
+}
+
+void mdp_dma_lcdc_vsync_ctrl(int enable)
+{
+ unsigned long flag;
+ int disabled_clocks;
+
+ if (vsync_cntrl.vsync_irq_enabled == enable)
+ return;
+
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ if (!enable)
+ INIT_COMPLETION(vsync_cntrl.vsync_wait);
+
+ vsync_cntrl.vsync_irq_enabled = enable;
+ if (!enable)
+ vsync_cntrl.disabled_clocks = 0;
+ disabled_clocks = vsync_cntrl.disabled_clocks;
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+ if (enable && disabled_clocks) {
+ mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+ spin_lock_irqsave(&mdp_spin_lock, flag);
+ outp32(MDP_INTR_CLEAR, LCDC_FRAME_START);
+ mdp_intr_mask |= LCDC_FRAME_START;
+ outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+ mdp_enable_irq(MDP_VSYNC_TERM);
+ spin_unlock_irqrestore(&mdp_spin_lock, flag);
+ }
+
+ if (vsync_cntrl.vsync_irq_enabled &&
+ atomic_read(&vsync_cntrl.suspend) == 0)
+ atomic_set(&vsync_cntrl.vsync_resume, 1);
+}
+
void mdp_lcdc_update(struct msm_fb_data_type *mfd)
{
struct fb_info *fbi = mfd->fbi;
View
@@ -407,6 +407,7 @@ static int msm_fb_probe(struct platform_device *pdev)
if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
return -ENOMEM;
+ vsync_cntrl.dev = mfd->fbi->dev;
mfd->panel_info.frame_count = 0;
///ZTE_LCD_LUYA_20100325_001,LCD_LUYA_20100610_01 2009-11-28 decrease initial brightness of backlight
mfd->bl_level = mfd->panel_info.bl_max/6;
@@ -2730,6 +2731,27 @@ static int msmfb_blit(struct fb_info *info, void __user *p)
return 0;
}
+static int msmfb_vsync_ctrl(struct fb_info *info, void __user *argp)
+{
+ int enable, ret;
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+ ret = copy_from_user(&enable, argp, sizeof(enable));
+ if (ret) {
+ pr_err("%s:msmfb_overlay_vsync ioctl failed", __func__);
+ return ret;
+ }
+
+ if (mfd->vsync_ctrl)
+ mfd->vsync_ctrl(enable);
+ else {
+ pr_err("%s: Vsync IOCTL not supported", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_FB_MSM_OVERLAY
static int msmfb_overlay_get(struct fb_info *info, void __user *p)
{
@@ -3274,6 +3296,15 @@ static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
ret = msmfb_overlay_ioctl_writeback_terminate(info);
break;
#endif
+ case MSMFB_VSYNC_CTRL:
+ case MSMFB_OVERLAY_VSYNC_CTRL:
+ down(&msm_fb_ioctl_ppp_sem);
+ if (mdp_rev >= MDP_REV_40)
+ ret = -1; //ret = msmfb_overlay_vsync_ctrl(info, argp);
+ else
+ ret = msmfb_vsync_ctrl(info, argp);
+ up(&msm_fb_ioctl_ppp_sem);
+ break;
case MSMFB_BLIT:
down(&msm_fb_ioctl_ppp_sem);
ret = msmfb_blit(info, argp);
Oops, something went wrong.

3 comments on commit ea2e287

Hello @grigorig
Do you think you could help with us with porting this to MDDI panels on a 3.0 kernel?
Here is the kernel source if you're possibly interested ;)
https://github.com/androidarmv6/android_kernel_lge_msm7x27-3.0.x

Thanks!
Rashed

Owner

grigorig replied Mar 7, 2013

This is probably not really feasible without the hardware in question, and I'm busy enough already. Use the implementation from CAF as reference.

Please sign in to comment.