Created
March 23, 2015 22:17
-
-
Save invisiblek/e127265b3136230e7594 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c | |
index a71d6f8..db66761 100644 | |
--- a/drivers/video/msm/mdss/mdss_fb.c | |
+++ b/drivers/video/msm/mdss/mdss_fb.c | |
@@ -2,7 +2,7 @@ | |
* Core MDSS framebuffer driver. | |
* | |
* Copyright (C) 2007 Google Incorporated | |
- * Copyright (c) 2008-2013, The Linux Foundation. All rights reserved. | |
+ * Copyright (c) 2008-2015, The Linux Foundation. All rights reserved. | |
* | |
* This software is licensed under the terms of the GNU General Public | |
* License version 2, as published by the Free Software Foundation, and | |
@@ -44,6 +44,7 @@ | |
#include <linux/file.h> | |
#include <linux/memory_alloc.h> | |
#include <linux/kthread.h> | |
+#include <linux/of_address.h> | |
#include <mach/board.h> | |
#include <mach/memory.h> | |
@@ -52,13 +53,7 @@ | |
#include <mach/msm_memtypes.h> | |
#include "mdss_fb.h" | |
- | |
-#ifdef CONFIG_MACH_LGE | |
-#include "mdss_mdp.h" | |
-#include <mach/board_lge.h> | |
-static int force_set_bl_f; | |
-unsigned long msm_fb_phys_addr_backup; | |
-#endif | |
+#include "mdss_mdp_splash_logo.h" | |
#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER | |
#define MDSS_FB_NUM 3 | |
@@ -93,7 +88,10 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd); | |
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
unsigned long arg); | |
-static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma); | |
+static int mdss_fb_fbmem_ion_mmap(struct fb_info *info, | |
+ struct vm_area_struct *vma); | |
+static int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd, | |
+ size_t size); | |
static void mdss_fb_release_fences(struct msm_fb_data_type *mfd); | |
static int __mdss_fb_sync_buf_done_callback(struct notifier_block *p, | |
unsigned long val, void *data); | |
@@ -102,6 +100,7 @@ static int __mdss_fb_display_thread(void *data); | |
static int mdss_fb_pan_idle(struct msm_fb_data_type *mfd); | |
static int mdss_fb_send_panel_event(struct msm_fb_data_type *mfd, | |
int event, void *arg); | |
+static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd); | |
void mdss_fb_no_update_notify_timer_cb(unsigned long data) | |
{ | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; | |
@@ -113,6 +112,31 @@ void mdss_fb_no_update_notify_timer_cb(unsigned long data) | |
complete(&mfd->no_update.comp); | |
} | |
+void mdss_fb_bl_update_notify(struct msm_fb_data_type *mfd) | |
+{ | |
+ if (!mfd) { | |
+ pr_err("%s mfd NULL\n", __func__); | |
+ return; | |
+ } | |
+ mutex_lock(&mfd->update.lock); | |
+ if (mfd->update.ref_count > 0) { | |
+ mutex_unlock(&mfd->update.lock); | |
+ mfd->update.value = NOTIFY_TYPE_BL_UPDATE; | |
+ complete(&mfd->update.comp); | |
+ mutex_lock(&mfd->update.lock); | |
+ } | |
+ mutex_unlock(&mfd->update.lock); | |
+ | |
+ mutex_lock(&mfd->no_update.lock); | |
+ if (mfd->no_update.ref_count > 0) { | |
+ mutex_unlock(&mfd->no_update.lock); | |
+ mfd->no_update.value = NOTIFY_TYPE_BL_UPDATE; | |
+ complete(&mfd->no_update.comp); | |
+ mutex_lock(&mfd->no_update.lock); | |
+ } | |
+ mutex_unlock(&mfd->no_update.lock); | |
+} | |
+ | |
static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, | |
unsigned long *argp) | |
{ | |
@@ -128,10 +152,20 @@ static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, | |
if (notify > NOTIFY_UPDATE_POWER_OFF) | |
return -EINVAL; | |
- if (notify == NOTIFY_UPDATE_START) { | |
+ if (mfd->update.is_suspend) { | |
+ to_user = NOTIFY_TYPE_SUSPEND; | |
+ mfd->update.is_suspend = 0; | |
+ ret = 1; | |
+ } else if (notify == NOTIFY_UPDATE_START) { | |
INIT_COMPLETION(mfd->update.comp); | |
- ret = wait_for_completion_timeout( | |
+ mutex_lock(&mfd->update.lock); | |
+ mfd->update.ref_count++; | |
+ mutex_unlock(&mfd->update.lock); | |
+ ret = wait_for_completion_interruptible_timeout( | |
&mfd->update.comp, 4 * HZ); | |
+ mutex_lock(&mfd->update.lock); | |
+ mfd->update.ref_count--; | |
+ mutex_unlock(&mfd->update.lock); | |
to_user = (unsigned int)mfd->update.value; | |
if (mfd->update.type == NOTIFY_TYPE_SUSPEND) { | |
to_user = (unsigned int)mfd->update.type; | |
@@ -139,13 +173,19 @@ static int mdss_fb_notify_update(struct msm_fb_data_type *mfd, | |
} | |
} else if (notify == NOTIFY_UPDATE_STOP) { | |
INIT_COMPLETION(mfd->no_update.comp); | |
- ret = wait_for_completion_timeout( | |
+ mutex_lock(&mfd->no_update.lock); | |
+ mfd->no_update.ref_count++; | |
+ mutex_unlock(&mfd->no_update.lock); | |
+ ret = wait_for_completion_interruptible_timeout( | |
&mfd->no_update.comp, 4 * HZ); | |
+ mutex_lock(&mfd->no_update.lock); | |
+ mfd->no_update.ref_count--; | |
+ mutex_unlock(&mfd->no_update.lock); | |
to_user = (unsigned int)mfd->no_update.value; | |
} else { | |
if (mfd->panel_power_on) { | |
INIT_COMPLETION(mfd->power_off_comp); | |
- ret = wait_for_completion_timeout( | |
+ ret = wait_for_completion_interruptible_timeout( | |
&mfd->power_off_comp, 1 * HZ); | |
} | |
} | |
@@ -230,23 +270,32 @@ static ssize_t mdss_fb_get_type(struct device *dev, | |
return ret; | |
} | |
-static void mdss_fb_parse_dt_split(struct msm_fb_data_type *mfd) | |
+static void mdss_fb_parse_dt(struct msm_fb_data_type *mfd) | |
{ | |
- u32 data[2]; | |
+ u32 data[2] = {0}; | |
+ u32 panel_xres; | |
struct platform_device *pdev = mfd->pdev; | |
- if (of_property_read_u32_array(pdev->dev.of_node, "qcom,mdss-fb-split", | |
- data, 2)) | |
- return; | |
- if (data[0] && data[1] && | |
- (mfd->panel_info->xres == (data[0] + data[1]))) { | |
- mfd->split_fb_left = data[0]; | |
- mfd->split_fb_right = data[1]; | |
- pr_info("split framebuffer left=%d right=%d\n", | |
- mfd->split_fb_left, mfd->split_fb_right); | |
+ | |
+ of_property_read_u32_array(pdev->dev.of_node, | |
+ "qcom,mdss-fb-split", data, 2); | |
+ | |
+ panel_xres = mfd->panel_info->xres; | |
+ if (data[0] && data[1]) { | |
+ if (mfd->split_display) | |
+ panel_xres *= 2; | |
+ | |
+ if (panel_xres == data[0] + data[1]) { | |
+ mfd->split_fb_left = data[0]; | |
+ mfd->split_fb_right = data[1]; | |
+ } | |
} else { | |
- mfd->split_fb_left = 0; | |
- mfd->split_fb_right = 0; | |
+ if (mfd->split_display) | |
+ mfd->split_fb_left = mfd->split_fb_right = panel_xres; | |
+ else | |
+ mfd->split_fb_left = mfd->split_fb_right = 0; | |
} | |
+ pr_info("split framebuffer left=%d right=%d\n", | |
+ mfd->split_fb_left, mfd->split_fb_right); | |
} | |
static ssize_t mdss_fb_get_split(struct device *dev, | |
@@ -330,12 +379,117 @@ static ssize_t mdss_fb_get_idle_notify(struct device *dev, | |
return ret; | |
} | |
+static ssize_t mdss_fb_get_panel_info(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ struct fb_info *fbi = dev_get_drvdata(dev); | |
+ struct msm_fb_data_type *mfd = fbi->par; | |
+ struct mdss_panel_info *pinfo = mfd->panel_info; | |
+ int ret; | |
+ | |
+ ret = scnprintf(buf, PAGE_SIZE, | |
+ "pu_en=%d\nxstart=%d\nwalign=%d\nystart=%d\nhalign=%d\n" | |
+ "min_w=%d\nmin_h=%d", | |
+ pinfo->partial_update_enabled, pinfo->xstart_pix_align, | |
+ pinfo->width_pix_align, pinfo->ystart_pix_align, | |
+ pinfo->height_pix_align, pinfo->min_width, | |
+ pinfo->min_height); | |
+ | |
+ return ret; | |
+} | |
+ | |
+/* | |
+ * mdss_fb_lpm_enable() - Function to Control LowPowerMode | |
+ * @mfd: Framebuffer data structure for display | |
+ * @mode: Enabled/Disable LowPowerMode | |
+ * 1: Enter into LowPowerMode | |
+ * 0: Exit from LowPowerMode | |
+ * | |
+ * This Function dynamically switches to and from LowPowerMode | |
+ * based on the argument @mode. | |
+ */ | |
+static int mdss_fb_lpm_enable(struct msm_fb_data_type *mfd, int mode) | |
+{ | |
+ int ret = 0; | |
+ u32 bl_lvl = 0; | |
+ struct mdss_panel_info *pinfo = NULL; | |
+ struct mdss_panel_data *pdata; | |
+ | |
+ if (!mfd || !mfd->panel_info) | |
+ return -EINVAL; | |
+ | |
+ pinfo = mfd->panel_info; | |
+ | |
+ if (!pinfo->mipi.dynamic_switch_enabled) { | |
+ pr_warn("Panel does not support dynamic switch!\n"); | |
+ return 0; | |
+ } | |
+ | |
+ if (mode == pinfo->mipi.mode) { | |
+ pr_debug("Already in requested mode!\n"); | |
+ return 0; | |
+ } | |
+ | |
+ pdata = dev_get_platdata(&mfd->pdev->dev); | |
+ | |
+ pr_debug("Enter mode: %d\n", mode); | |
+ pdata->panel_info.dynamic_switch_pending = true; | |
+ | |
+ mutex_lock(&mfd->bl_lock); | |
+ bl_lvl = mfd->bl_level; | |
+ mdss_fb_set_backlight(mfd, 0); | |
+ mutex_unlock(&mfd->bl_lock); | |
+ | |
+ lock_fb_info(mfd->fbi); | |
+ ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, | |
+ mfd->op_enable); | |
+ if (ret) { | |
+ pr_err("can't turn off display!\n"); | |
+ unlock_fb_info(mfd->fbi); | |
+ return ret; | |
+ } | |
+ | |
+ mfd->op_enable = false; | |
+ | |
+ ret = mfd->mdp.configure_panel(mfd, mode); | |
+ mdss_fb_set_mdp_sync_pt_threshold(mfd); | |
+ | |
+ mfd->op_enable = true; | |
+ | |
+ ret = mdss_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi, | |
+ mfd->op_enable); | |
+ if (ret) { | |
+ pr_err("can't turn on display!\n"); | |
+ unlock_fb_info(mfd->fbi); | |
+ return ret; | |
+ } | |
+ unlock_fb_info(mfd->fbi); | |
+ | |
+ mutex_lock(&mfd->bl_lock); | |
+ mfd->bl_updated = true; | |
+ mdss_fb_set_backlight(mfd, bl_lvl); | |
+ mutex_unlock(&mfd->bl_lock); | |
+ | |
+ pdata->panel_info.dynamic_switch_pending = false; | |
+ pdata->panel_info.is_lpm_mode = mode ? 1 : 0; | |
+ | |
+ if (ret) { | |
+ pr_err("can't turn on display!\n"); | |
+ return ret; | |
+ } | |
+ | |
+ pr_debug("Exit mode: %d\n", mode); | |
+ | |
+ return 0; | |
+} | |
+ | |
static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL); | |
static DEVICE_ATTR(msm_fb_split, S_IRUGO, mdss_fb_get_split, NULL); | |
static DEVICE_ATTR(show_blank_event, S_IRUGO, mdss_mdp_show_blank_event, NULL); | |
static DEVICE_ATTR(idle_time, S_IRUGO | S_IWUSR | S_IWGRP, | |
mdss_fb_get_idle_time, mdss_fb_set_idle_time); | |
static DEVICE_ATTR(idle_notify, S_IRUGO, mdss_fb_get_idle_notify, NULL); | |
+static DEVICE_ATTR(msm_fb_panel_info, S_IRUGO, mdss_fb_get_panel_info, NULL); | |
static struct attribute *mdss_fb_attrs[] = { | |
&dev_attr_msm_fb_type.attr, | |
@@ -343,6 +497,7 @@ static struct attribute *mdss_fb_attrs[] = { | |
&dev_attr_show_blank_event.attr, | |
&dev_attr_idle_time.attr, | |
&dev_attr_idle_notify.attr, | |
+ &dev_attr_msm_fb_panel_info.attr, | |
NULL, | |
}; | |
@@ -410,8 +565,10 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
mfd->ext_ad_ctrl = -1; | |
mfd->bl_level = 0; | |
+ mfd->bl_level_prev_scaled = 0; | |
mfd->bl_scale = 1024; | |
mfd->bl_min_lvl = 30; | |
+ mfd->ad_bl_level = 0; | |
mfd->fb_imgType = MDP_RGBA_8888; | |
mfd->pdev = pdev; | |
@@ -420,7 +577,6 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
mfd->mdp = *mdp_instance; | |
INIT_LIST_HEAD(&mfd->proc_list); | |
- mutex_init(&mfd->lock); | |
mutex_init(&mfd->bl_lock); | |
fbi_list[fbi_list_index++] = fbi; | |
@@ -473,6 +629,21 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
__mdss_fb_sync_buf_done_callback; | |
} | |
+ mdss_fb_set_mdp_sync_pt_threshold(mfd); | |
+ | |
+ if (mfd->mdp.splash_init_fnc) | |
+ mfd->mdp.splash_init_fnc(mfd); | |
+ | |
+ INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work); | |
+ | |
+ return rc; | |
+} | |
+ | |
+static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd) | |
+{ | |
+ if (!mfd) | |
+ return; | |
+ | |
switch (mfd->panel.type) { | |
case WRITEBACK_PANEL: | |
mfd->mdp_sync_pt_data.threshold = 1; | |
@@ -487,25 +658,20 @@ static int mdss_fb_probe(struct platform_device *pdev) | |
mfd->mdp_sync_pt_data.retire_threshold = 0; | |
break; | |
} | |
- | |
- INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work); | |
- | |
- return rc; | |
} | |
- | |
static int mdss_fb_remove(struct platform_device *pdev) | |
{ | |
struct msm_fb_data_type *mfd; | |
mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); | |
+ if (!mfd) | |
+ return -ENODEV; | |
+ | |
mdss_fb_remove_sysfs(mfd); | |
pm_runtime_disable(mfd->fbi->dev); | |
- if (!mfd) | |
- return -ENODEV; | |
- | |
if (mfd->key != MFD_KEY) | |
return -EINVAL; | |
@@ -721,26 +887,12 @@ static void mdss_fb_scale_bl(struct msm_fb_data_type *mfd, u32 *bl_lvl) | |
void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
{ | |
struct mdss_panel_data *pdata; | |
- int (*update_ad_input)(struct msm_fb_data_type *mfd); | |
u32 temp = bkl_lvl; | |
+ bool bl_notify_needed = false; | |
-#ifdef CONFIG_MACH_LGE | |
- if(force_set_bl_f || lge_get_boot_mode()== LGE_BOOT_MODE_QEM_130K) { | |
- | |
- pdata = dev_get_platdata(&mfd->pdev->dev); | |
- | |
- if ((pdata) && (pdata->set_backlight)) { | |
- mdss_fb_scale_bl(mfd, &temp); | |
- pdata->set_backlight(pdata, temp); | |
- pr_info("regardless bl_updated, force to set bl. level=%d, laf_mode=%d\n", | |
- temp, lge_get_laf_mode()); | |
- } | |
- return; | |
- } | |
-#endif | |
- | |
- if (((!mfd->panel_power_on && mfd->dcm_state != DCM_ENTER) | |
- || !mfd->bl_updated) && !IS_CALIB_MODE_BL(mfd)) { | |
+ if ((((!mfd->panel_power_on && mfd->dcm_state != DCM_ENTER) | |
+ || !mfd->bl_updated) && !IS_CALIB_MODE_BL(mfd)) || | |
+ mfd->panel_info->cont_splash_enabled) { | |
mfd->unset_bl_level = bkl_lvl; | |
return; | |
} else { | |
@@ -750,6 +902,13 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
pdata = dev_get_platdata(&mfd->pdev->dev); | |
if ((pdata) && (pdata->set_backlight)) { | |
+ if (mfd->mdp.ad_calc_bl) | |
+ (*mfd->mdp.ad_calc_bl)(mfd, temp, &temp, | |
+ &bl_notify_needed); | |
+ if (bl_notify_needed) | |
+ mdss_fb_bl_update_notify(mfd); | |
+ | |
+ mfd->bl_level_prev_scaled = mfd->bl_level_scaled; | |
if (!IS_CALIB_MODE_BL(mfd)) | |
mdss_fb_scale_bl(mfd, &temp); | |
/* | |
@@ -760,20 +919,13 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
* as well as setting bl_level to bkl_lvl even though the | |
* backlight has been set to the scaled value. | |
*/ | |
- if (mfd->bl_level_old == temp) { | |
+ if (mfd->bl_level_scaled == temp) { | |
mfd->bl_level = bkl_lvl; | |
- return; | |
- } | |
- pdata->set_backlight(pdata, temp); | |
- mfd->bl_level = bkl_lvl; | |
- mfd->bl_level_old = temp; | |
- | |
- if (mfd->mdp.update_ad_input) { | |
- update_ad_input = mfd->mdp.update_ad_input; | |
- mutex_unlock(&mfd->bl_lock); | |
- /* Will trigger ad_setup which will grab bl_lock */ | |
- update_ad_input(mfd); | |
- mutex_lock(&mfd->bl_lock); | |
+ } else { | |
+ pr_debug("backlight sent to panel :%d\n", temp); | |
+ pdata->set_backlight(pdata, temp); | |
+ mfd->bl_level = bkl_lvl; | |
+ mfd->bl_level_scaled = temp; | |
} | |
} | |
} | |
@@ -781,18 +933,26 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) | |
void mdss_fb_update_backlight(struct msm_fb_data_type *mfd) | |
{ | |
struct mdss_panel_data *pdata; | |
+ u32 temp; | |
+ bool bl_notify = false; | |
+ mutex_lock(&mfd->bl_lock); | |
if (mfd->unset_bl_level && !mfd->bl_updated) { | |
pdata = dev_get_platdata(&mfd->pdev->dev); | |
if ((pdata) && (pdata->set_backlight)) { | |
- mutex_lock(&mfd->bl_lock); | |
mfd->bl_level = mfd->unset_bl_level; | |
- pdata->set_backlight(pdata, mfd->bl_level); | |
- mfd->bl_level_old = mfd->unset_bl_level; | |
- mutex_unlock(&mfd->bl_lock); | |
+ temp = mfd->bl_level; | |
+ if (mfd->mdp.ad_calc_bl) | |
+ (*mfd->mdp.ad_calc_bl)(mfd, temp, &temp, | |
+ &bl_notify); | |
+ if (bl_notify) | |
+ mdss_fb_bl_update_notify(mfd); | |
+ pdata->set_backlight(pdata, temp); | |
+ mfd->bl_level_scaled = mfd->unset_bl_level; | |
mfd->bl_updated = 1; | |
} | |
} | |
+ mutex_unlock(&mfd->bl_lock); | |
} | |
static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
@@ -817,6 +977,7 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
} | |
mutex_lock(&mfd->update.lock); | |
mfd->update.type = NOTIFY_TYPE_UPDATE; | |
+ mfd->update.is_suspend = 0; | |
mutex_unlock(&mfd->update.lock); | |
/* Start the work thread to signal idle time */ | |
@@ -824,6 +985,13 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
schedule_delayed_work(&mfd->idle_notify_work, | |
msecs_to_jiffies(mfd->idle_time)); | |
} | |
+ | |
+ mutex_lock(&mfd->bl_lock); | |
+ if (!mfd->bl_updated) { | |
+ mfd->bl_updated = 1; | |
+ mdss_fb_set_backlight(mfd, mfd->bl_level_prev_scaled); | |
+ } | |
+ mutex_unlock(&mfd->bl_lock); | |
break; | |
case FB_BLANK_VSYNC_SUSPEND: | |
@@ -836,15 +1004,20 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
mutex_lock(&mfd->update.lock); | |
mfd->update.type = NOTIFY_TYPE_SUSPEND; | |
+ mfd->update.is_suspend = 1; | |
mutex_unlock(&mfd->update.lock); | |
+ complete(&mfd->update.comp); | |
del_timer(&mfd->no_update.timer); | |
mfd->no_update.value = NOTIFY_TYPE_SUSPEND; | |
complete(&mfd->no_update.comp); | |
mfd->op_enable = false; | |
curr_pwr_state = mfd->panel_power_on; | |
+ mutex_lock(&mfd->bl_lock); | |
+ mdss_fb_set_backlight(mfd, 0); | |
mfd->panel_power_on = false; | |
mfd->bl_updated = 0; | |
+ mutex_unlock(&mfd->bl_lock); | |
ret = mfd->mdp.off_fnc(mfd); | |
if (ret) | |
@@ -856,12 +1029,15 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, | |
} | |
break; | |
} | |
+ /* Notify listeners */ | |
+ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event"); | |
return ret; | |
} | |
static int mdss_fb_blank(int blank_mode, struct fb_info *info) | |
{ | |
+ struct mdss_panel_data *pdata; | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
mdss_fb_pan_idle(mfd); | |
@@ -872,71 +1048,297 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) | |
mfd->suspend.panel_power_on = false; | |
return 0; | |
} | |
+ pr_debug("mode: %d\n", blank_mode); | |
+ | |
+ pdata = dev_get_platdata(&mfd->pdev->dev); | |
+ | |
+ if (pdata->panel_info.is_lpm_mode && | |
+ blank_mode == FB_BLANK_UNBLANK) { | |
+ pr_debug("panel is in lpm mode\n"); | |
+ mfd->mdp.configure_panel(mfd, 0); | |
+ mdss_fb_set_mdp_sync_pt_threshold(mfd); | |
+ pdata->panel_info.is_lpm_mode = false; | |
+ } | |
+ | |
return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); | |
} | |
+static inline int mdss_fb_create_ion_client(struct msm_fb_data_type *mfd) | |
+{ | |
+ mfd->fb_ion_client = msm_ion_client_create(-1 , "mdss_fb_iclient"); | |
+ if (IS_ERR_OR_NULL(mfd->fb_ion_client)) { | |
+ pr_err("Err:client not created, val %d\n", | |
+ PTR_RET(mfd->fb_ion_client)); | |
+ mfd->fb_ion_client = NULL; | |
+ return PTR_RET(mfd->fb_ion_client); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+void mdss_fb_free_fb_ion_memory(struct msm_fb_data_type *mfd) | |
+{ | |
+ if (!mfd) { | |
+ pr_err("no mfd\n"); | |
+ return; | |
+ } | |
+ | |
+ if (!mfd->fbi->screen_base) | |
+ return; | |
+ | |
+ if (!mfd->fb_ion_client || !mfd->fb_ion_handle) { | |
+ pr_err("invalid input parameters for fb%d\n", mfd->index); | |
+ return; | |
+ } | |
+ | |
+ mfd->fbi->screen_base = NULL; | |
+ mfd->fbi->fix.smem_start = 0; | |
+ | |
+ ion_unmap_kernel(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ | |
+ if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ ion_unmap_iommu(mfd->fb_ion_client, mfd->fb_ion_handle, | |
+ mfd->mdp.fb_mem_get_iommu_domain(), 0); | |
+ } | |
+ | |
+ ion_free(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ mfd->fb_ion_handle = NULL; | |
+} | |
+ | |
+int mdss_fb_alloc_fb_ion_memory(struct msm_fb_data_type *mfd, size_t fb_size) | |
+{ | |
+ unsigned long buf_size; | |
+ int rc; | |
+ void *vaddr; | |
+ | |
+ if (!mfd) { | |
+ pr_err("Invalid input param - no mfd"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (!mfd->fb_ion_client) { | |
+ rc = mdss_fb_create_ion_client(mfd); | |
+ if (rc < 0) { | |
+ pr_err("fb ion client couldn't be created - %d\n", rc); | |
+ return rc; | |
+ } | |
+ } | |
+ | |
+ pr_debug("size for mmap = %zu", fb_size); | |
+ mfd->fb_ion_handle = ion_alloc(mfd->fb_ion_client, fb_size, SZ_4K, | |
+ ION_HEAP(ION_SYSTEM_HEAP_ID), 0); | |
+ if (IS_ERR_OR_NULL(mfd->fb_ion_handle)) { | |
+ pr_err("unable to alloc fbmem from ion - %ld\n", | |
+ PTR_ERR(mfd->fb_ion_handle)); | |
+ return PTR_ERR(mfd->fb_ion_handle); | |
+ } | |
+ | |
+ if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ rc = ion_map_iommu(mfd->fb_ion_client, mfd->fb_ion_handle, | |
+ mfd->mdp.fb_mem_get_iommu_domain(), 0, SZ_4K, 0, | |
+ &mfd->iova, &buf_size, 0, 0); | |
+ if (rc) { | |
+ pr_err("Cannot map fb_mem to IOMMU. rc=%d\n", rc); | |
+ goto fb_mmap_failed; | |
+ } | |
+ } else { | |
+ pr_err("No IOMMU Domain"); | |
+ goto fb_mmap_failed; | |
+ | |
+ } | |
+ | |
+ vaddr = ion_map_kernel(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ if (IS_ERR_OR_NULL(vaddr)) { | |
+ pr_err("ION memory mapping failed - %ld\n", PTR_ERR(vaddr)); | |
+ rc = PTR_ERR(vaddr); | |
+ if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ ion_unmap_iommu(mfd->fb_ion_client, mfd->fb_ion_handle, | |
+ mfd->mdp.fb_mem_get_iommu_domain(), 0); | |
+ } | |
+ goto fb_mmap_failed; | |
+ } | |
+ | |
+ pr_debug("alloc 0x%zuB vaddr = %p (%pa iova) for fb%d\n", fb_size, | |
+ vaddr, &mfd->iova, mfd->index); | |
+ | |
+ mfd->fbi->screen_base = (char *) vaddr; | |
+ mfd->fbi->fix.smem_start = (unsigned int) mfd->iova; | |
+ mfd->fbi->fix.smem_len = fb_size; | |
+ | |
+ return rc; | |
+ | |
+fb_mmap_failed: | |
+ ion_free(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ return rc; | |
+} | |
+ | |
+/** | |
+ * mdss_fb_fbmem_ion_mmap() - Custom fb mmap() function for MSM driver. | |
+ * | |
+ * @info - Framebuffer info. | |
+ * @vma - VM area which is part of the process virtual memory. | |
+ * | |
+ * This framebuffer mmap function differs from standard mmap() function by | |
+ * allowing for customized page-protection and dynamically allocate framebuffer | |
+ * memory from system heap and map to iommu virtual address. | |
+ * | |
+ * Return: virtual address is returned through vma | |
+ */ | |
+static int mdss_fb_fbmem_ion_mmap(struct fb_info *info, | |
+ struct vm_area_struct *vma) | |
+{ | |
+ int rc = 0; | |
+ size_t req_size, fb_size; | |
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
+ struct sg_table *table; | |
+ unsigned long addr = vma->vm_start; | |
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE; | |
+ struct scatterlist *sg; | |
+ unsigned int i; | |
+ struct page *page; | |
+ | |
+ if (!mfd || !mfd->pdev || !mfd->pdev->dev.of_node) { | |
+ pr_err("Invalid device node\n"); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ req_size = vma->vm_end - vma->vm_start; | |
+ fb_size = mfd->fbi->fix.smem_len; | |
+ if (req_size > fb_size) { | |
+ pr_warn("requested map is greater than framebuffer"); | |
+ return -EOVERFLOW; | |
+ } | |
+ | |
+ if (!mfd->fbi->screen_base) { | |
+ rc = mdss_fb_alloc_fb_ion_memory(mfd, fb_size); | |
+ if (rc < 0) { | |
+ pr_err("fb mmap failed!!!!"); | |
+ return rc; | |
+ } | |
+ } | |
+ | |
+ table = ion_sg_table(mfd->fb_ion_client, mfd->fb_ion_handle); | |
+ if (IS_ERR(table)) { | |
+ pr_err("Unable to get sg_table from ion:%ld\n", PTR_ERR(table)); | |
+ mfd->fbi->screen_base = NULL; | |
+ return PTR_ERR(table); | |
+ } else if (!table) { | |
+ pr_err("sg_list is NULL\n"); | |
+ mfd->fbi->screen_base = NULL; | |
+ return -EINVAL; | |
+ } | |
+ | |
+ page = sg_page(table->sgl); | |
+ if (page) { | |
+ for_each_sg(table->sgl, sg, table->nents, i) { | |
+ unsigned long remainder = vma->vm_end - addr; | |
+ unsigned long len = sg->length; | |
+ | |
+ page = sg_page(sg); | |
+ | |
+ if (offset >= sg->length) { | |
+ offset -= sg->length; | |
+ continue; | |
+ } else if (offset) { | |
+ page += offset / PAGE_SIZE; | |
+ len = sg->length - offset; | |
+ offset = 0; | |
+ } | |
+ len = min(len, remainder); | |
+ | |
+ if (mfd->mdp_fb_page_protection == | |
+ MDP_FB_PAGE_PROTECTION_WRITECOMBINE) | |
+ vma->vm_page_prot = | |
+ pgprot_writecombine(vma->vm_page_prot); | |
+ | |
+ pr_debug("vma=%p, addr=%x len=%ld", | |
+ vma, (unsigned int)addr, len); | |
+ pr_cont("vm_start=%x vm_end=%x vm_page_prot=%ld\n", | |
+ (unsigned int)vma->vm_start, | |
+ (unsigned int)vma->vm_end, | |
+ (unsigned long int)vma->vm_page_prot); | |
+ | |
+ io_remap_pfn_range(vma, addr, page_to_pfn(page), len, | |
+ vma->vm_page_prot); | |
+ addr += len; | |
+ if (addr >= vma->vm_end) | |
+ break; | |
+ } | |
+ } else { | |
+ pr_err("PAGE is null\n"); | |
+ mdss_fb_free_fb_ion_memory(mfd); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ return rc; | |
+} | |
+ | |
/* | |
- * Custom Framebuffer mmap() function for MSM driver. | |
- * Differs from standard mmap() function by allowing for customized | |
- * page-protection. | |
+ * mdss_fb_physical_mmap() - Custom fb mmap() function for MSM driver. | |
+ * | |
+ * @info - Framebuffer info. | |
+ * @vma - VM area which is part of the process virtual memory. | |
+ * | |
+ * This framebuffer mmap function differs from standard mmap() function as | |
+ * map to framebuffer memory from the CMA memory which is allocated during | |
+ * bootup. | |
+ * | |
+ * Return: virtual address is returned through vma | |
*/ | |
-static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) | |
+static int mdss_fb_physical_mmap(struct fb_info *info, | |
+ struct vm_area_struct *vma) | |
{ | |
/* Get frame buffer memory range. */ | |
unsigned long start = info->fix.smem_start; | |
u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); | |
unsigned long off = vma->vm_pgoff << PAGE_SHIFT; | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
- int ret = 0; | |
if (!start) { | |
- pr_warn("No framebuffer memory is allocated.\n"); | |
+ pr_warn("No framebuffer memory is allocated\n"); | |
return -ENOMEM; | |
} | |
- ret = mdss_fb_pan_idle(mfd); | |
- if (ret) { | |
- pr_err("Shutdown pending. Aborting operation\n"); | |
- return ret; | |
- } | |
- | |
/* Set VM flags. */ | |
start &= PAGE_MASK; | |
if ((vma->vm_end <= vma->vm_start) || | |
- (off >= len) || | |
- ((vma->vm_end - vma->vm_start) > (len - off))) | |
+ (off >= len) || | |
+ ((vma->vm_end - vma->vm_start) > (len - off))) | |
return -EINVAL; | |
off += start; | |
if (off < start) | |
return -EINVAL; | |
vma->vm_pgoff = off >> PAGE_SHIFT; | |
/* This is an IO map - tell maydump to skip this VMA */ | |
- vma->vm_flags |= VM_IO | VM_RESERVED; | |
+ vma->vm_flags |= VM_IO; | |
- /* Set VM page protection */ | |
if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE) | |
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | |
- else if (mfd->mdp_fb_page_protection == | |
- MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE) | |
- vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot); | |
- else if (mfd->mdp_fb_page_protection == | |
- MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE) | |
- vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot); | |
- else if (mfd->mdp_fb_page_protection == | |
- MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE) | |
- vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot); | |
- else | |
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | |
/* Remap the frame buffer I/O range */ | |
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | |
- vma->vm_end - vma->vm_start, | |
- vma->vm_page_prot)) | |
+ vma->vm_end - vma->vm_start, | |
+ vma->vm_page_prot)) | |
return -EAGAIN; | |
return 0; | |
} | |
+static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) | |
+{ | |
+ struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
+ int rc = 0; | |
+ | |
+ if (!info->fix.smem_start && !mfd->fb_ion_handle) | |
+ rc = mdss_fb_fbmem_ion_mmap(info, vma); | |
+ else | |
+ rc = mdss_fb_physical_mmap(info, vma); | |
+ | |
+ if (rc < 0) | |
+ pr_err("fb mmap failed with rc = %d", rc); | |
+ | |
+ return rc; | |
+} | |
+ | |
static struct fb_ops mdss_fb_ops = { | |
.owner = THIS_MODULE, | |
.fb_open = mdss_fb_open, | |
@@ -946,56 +1348,70 @@ static struct fb_ops mdss_fb_ops = { | |
.fb_blank = mdss_fb_blank, /* blank display */ | |
.fb_pan_display = mdss_fb_pan_display, /* pan display */ | |
.fb_ioctl = mdss_fb_ioctl, /* perform fb specific ioctl */ | |
+#ifdef CONFIG_COMPAT | |
+ .fb_compat_ioctl = mdss_fb_compat_ioctl, | |
+#endif | |
.fb_mmap = mdss_fb_mmap, | |
}; | |
static int mdss_fb_alloc_fbmem_iommu(struct msm_fb_data_type *mfd, int dom) | |
{ | |
void *virt = NULL; | |
- unsigned long phys = 0; | |
+ phys_addr_t phys = 0; | |
size_t size = 0; | |
struct platform_device *pdev = mfd->pdev; | |
+ int rc = 0; | |
+ struct device_node *fbmem_pnode = NULL; | |
if (!pdev || !pdev->dev.of_node) { | |
pr_err("Invalid device node\n"); | |
return -ENODEV; | |
} | |
- if (of_property_read_u32(pdev->dev.of_node, | |
- "qcom,memory-reservation-size", | |
- &size) || !size) { | |
+ fbmem_pnode = of_parse_phandle(pdev->dev.of_node, | |
+ "linux,contiguous-region", 0); | |
+ if (!fbmem_pnode) { | |
+ pr_debug("fbmem is not reserved for %s\n", pdev->name); | |
mfd->fbi->screen_base = NULL; | |
mfd->fbi->fix.smem_start = 0; | |
- mfd->fbi->fix.smem_len = 0; | |
return 0; | |
+ } else { | |
+ const u32 *addr; | |
+ u64 len; | |
+ | |
+ addr = of_get_address(fbmem_pnode, 0, &len, NULL); | |
+ if (!addr) { | |
+ pr_err("fbmem size is not specified\n"); | |
+ of_node_put(fbmem_pnode); | |
+ return -EINVAL; | |
+ } | |
+ size = (size_t)len; | |
+ of_node_put(fbmem_pnode); | |
} | |
- pr_info("%s frame buffer reserve_size=0x%x\n", __func__, size); | |
+ pr_debug("%s frame buffer reserve_size=0x%zx\n", __func__, size); | |
if (size < PAGE_ALIGN(mfd->fbi->fix.line_length * | |
mfd->fbi->var.yres_virtual)) | |
pr_warn("reserve size is smaller than framebuffer size\n"); | |
- virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0); | |
+ virt = dma_alloc_coherent(&pdev->dev, size, &phys, GFP_KERNEL); | |
if (!virt) { | |
- pr_err("unable to alloc fbmem size=%u\n", size); | |
+ pr_err("unable to alloc fbmem size=%zx\n", size); | |
return -ENOMEM; | |
} | |
- phys = memory_pool_node_paddr(virt); | |
- | |
- msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0, | |
+ rc = msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K, 0, | |
&mfd->iova); | |
- pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n", | |
- size, virt, phys, mfd->index); | |
+ if (rc) | |
+ pr_warn("Cannot map fb_mem %pa to IOMMU. rc=%d\n", &phys, rc); | |
+ | |
+ pr_debug("alloc 0x%zxB @ (%pa phys) (0x%p virt) (%pa iova) for fb%d\n", | |
+ size, &phys, virt, &mfd->iova, mfd->index); | |
mfd->fbi->screen_base = virt; | |
mfd->fbi->fix.smem_start = phys; | |
mfd->fbi->fix.smem_len = size; | |
-#ifdef CONFIG_MACH_LGE | |
- msm_fb_phys_addr_backup = phys; | |
- memset(virt,0,size); | |
-#endif | |
return 0; | |
} | |
@@ -1003,9 +1419,9 @@ static int mdss_fb_alloc_fbmem_iommu(struct msm_fb_data_type *mfd, int dom) | |
static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd) | |
{ | |
- if (mfd->mdp.fb_mem_alloc_fnc) | |
+ if (mfd->mdp.fb_mem_alloc_fnc) { | |
return mfd->mdp.fb_mem_alloc_fnc(mfd); | |
- else if (mfd->mdp.fb_mem_get_iommu_domain) { | |
+ } else if (mfd->mdp.fb_mem_get_iommu_domain) { | |
int dom = mfd->mdp.fb_mem_get_iommu_domain(); | |
if (dom >= 0) | |
return mdss_fb_alloc_fbmem_iommu(mfd, dom); | |
@@ -1183,8 +1599,14 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
var->hsync_len = panel_info->lcdc.h_pulse_width; | |
var->pixclock = panel_info->clk_rate / 1000; | |
- /* id field for fb app */ | |
+ /* | |
+ * Populate smem length here for uspace to get the | |
+ * Framebuffer size when FBIO_FSCREENINFO ioctl is | |
+ * called. | |
+ */ | |
+ fix->smem_len = PAGE_ALIGN(fix->line_length * var->yres) * mfd->fb_page; | |
+ /* id field for fb app */ | |
id = (int *)&mfd->panel; | |
snprintf(fix->id, sizeof(fix->id), "mdssfb_%x", (u32) *id); | |
@@ -1197,12 +1619,10 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
mfd->panel_power_on = false; | |
mfd->dcm_state = DCM_UNINIT; | |
- mdss_fb_parse_dt_split(mfd); | |
+ mdss_fb_parse_dt(mfd); | |
- if (mdss_fb_alloc_fbmem(mfd)) { | |
- pr_err("unable to allocate framebuffer memory\n"); | |
- return -ENOMEM; | |
- } | |
+ if (mdss_fb_alloc_fbmem(mfd)) | |
+ pr_warn("unable to allocate fb memory in fb register\n"); | |
mfd->op_enable = true; | |
@@ -1211,16 +1631,22 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
mutex_init(&mfd->mdp_sync_pt_data.sync_mutex); | |
atomic_set(&mfd->mdp_sync_pt_data.commit_cnt, 0); | |
atomic_set(&mfd->commits_pending, 0); | |
+ atomic_set(&mfd->ioctl_ref_cnt, 0); | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
init_timer(&mfd->no_update.timer); | |
mfd->no_update.timer.function = mdss_fb_no_update_notify_timer_cb; | |
mfd->no_update.timer.data = (unsigned long)mfd; | |
+ mfd->update.ref_count = 0; | |
+ mfd->no_update.ref_count = 0; | |
init_completion(&mfd->update.comp); | |
init_completion(&mfd->no_update.comp); | |
init_completion(&mfd->power_off_comp); | |
init_completion(&mfd->power_set_comp); | |
init_waitqueue_head(&mfd->commit_wait_q); | |
init_waitqueue_head(&mfd->idle_wait_q); | |
+ init_waitqueue_head(&mfd->ioctl_q); | |
+ init_waitqueue_head(&mfd->kickoff_wait_q); | |
ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | |
if (ret) | |
@@ -1233,9 +1659,8 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) | |
return -EPERM; | |
} | |
- pr_info("FrameBuffer[%d] %dx%d size=%d registered successfully!\n", | |
- mfd->index, fbi->var.xres, fbi->var.yres, | |
- fbi->fix.smem_len); | |
+ pr_info("FrameBuffer[%d] %dx%d registered successfully!\n", mfd->index, | |
+ fbi->var.xres, fbi->var.yres); | |
return 0; | |
} | |
@@ -1246,9 +1671,11 @@ static int mdss_fb_open(struct fb_info *info, int user) | |
struct mdss_fb_proc_info *pinfo = NULL; | |
int result; | |
int pid = current->tgid; | |
+ struct task_struct *task = current->group_leader; | |
if (mfd->shutdown_pending) { | |
- pr_err("Shutdown pending. Aborting operation\n"); | |
+ pr_err("Shutdown pending. Aborting operation. Request from pid:%d name=%s\n", | |
+ pid, task->comm); | |
return -EPERM; | |
} | |
@@ -1331,6 +1758,12 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) | |
return -EINVAL; | |
} | |
+ if (!wait_event_timeout(mfd->ioctl_q, | |
+ !atomic_read(&mfd->ioctl_ref_cnt) || !release_all, | |
+ msecs_to_jiffies(1000))) | |
+ pr_warn("fb%d ioctl could not finish. waited 1 sec.\n", | |
+ mfd->index); | |
+ | |
mdss_fb_pan_idle(mfd); | |
pr_debug("release_all = %s\n", release_all ? "true" : "false"); | |
@@ -1401,6 +1834,16 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) | |
mfd->disp_thread = NULL; | |
} | |
+ if (mfd->mdp.release_fnc) { | |
+ ret = mfd->mdp.release_fnc(mfd, true); | |
+ if (ret) | |
+ pr_err("error fb%d release process %s pid=%d\n", | |
+ mfd->index, task->comm, pid); | |
+ } | |
+ | |
+ if (mfd->fb_ion_handle) | |
+ mdss_fb_free_fb_ion_memory(mfd); | |
+ | |
ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, | |
mfd->op_enable); | |
if (ret) { | |
@@ -1408,6 +1851,7 @@ static int mdss_fb_release_all(struct fb_info *info, bool release_all) | |
mfd->index, ret, task->comm, pid); | |
return ret; | |
} | |
+ atomic_set(&mfd->ioctl_ref_cnt, 0); | |
} | |
return ret; | |
@@ -1608,6 +2052,25 @@ static int mdss_fb_pan_idle(struct msm_fb_data_type *mfd) | |
return 0; | |
} | |
+static int mdss_fb_wait_for_kickoff(struct msm_fb_data_type *mfd) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = wait_event_timeout(mfd->kickoff_wait_q, | |
+ (!atomic_read(&mfd->kickoff_pending) || | |
+ mfd->shutdown_pending), | |
+ msecs_to_jiffies(WAIT_DISP_OP_TIMEOUT / 2)); | |
+ if (!ret) { | |
+ pr_err("wait for kickoff timeout %d pending=%d\n", | |
+ ret, atomic_read(&mfd->kickoff_pending)); | |
+ | |
+ } else if (mfd->shutdown_pending) { | |
+ pr_debug("Shutdown signalled\n"); | |
+ return -EPERM; | |
+ } | |
+ | |
+ return 0; | |
+} | |
static int mdss_fb_pan_display_ex(struct fb_info *info, | |
struct mdp_display_commit *disp_commit) | |
@@ -1617,7 +2080,11 @@ static int mdss_fb_pan_display_ex(struct fb_info *info, | |
u32 wait_for_finish = disp_commit->wait_for_finish; | |
int ret = 0; | |
- if (!mfd || (!mfd->op_enable) || (!mfd->panel_power_on)) | |
+ if (!mfd || (!mfd->op_enable)) | |
+ return -EPERM; | |
+ | |
+ if ((!mfd->panel_power_on) && !((mfd->dcm_state == DCM_ENTER) && | |
+ (mfd->panel.type == MIPI_CMD_PANEL))) | |
return -EPERM; | |
if (var->xoffset > (info->var.xres_virtual - info->var.xres)) | |
@@ -1646,6 +2113,7 @@ static int mdss_fb_pan_display_ex(struct fb_info *info, | |
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt); | |
atomic_inc(&mfd->commits_pending); | |
+ atomic_inc(&mfd->kickoff_pending); | |
wake_up_all(&mfd->commit_wait_q); | |
mutex_unlock(&mfd->mdp_sync_pt_data.sync_mutex); | |
if (wait_for_finish) | |
@@ -1668,7 +2136,11 @@ static int mdss_fb_pan_display_sub(struct fb_var_screeninfo *var, | |
{ | |
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; | |
- if ((!mfd->op_enable) || (!mfd->panel_power_on)) | |
+ if (!mfd->op_enable) | |
+ return -EPERM; | |
+ | |
+ if ((!mfd->panel_power_on) && !((mfd->dcm_state == DCM_ENTER) && | |
+ (mfd->panel.type == MIPI_CMD_PANEL))) | |
return -EPERM; | |
if (var->xoffset > (info->var.xres_virtual - info->var.xres)) | |
@@ -1738,6 +2210,8 @@ static int __mdss_fb_perform_commit(struct msm_fb_data_type *mfd) | |
if (ret) | |
pr_err("pan display failed %x on fb%d\n", ret, | |
mfd->index); | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
+ wake_up_all(&mfd->kickoff_wait_q); | |
} | |
if (!ret) | |
mdss_fb_update_backlight(mfd); | |
@@ -1761,7 +2235,7 @@ static int __mdss_fb_display_thread(void *data) | |
mfd->index); | |
while (1) { | |
- wait_event_interruptible(mfd->commit_wait_q, | |
+ wait_event(mfd->commit_wait_q, | |
(atomic_read(&mfd->commits_pending) || | |
kthread_should_stop())); | |
@@ -1774,6 +2248,7 @@ static int __mdss_fb_display_thread(void *data) | |
} | |
atomic_set(&mfd->commits_pending, 0); | |
+ atomic_set(&mfd->kickoff_pending, 0); | |
wake_up_all(&mfd->idle_wait_q); | |
return ret; | |
@@ -1944,6 +2419,8 @@ static int mdss_fb_set_par(struct fb_info *info) | |
else | |
mfd->fbi->fix.line_length = var->xres * var->bits_per_pixel / 8; | |
+ mfd->fbi->fix.smem_len = mfd->fbi->fix.line_length * | |
+ mfd->fbi->var.yres_virtual; | |
if (mfd->panel_reconfig || (mfd->fb_imgType != old_imgType)) { | |
mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); | |
@@ -2233,6 +2710,28 @@ static int mdss_fb_display_commit(struct fb_info *info, | |
return ret; | |
} | |
+static int __ioctl_wait_idle(struct msm_fb_data_type *mfd, u32 cmd) | |
+{ | |
+ int ret = 0; | |
+ | |
+ if (mfd->wait_for_kickoff && | |
+ ((cmd == MSMFB_OVERLAY_PREPARE) || | |
+ (cmd == MSMFB_BUFFER_SYNC) || | |
+ (cmd == MSMFB_OVERLAY_SET))) { | |
+ ret = mdss_fb_wait_for_kickoff(mfd); | |
+ } else if ((cmd != MSMFB_VSYNC_CTRL) && | |
+ (cmd != MSMFB_OVERLAY_VSYNC_CTRL) && | |
+ (cmd != MSMFB_ASYNC_BLIT) && | |
+ (cmd != MSMFB_BLIT) && | |
+ (cmd != MSMFB_NOTIFY_UPDATE) && | |
+ (cmd != MSMFB_OVERLAY_PREPARE)) { | |
+ ret = mdss_fb_pan_idle(mfd); | |
+ } | |
+ | |
+ if (ret) | |
+ pr_debug("Shutdown pending. Aborting operation %x\n", cmd); | |
+ return ret; | |
+} | |
static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
unsigned long arg) | |
@@ -2243,25 +2742,25 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
int ret = -ENOSYS; | |
struct mdp_buf_sync buf_sync; | |
struct msm_sync_pt_data *sync_pt_data = NULL; | |
-#ifdef CONFIG_MACH_LGE | |
- u32 dsi_panel_invert = 0; | |
-#endif | |
+ unsigned int dsi_mode = 0; | |
if (!info || !info->par) | |
return -EINVAL; | |
+ | |
mfd = (struct msm_fb_data_type *)info->par; | |
+ if (!mfd) | |
+ return -EINVAL; | |
+ | |
+ if (mfd->shutdown_pending) | |
+ return -EPERM; | |
+ | |
+ atomic_inc(&mfd->ioctl_ref_cnt); | |
+ | |
mdss_fb_power_setting_idle(mfd); | |
- if ((cmd != MSMFB_VSYNC_CTRL) && (cmd != MSMFB_OVERLAY_VSYNC_CTRL) && | |
- (cmd != MSMFB_ASYNC_BLIT) && (cmd != MSMFB_BLIT) && | |
- (cmd != MSMFB_NOTIFY_UPDATE) && | |
- (cmd != MSMFB_OVERLAY_PREPARE)) { | |
- ret = mdss_fb_pan_idle(mfd); | |
- if (ret) { | |
- pr_debug("Shutdown pending. Aborting operation %x\n", | |
- cmd); | |
- return ret; | |
- } | |
- } | |
+ | |
+ ret = __ioctl_wait_idle(mfd, cmd); | |
+ if (ret) | |
+ goto exit; | |
switch (cmd) { | |
case MSMFB_CURSOR: | |
@@ -2278,15 +2777,19 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
ret = copy_to_user(argp, &fb_page_protection, | |
sizeof(fb_page_protection)); | |
if (ret) | |
- return ret; | |
+ goto exit; | |
break; | |
case MSMFB_BUFFER_SYNC: | |
ret = copy_from_user(&buf_sync, argp, sizeof(buf_sync)); | |
if (ret) | |
- return ret; | |
- if ((!mfd->op_enable) || (!mfd->panel_power_on)) | |
- return -EPERM; | |
+ goto exit; | |
+ | |
+ if ((!mfd->op_enable) || (!mfd->panel_power_on)) { | |
+ ret = -EPERM; | |
+ goto exit; | |
+ } | |
+ | |
if (mfd->mdp.get_sync_fnc) | |
sync_pt_data = mfd->mdp.get_sync_fnc(mfd, &buf_sync); | |
if (!sync_pt_data) | |
@@ -2306,14 +2809,15 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
ret = mdss_fb_display_commit(info, argp); | |
break; | |
-#ifdef CONFIG_MACH_LGE | |
- case MSMFB_INVERT_PANEL: | |
- ret = copy_from_user(&dsi_panel_invert, argp, sizeof(int)); | |
- if(ret) | |
- return ret; | |
- ret = mdss_dsi_panel_invert(dsi_panel_invert); | |
- break; | |
-#endif | |
+ case MSMFB_LPM_ENABLE: | |
+ ret = copy_from_user(&dsi_mode, argp, sizeof(dsi_mode)); | |
+ if (ret) { | |
+ pr_err("%s: MSMFB_LPM_ENABLE ioctl failed\n", __func__); | |
+ goto exit; | |
+ } | |
+ | |
+ ret = mdss_fb_lpm_enable(mfd, dsi_mode); | |
+ break; | |
default: | |
if (mfd->mdp.ioctl_handler) | |
@@ -2324,6 +2828,10 @@ static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, | |
if (ret == -ENOSYS) | |
pr_err("unsupported ioctl (%x)\n", cmd); | |
+exit: | |
+ if (!atomic_dec_return(&mfd->ioctl_ref_cnt)) | |
+ wake_up_all(&mfd->ioctl_q); | |
+ | |
return ret; | |
} | |
@@ -2359,13 +2867,6 @@ static int mdss_fb_register_extra_panel(struct platform_device *pdev, | |
return -EEXIST; | |
} | |
- if ((fb_pdata->panel_info.type != MIPI_VIDEO_PANEL) || | |
- (pdata->panel_info.type != MIPI_VIDEO_PANEL)) { | |
- pr_err("Split panel not supported for panel type %d\n", | |
- pdata->panel_info.type); | |
- return -EINVAL; | |
- } | |
- | |
fb_pdata->next = pdata; | |
return 0; | |
@@ -2411,7 +2912,8 @@ int mdss_register_panel(struct platform_device *pdev, | |
pr_info("adding framebuffer device %s\n", dev_name(&pdev->dev)); | |
fb_pdev = of_platform_device_create(node, NULL, | |
&mdss_pdev->dev); | |
- fb_pdev->dev.platform_data = pdata; | |
+ if (fb_pdev) | |
+ fb_pdev->dev.platform_data = pdata; | |
} | |
if (master_panel && mdp_instance->panel_register_done) | |
@@ -2440,7 +2942,7 @@ int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num) | |
struct fb_info *info; | |
struct msm_fb_data_type *mfd; | |
- if (fb_num > MAX_FBI_LIST) | |
+ if (fb_num >= MAX_FBI_LIST) | |
return -EINVAL; | |
info = fbi_list[fb_num]; | |
@@ -2472,3 +2974,27 @@ int __init mdss_fb_init(void) | |
} | |
module_init(mdss_fb_init); | |
+ | |
+int mdss_fb_suspres_panel(struct device *dev, void *data) | |
+{ | |
+ struct msm_fb_data_type *mfd; | |
+ int rc; | |
+ u32 event; | |
+ | |
+ if (!data) { | |
+ pr_err("Device state not defined\n"); | |
+ return -EINVAL; | |
+ } | |
+ mfd = dev_get_drvdata(dev); | |
+ if (!mfd) | |
+ return 0; | |
+ | |
+ event = *((bool *) data) ? MDSS_EVENT_RESUME : MDSS_EVENT_SUSPEND; | |
+ | |
+ rc = mdss_fb_send_panel_event(mfd, event, NULL); | |
+ if (rc) | |
+ pr_warn("unable to %s fb%d (%d)\n", | |
+ event == MDSS_EVENT_RESUME ? "resume" : "suspend", | |
+ mfd->index, rc); | |
+ return rc; | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment