added HDMwIn v1.2.5, modified to accomodate none MSM HDMI devices

file:4db30e1a2c6e7de3b8e8a3e4ddbe1dda78a30dcc -> file:a4e9ed78bb12069007028a44aa9ce3c68c14ad79
--- a/drivers/video/msm/hdmi/fb-hdmi.c
+++ b/drivers/video/msm/hdmi/fb-hdmi.c
@@ -46,6 +46,8 @@
#define HDMI_DBG(s...) do {} while (0)
#endif
+#define BITS_PER_PIXEL 16
+
struct update_info_t {
int left;
int top;
@@ -68,6 +70,12 @@ struct hdmifb_info {
int yres;
unsigned long state;
atomic_t use_count;
+ int vsyncMode;
+ int mirroring;
+ int doubleBuffering;
+ struct mdp_blit_req* vsyncBlitReq;
+ struct mdp_blit_req mirrorReq;
+ struct mirror_statistics mirror_stats;
};
static struct mdp_device *mdp;
@@ -88,6 +96,11 @@ static int hdmifb_release(struct fb_info
return 0;
}
+static int hdmifb_read(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos)
+{
+ pr_info("%s\n", __func__);
+ return 0;
+}
static int hdmifb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
u32 size;
@@ -286,13 +299,143 @@ static int hdmifb_blit(struct fb_info *i
if (copy_from_user(&req, &list->req[i], sizeof(req)))
return -EFAULT;
req.flags |= MDP_DITHER;
- ret = mdp->blit(mdp, info, &req);
+
+ /* Copy the requested blit in case we're mirroring */
+ if (_hdmi_fb->mirroring)
+ {
+ memcpy(&_hdmi_fb->mirrorReq, &req, sizeof(struct mdp_blit_req));
+
+ /* Default double-buffering off */
+ _hdmi_fb->doubleBuffering = 0;
+
+ /* Are we rotating? */
+ if ((req.flags & MDP_ROT_MASK) == MDP_ROT_90 || (req.flags & MDP_ROT_MASK) == MDP_ROT_270)
+ {
+ /* Are we scaling at the same time? */
+ if (req.src_rect.w != req.dst_rect.w || req.src_rect.h != req.dst_rect.h)
+ {
+ _hdmi_fb->doubleBuffering = 1;
+ }
+ else
+ {
+ // Switch us back to buffer 0
+ mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2,
+ _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0,
+ &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type);
+ }
+ }
+ }
+
+ ret = mdp->blit(mdp, info, &req);
if (ret)
return ret;
}
return 0;
}
+void reportUnderflow(void)
+{
+ if (_hdmi_fb) _hdmi_fb->mirror_stats.underflows++;
+}
+
+int hdmi_usePanelSync(void)
+{
+ if (_hdmi_fb->mirroring &&
+ (_hdmi_fb->vsyncMode == VSYNC_NONE || _hdmi_fb->vsyncMode == VSYNC_HDMI_ONLY))
+ return 0;
+ return 1;
+}
+
+int hdmi_useHdmiSync(void)
+{
+ if (_hdmi_fb->mirroring &&
+ (_hdmi_fb->vsyncMode == VSYNC_NONE || _hdmi_fb->vsyncMode == VSYNC_PANEL_ONLY))
+ return 0;
+ return 1;
+}
+
+void blit_on_vsync(struct mdp_blit_req* blitReq)
+{
+ struct msm_panel_data *panel = _hdmi_fb->panel;
+
+ /* Request mirror blit and vsync callback */
+ _hdmi_fb->vsyncBlitReq = blitReq;
+ panel->request_vsync(panel, &_hdmi_fb->vsync_callback);
+ return;
+}
+
+void hdmi_DoBlit(int fb0Offset)
+{
+ int yoffset = 0;
+
+ /* Always track frame counts, so we can measure FPS of panel-only mode */
+ _hdmi_fb->mirror_stats.frames++;
+
+ /* This handles the disabled case */
+ if (_hdmi_fb->mirroring == 0 || _hdmi_fb->mirrorReq.src.width == 0)
+ return;
+
+ /* Verify HDMI is enabled */
+ if ((test_bit(fb_enabled, &_hdmi_fb->state) == 0) ||
+ (test_bit(hdmi_enabled, &_hdmi_fb->state) == 0))
+ return;
+
+ /* Set the proper offsets */
+ _hdmi_fb->mirrorReq.src.offset = fb0Offset;
+
+ if (_hdmi_fb->doubleBuffering)
+ {
+ if (_hdmi_fb->mirrorReq.dst.offset == 0)
+ {
+ yoffset = _hdmi_fb->mirrorReq.dst.height;
+ _hdmi_fb->mirrorReq.dst.offset = yoffset * _hdmi_fb->mirrorReq.dst.width * BITS_PER_PIXEL / 8;
+ }
+ else
+ {
+ _hdmi_fb->mirrorReq.dst.offset = 0;
+ yoffset = 0;
+ }
+ }
+ else
+ {
+ if (_hdmi_fb->mirrorReq.dst.offset != 0)
+ {
+ // Force us back to the primary buffer
+ _hdmi_fb->mirrorReq.dst.offset = 0;
+ mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2,
+ _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0,
+ &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type);
+ }
+ }
+
+ /* Either schedule the blit on vsync, or do it now */
+ if (hdmi_useHdmiSync() && !_hdmi_fb->doubleBuffering)
+ {
+ blit_on_vsync(&_hdmi_fb->mirrorReq);
+ }
+ else
+ {
+ mdp->blit(mdp, _hdmi_fb->fb, &_hdmi_fb->mirrorReq);
+ }
+
+ if (_hdmi_fb->doubleBuffering)
+ {
+ if (hdmi_useHdmiSync())
+ {
+ hdmifb_pan_update(_hdmi_fb->fb, 0, 0,
+ _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, yoffset);
+ }
+ else
+ {
+ mdp->dma(mdp, _hdmi_fb->mirrorReq.dst.offset + _hdmi_fb->fb->fix.smem_start,
+ _hdmi_fb->fb->var.xres * 2, _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0,
+ &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type);
+ }
+ }
+
+ return;
+}
+
enum ioctl_cmd_index {
CMD_SET_MODE,
CMD_GET_MODE,
@@ -304,6 +447,17 @@ enum ioctl_cmd_index {
CMD_ESTABLISH_TIMING,
};
+/*static char *cmd_str[] = {
+ "HDMI_SET_MODE",
+ "HDMI_GET_MODE",
+ "HDMI_DISABLE",
+ "HDMI_ENABLE",
+ "HDMI_GET_STATE",
+ "HDMI_BLIT",
+ "HDMI_CABLE_STAT",
+ "HDMI_ESTABLISH_TIMING",
+};*/
+
static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
{
struct hdmifb_info *hdmi_fb = p->par;
@@ -324,6 +478,7 @@ static int hdmifb_ioctl(struct fb_info *
get_user(val, (unsigned __user *) arg);
//pr_info("[hdmi] SET_MODE: %d\n", val);
ret = hdmifb_change_mode(p, val);
+ _hdmi_fb->mirroring = 0;
break;
case HDMI_GET_MODE:
/*
@@ -335,6 +490,7 @@ static int hdmifb_ioctl(struct fb_info *
break;
case HDMI_DISABLE:
get_user(val, (unsigned __user *) arg);
+ _hdmi_fb->mirroring = 0;
ret = hdmifb_pause(p, 1);
break;
case HDMI_ENABLE:
@@ -384,6 +540,37 @@ static int hdmifb_ioctl(struct fb_info *
&dinfo, sizeof(dinfo));
break;
}
+
+ case HDMI_GET_MIRRORING:
+ ret = put_user(hdmi_fb->mirroring, (unsigned __user *) arg);
+ break;
+ case HDMI_SET_MIRRORING:
+ get_user(val, (unsigned __user *) arg);
+ hdmi_fb->mirroring = val;
+ memset(&hdmi_fb->mirror_stats, 0, sizeof(struct mirror_statistics));
+ hdmi_fb->mirror_stats.statisticsTime = ktime_to_ns(ktime_get());
+ break;
+ case HDMI_GET_STATISTICS:
+ {
+ struct mirror_statistics temp;
+
+ memcpy(&temp, &hdmi_fb->mirror_stats, sizeof(struct mirror_statistics));
+ temp.statisticsTime = ktime_to_ns(ktime_get()) - hdmi_fb->mirror_stats.statisticsTime;
+ ret = copy_to_user((unsigned __user *) arg, &temp, sizeof(struct mirror_statistics));
+ }
+ break;
+ case HDMI_CLEAR_STATISTICS:
+ memset(&hdmi_fb->mirror_stats, 0, sizeof(struct mirror_statistics));
+ hdmi_fb->mirror_stats.statisticsTime = ktime_to_ns(ktime_get());
+ break;
+ case HDMI_GET_VSYNC_MODE:
+ ret = put_user(hdmi_fb->vsyncMode, (unsigned __user *) arg);
+ break;
+ case HDMI_SET_VSYNC_MODE:
+ get_user(val, (unsigned __user *) arg);
+ hdmi_fb->vsyncMode = val;
+ ret = 0;
+ break;
default:
printk(KERN_ERR "hdmi: unknown cmd, cmd = %d\n", cmd);
}
@@ -401,6 +588,7 @@ static struct fb_ops hdmi_fb_ops = {
.fb_copyarea = hdmifb_copyarea,
.fb_imageblit = hdmifb_imageblit,
.fb_ioctl = hdmifb_ioctl,
+ .fb_read = hdmifb_read,
};
#ifdef CONFIG_HAS_EARLYSUSPEND
@@ -443,8 +631,6 @@ static void hdmifb_resume(struct early_s
}
#endif
-#define BITS_PER_PIXEL 16
-
static void setup_fb_info(struct hdmifb_info *hdmi_fb)
{
struct fb_info *fb_info = hdmi_fb->fb;
@@ -543,6 +729,14 @@ static void hdmi_handle_vsync(struct msm
vsync_callback);
struct msm_panel_data *panel = hdmi->panel;
+ /* Handle blitting requests */
+ if (hdmi->vsyncBlitReq)
+ {
+ mdp->blit(mdp, _hdmi_fb->fb, hdmi->vsyncBlitReq);
+ hdmi->vsyncBlitReq = NULL;
+ return;
+ }
+
spin_lock_irqsave(&hdmi->update_lock, irq_flags);
x = hdmi->update_info.left;
y = hdmi->update_info.top;
@@ -600,6 +794,9 @@ static int hdmifb_probe(struct platform_
if (!info)
return -ENOMEM;
+ /* Zero out the structure before using it */
+ memset(info, 0, sizeof(struct hdmifb_info));
+
hdmi_fb = info->par;
_hdmi_fb = hdmi_fb;
hdmi_fb->fb = info;
file:83e8d26600a93bf785678393d624506ac7802ba1 -> file:10ebc3e94bfac9f9879c1faca61870f05dca8495
--- a/drivers/video/msm/hdmi/transmitter.c
+++ b/drivers/video/msm/hdmi/transmitter.c
@@ -381,7 +381,7 @@ static int hdmi_get_cable_state(struct h
container_of(hdmi_device, struct hdmi_info, hdmi_dev);
*connect = hdmi->cable_connected;
#endif
- HDMI_DBG("%s, state=%s\n", __func__, *connect ? "on" : "off" );
+// HDMI_DBG("%s, state=%s\n", __func__, *connect ? "on" : "off" );
return 0;
}
file:d095c7f8c924fac59c0fda8fa8c4a624e0d1e30c -> file:a22a979e1ffe02a2ae14423197666189d8c6a1e4
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/clk.h>
+#include <linux/timer.h>
#include <linux/file.h>
#include <linux/android_pmem.h>
#include <linux/major.h>
@@ -36,6 +37,13 @@
struct class *mdp_class;
+/* Used to report LCDC underflows */
+#ifndef CONFIG_MSM_HDMI
+void reportUnderflow(void) { return; }
+#else
+void reportUnderflow(void);
+#endif
+
#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000)
static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
@@ -141,6 +149,15 @@ static irqreturn_t mdp_isr(int irq, void
// pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status,
// mdp_irq_mask);
+
+#ifdef CONFIG_MSM_HDMI
+ if (status & MDP_LCDC_UNDERFLOW)
+ {
+ pr_err("%s: LCDC Underflow\n", __func__);
+ reportUnderflow();
+ }
+#endif
+
status &= mdp_irq_mask;
#ifdef CONFIG_MSM_MDP40
if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) {
@@ -435,6 +452,20 @@ static int get_img(struct mdp_img *img,
struct file *file;
unsigned long vstart;
+#ifdef CONFIG_MSM_HDMI
+ if (img->memory_id & 0x40000000)
+ {
+ struct fb_info *fb = registered_fb[img->memory_id & 0x0000FFFF];
+ if (fb)
+ {
+ *start = fb->fix.smem_start;
+ *len = fb->fix.smem_len;
+ }
+ *filep = NULL;
+ return 0;
+ }
+#endif
+
if (!get_pmem_file(img->memory_id, start, &vstart, len, filep))
return 0;
else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len,
file:1c71bfd175cfb5e6fe67de18b4dc0417de9f1ac0 -> file:eb041c120b84a01a9fed7c0993e2c97ea448ac69
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -33,11 +33,20 @@
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/android_pmem.h>
+#include "mdp_hw.h"
extern void start_drawing_late_resume(struct early_suspend *h);
static void msmfb_resume_handler(struct early_suspend *h);
static void msmfb_resume(struct work_struct *work);
+#ifndef CONFIG_MSM_HDMI
+void hdmi_DoBlit(int offset) { return; }
+int hdmi_usePanelSync(void) { return 1; }
+#else
+void hdmi_DoBlit(int offset);
+int hdmi_usePanelSync(void);
+#endif
+
#define MSMFB_DEBUG 1
#ifdef CONFIG_FB_MSM_LOGO
#define INIT_IMAGE_FILE "/logo.rle"
@@ -397,19 +406,30 @@ restart:
msmfb->yoffset);
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
- /* if the panel is all the way on wait for vsync, otherwise sleep
- * for 16 ms (long enough for the dma to panel) and then begin dma */
- msmfb->vsync_request_time = ktime_get();
- if (panel->request_vsync && (sleeping == AWAKE)) {
- wake_lock_timeout(&msmfb->idle_lock, HZ/4);
- panel->request_vsync(panel, &msmfb->vsync_callback);
+ if (!hdmi_usePanelSync())
+ {
+ msmfb->vsync_request_time = ktime_get();
+ msmfb_start_dma(msmfb);
} else {
- if (!hrtimer_active(&msmfb->fake_vsync)) {
- hrtimer_start(&msmfb->fake_vsync,
- ktime_set(0, NSEC_PER_SEC/60),
- HRTIMER_MODE_REL);
+ /* if the panel is all the way on wait for vsync, otherwise sleep
+ * for 16 ms (long enough for the dma to panel) and then begin dma */
+ msmfb->vsync_request_time = ktime_get();
+ if (panel->request_vsync && (sleeping == AWAKE)) {
+ wake_lock_timeout(&msmfb->idle_lock, HZ/4);
+ panel->request_vsync(panel, &msmfb->vsync_callback);
+ } else {
+ if (!hrtimer_active(&msmfb->fake_vsync)) {
+ hrtimer_start(&msmfb->fake_vsync,
+ ktime_set(0, NSEC_PER_SEC/60),
+ HRTIMER_MODE_REL);
+ }
}
}
+
+ /* We did the DMA, now blit the data to the other display */
+ hdmi_DoBlit(msmfb->xres * msmfb->yoffset * BYTES_PER_PIXEL(msmfb));
+
+ return;
}
static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top,
file:1ad580ff6d127ab62049035c88046cd2cc80f0c6 -> file:d89d0b966fe809f32fd504ba3cef9506aec90785
--- a/include/linux/htc_hdmi.h
+++ b/include/linux/htc_hdmi.h
@@ -34,6 +34,13 @@ enum {
#define HDMI_GET_EDID _IOR(HDMI_IOCTL_MAGIC, 9, unsigned)
#define HDMI_GET_DISPLAY_INFO _IOR(HDMI_IOCTL_MAGIC, 10, unsigned)
+#define HDMI_GET_MIRRORING _IOR(HDMI_IOCTL_MAGIC, 30, unsigned)
+#define HDMI_SET_MIRRORING _IOW(HDMI_IOCTL_MAGIC, 31, unsigned)
+#define HDMI_GET_STATISTICS _IOR(HDMI_IOCTL_MAGIC, 32, unsigned)
+#define HDMI_CLEAR_STATISTICS _IOW(HDMI_IOCTL_MAGIC, 33, unsigned)
+#define HDMI_GET_VSYNC_MODE _IOR(HDMI_IOCTL_MAGIC, 34, unsigned)
+#define HDMI_SET_VSYNC_MODE _IOW(HDMI_IOCTL_MAGIC, 35, unsigned)
+
#define ASPECT(w, h) (w << 8 | h)
struct video_mode {
unsigned short width, height, refresh_rate, aspect;
@@ -47,10 +54,25 @@ enum {
};
struct display_info {
- unsigned int visible_width; /* in mm */
- unsigned int visible_height;
- unsigned int resolution_width; /* in pixel */
- unsigned int resolution_height;
+ unsigned int visible_width; /* in mm */
+ unsigned int visible_height;
+ unsigned int resolution_width; /* in pixel */
+ unsigned int resolution_height;
+};
+
+/* Gathered statistics for mirroring */
+struct mirror_statistics {
+ unsigned int frames; /* Number of panel frames requested */
+ unsigned int underflows; /* Number of times we underflowed the LCDC */
+ s64 statisticsTime; /* Mirror time, in ns */
+};
+
+/* Panel state while mirroring */
+enum {
+ VSYNC_ALL = 0,
+ VSYNC_PANEL_ONLY,
+ VSYNC_HDMI_ONLY,
+ VSYNC_NONE
};
#endif