summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorArun KS <arunks@codeaurora.org>2017-04-06 15:45:04 +0530
committerArun KS <arunks@codeaurora.org>2017-04-06 15:45:04 +0530
commite8a49b120cbc3a0472aebcd0f8b06cf95f775a54 (patch)
tree7b58ec70803bbfda534ed2a8b50d321a1ecb22ba /drivers/video
parent9a0d24cf9f0e1d4cad7cf92f7e50939fb605e075 (diff)
parenta3851309dbf7e919b27e2ec927ba3f6350347dff (diff)
Merge remote-tracking branch 'remotes/origin/msm-4.4' into dev/msm-4.4-8996au
Conflicts: arch/arm/boot/dts/qcom/msm8996pro.dtsi arch/arm64/kernel/Makefile drivers/leds/leds-qpnp-flash.c sound/soc/msm/apq8096-auto.c Change-Id: Idea5d05fec354b8f38ea70643decb03f7b80ddb7 Signed-off-by: Arun KS <arunks@codeaurora.org>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/adf/adf_fops.c73
-rw-r--r--drivers/video/fbdev/core/fbcmap.c27
-rw-r--r--drivers/video/fbdev/efifb.c6
-rw-r--r--drivers/video/fbdev/goldfishfb.c18
-rw-r--r--drivers/video/fbdev/msm/mdss.h9
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.h7
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c46
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c346
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c54
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c13
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c51
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c115
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c17
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c24
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h6
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_cec.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c38
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c39
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h17
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c64
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h13
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c115
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c223
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c18
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c149
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c217
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c28
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_trace.h20
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_util.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h23
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator.c3
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c24
38 files changed, 1438 insertions, 417 deletions
diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c
index 8726617f73ab..705411bfaebb 100644
--- a/drivers/video/adf/adf_fops.c
+++ b/drivers/video/adf/adf_fops.c
@@ -132,7 +132,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng,
eng->ops->n_supported_formats));
mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&eng->base, data.custom_data,
&data.custom_data_size);
mutex_unlock(&dev->client_lock);
@@ -144,7 +144,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng,
goto done;
}
- if (supported_formats && copy_to_user(arg->supported_formats,
+ if (supported_formats && copy_to_user(data.supported_formats,
supported_formats,
n_supported_formats * sizeof(supported_formats[0])))
ret = -EFAULT;
@@ -220,56 +220,45 @@ static int adf_device_post_config(struct adf_device *dev,
int complete_fence_fd;
struct adf_buffer *bufs = NULL;
struct adf_interface **intfs = NULL;
- size_t n_intfs, n_bufs, i;
+ struct adf_post_config data;
+ size_t i;
void *custom_data = NULL;
- size_t custom_data_size;
int ret = 0;
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
complete_fence_fd = get_unused_fd_flags(O_CLOEXEC);
if (complete_fence_fd < 0)
return complete_fence_fd;
- if (get_user(n_intfs, &arg->n_interfaces)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (n_intfs > ADF_MAX_INTERFACES) {
+ if (data.n_interfaces > ADF_MAX_INTERFACES) {
ret = -EINVAL;
goto err_get_user;
}
- if (get_user(n_bufs, &arg->n_bufs)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (n_bufs > ADF_MAX_BUFFERS) {
+ if (data.n_bufs > ADF_MAX_BUFFERS) {
ret = -EINVAL;
goto err_get_user;
}
- if (get_user(custom_data_size, &arg->custom_data_size)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
+ if (data.custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
ret = -EINVAL;
goto err_get_user;
}
- if (n_intfs) {
- intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL);
+ if (data.n_interfaces) {
+ intfs = kmalloc(sizeof(intfs[0]) * data.n_interfaces,
+ GFP_KERNEL);
if (!intfs) {
ret = -ENOMEM;
goto err_get_user;
}
}
- for (i = 0; i < n_intfs; i++) {
+ for (i = 0; i < data.n_interfaces; i++) {
u32 intf_id;
- if (get_user(intf_id, &arg->interfaces[i])) {
+ if (get_user(intf_id, &data.interfaces[i])) {
ret = -EFAULT;
goto err_get_user;
}
@@ -281,31 +270,31 @@ static int adf_device_post_config(struct adf_device *dev,
}
}
- if (n_bufs) {
- bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL);
+ if (data.n_bufs) {
+ bufs = kzalloc(sizeof(bufs[0]) * data.n_bufs, GFP_KERNEL);
if (!bufs) {
ret = -ENOMEM;
goto err_get_user;
}
}
- for (i = 0; i < n_bufs; i++) {
- ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]);
+ for (i = 0; i < data.n_bufs; i++) {
+ ret = adf_buffer_import(dev, &data.bufs[i], &bufs[i]);
if (ret < 0) {
memset(&bufs[i], 0, sizeof(bufs[i]));
goto err_import;
}
}
- if (custom_data_size) {
- custom_data = kzalloc(custom_data_size, GFP_KERNEL);
+ if (data.custom_data_size) {
+ custom_data = kzalloc(data.custom_data_size, GFP_KERNEL);
if (!custom_data) {
ret = -ENOMEM;
goto err_import;
}
- if (copy_from_user(custom_data, arg->custom_data,
- custom_data_size)) {
+ if (copy_from_user(custom_data, data.custom_data,
+ data.custom_data_size)) {
ret = -EFAULT;
goto err_import;
}
@@ -316,8 +305,8 @@ static int adf_device_post_config(struct adf_device *dev,
goto err_import;
}
- complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs,
- n_bufs, custom_data, custom_data_size);
+ complete_fence = adf_device_post_nocopy(dev, intfs, data.n_interfaces,
+ bufs, data.n_bufs, custom_data, data.custom_data_size);
if (IS_ERR(complete_fence)) {
ret = PTR_ERR(complete_fence);
goto err_import;
@@ -327,7 +316,7 @@ static int adf_device_post_config(struct adf_device *dev,
return 0;
err_import:
- for (i = 0; i < n_bufs; i++)
+ for (i = 0; i < data.n_bufs; i++)
adf_buffer_cleanup(&bufs[i]);
err_get_user:
@@ -481,19 +470,19 @@ static int adf_device_get_data(struct adf_device *dev,
data.n_allowed_attachments);
mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&dev->base, data.custom_data,
&data.custom_data_size);
mutex_unlock(&dev->client_lock);
if (ret < 0)
goto done;
- ret = adf_copy_attachment_list_to_user(arg->attachments,
+ ret = adf_copy_attachment_list_to_user(data.attachments,
data.n_attachments, attach, n_attach);
if (ret < 0)
goto done;
- ret = adf_copy_attachment_list_to_user(arg->allowed_attachments,
+ ret = adf_copy_attachment_list_to_user(data.allowed_attachments,
data.n_allowed_attachments, allowed_attach,
n_allowed_attach);
if (ret < 0)
@@ -592,7 +581,7 @@ static int adf_intf_get_data(struct adf_interface *intf,
data.n_available_modes = intf->n_modes;
read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
- if (copy_to_user(arg->available_modes, modelist, modelist_size)) {
+ if (copy_to_user(data.available_modes, modelist, modelist_size)) {
ret = -EFAULT;
goto done;
}
@@ -601,7 +590,7 @@ static int adf_intf_get_data(struct adf_interface *intf,
memcpy(&data.current_mode, &intf->current_mode,
sizeof(intf->current_mode));
- ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&intf->base, data.custom_data,
&data.custom_data_size);
done:
mutex_unlock(&dev->client_lock);
diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
index 021755f7f32d..a04fa896361a 100644
--- a/drivers/video/fbdev/core/fbcmap.c
+++ b/drivers/video/fbdev/core/fbcmap.c
@@ -163,17 +163,18 @@ void fb_dealloc_cmap(struct fb_cmap *cmap)
int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
{
- int tooff = 0, fromoff = 0;
- int size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len - fromoff;
- if (size <= 0)
+ if (fromoff >= from->len || tooff >= to->len)
+ return -EINVAL;
+
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
+ if (size == 0)
return -EINVAL;
size *= sizeof(u16);
@@ -187,22 +188,22 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
{
- u32 tooff = 0, fromoff = 0;
- u32 size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- if ((to->len <= tooff) || (from->len <= fromoff))
+ if (fromoff >= from->len || tooff >= to->len)
return -EINVAL;
- size = to->len - tooff;
- if (size > (from->len - fromoff))
- size = from->len - fromoff;
- size *= sizeof(u16);
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
if (size == 0)
return -EINVAL;
+ size *= sizeof(u16);
+
+
if (copy_to_user(to->red+tooff, from->red+fromoff, size))
return -EFAULT;
diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
index 95d293b7445a..dc2fcda54d53 100644
--- a/drivers/video/fbdev/efifb.c
+++ b/drivers/video/fbdev/efifb.c
@@ -52,9 +52,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 1;
if (regno < 16) {
- red >>= 8;
- green >>= 8;
- blue >>= 8;
+ red >>= 16 - info->var.red.length;
+ green >>= 16 - info->var.green.length;
+ blue >>= 16 - info->var.blue.length;
((u32 *)(info->pseudo_palette))[regno] =
(red << info->var.red.offset) |
(green << info->var.green.offset) |
diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c
index 7f6c9e6cfc6c..1e56b50e4082 100644
--- a/drivers/video/fbdev/goldfishfb.c
+++ b/drivers/video/fbdev/goldfishfb.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
+#include <linux/acpi.h>
enum {
FB_GET_WIDTH = 0x00,
@@ -234,7 +235,7 @@ static int goldfish_fb_probe(struct platform_device *pdev)
fb->fb.var.activate = FB_ACTIVATE_NOW;
fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
- fb->fb.var.pixclock = 10000;
+ fb->fb.var.pixclock = 0;
fb->fb.var.red.offset = 11;
fb->fb.var.red.length = 5;
@@ -304,12 +305,25 @@ static int goldfish_fb_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_fb_of_match[] = {
+ { .compatible = "google,goldfish-fb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_fb_of_match);
+
+static const struct acpi_device_id goldfish_fb_acpi_match[] = {
+ { "GFSH0004", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_fb_acpi_match);
static struct platform_driver goldfish_fb_driver = {
.probe = goldfish_fb_probe,
.remove = goldfish_fb_remove,
.driver = {
- .name = "goldfish_fb"
+ .name = "goldfish_fb",
+ .of_match_table = goldfish_fb_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_fb_acpi_match),
}
};
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index d9a4bd91f3eb..796246a856b4 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -210,6 +210,15 @@ enum mdss_mdp_pipe_type {
MDSS_MDP_PIPE_TYPE_MAX,
};
+enum mdss_mdp_intf_index {
+ MDSS_MDP_NO_INTF,
+ MDSS_MDP_INTF0,
+ MDSS_MDP_INTF1,
+ MDSS_MDP_INTF2,
+ MDSS_MDP_INTF3,
+ MDSS_MDP_MAX_INTF
+};
+
struct reg_bus_client {
char name[MAX_CLIENT_NAME_LEN];
short usecase_ndx;
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 14d998d14eeb..17644e3556b6 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -125,6 +125,7 @@ static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit,
commit32->commit_v1.input_layer_cnt;
commit->commit_v1.left_roi = commit32->commit_v1.left_roi;
commit->commit_v1.right_roi = commit32->commit_v1.right_roi;
+ commit->commit_v1.bl_level = commit32->commit_v1.bl_level;
memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved,
sizeof(commit32->commit_v1.reserved));
}
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h
index 626792925cb6..4f44cd1c9471 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.h
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,9 +19,9 @@
* To allow proper structure padding for 64bit/32bit target
*/
#ifdef __LP64
-#define MDP_LAYER_COMMIT_V1_PAD 3
+#define MDP_LAYER_COMMIT_V1_PAD 2
#else
-#define MDP_LAYER_COMMIT_V1_PAD 4
+#define MDP_LAYER_COMMIT_V1_PAD 3
#endif
struct mdp_buf_sync32 {
@@ -537,6 +537,7 @@ struct mdp_layer_commit_v1_32 {
compat_caddr_t dest_scaler;
uint32_t dest_scaler_cnt;
compat_caddr_t frc_info;
+ uint32_t bl_level; /* BL level to be updated in commit */
uint32_t reserved[MDP_LAYER_COMMIT_V1_PAD];
};
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 3e1dbba7c9ae..e60869144339 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -44,6 +44,8 @@
#define INVALID_XIN_ID 0xFF
+static DEFINE_MUTEX(mdss_debug_lock);
+
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
static int panel_debug_base_open(struct inode *inode, struct file *file)
@@ -93,8 +95,10 @@ static ssize_t panel_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&mdss_debug_lock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&mdss_debug_lock);
pr_debug("offset=%x cnt=%d\n", off, cnt);
@@ -114,15 +118,21 @@ static ssize_t panel_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&mdss_debug_lock);
len = snprintf(buf, sizeof(buf), "0x%02zx %zx\n", dbg->off, dbg->cnt);
- if (len < 0 || len >= sizeof(buf))
+ if (len < 0 || len >= sizeof(buf)) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
+ }
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
@@ -220,11 +230,16 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
if (!dbg)
return -ENODEV;
- if (!dbg->cnt)
+ mutex_lock(&mdss_debug_lock);
+ if (!dbg->cnt) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if (*ppos)
+ if (*ppos) {
+ mutex_unlock(&mdss_debug_lock);
return 0; /* the end */
+ }
/* '0x' + 2 digit + blank = 5 bytes for each number */
reg_buf_len = (dbg->cnt * PANEL_REG_FORMAT_LEN)
@@ -265,11 +280,13 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
kfree(panel_reg_buf);
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
read_reg_fail:
kfree(rx_buf);
kfree(panel_reg_buf);
+ mutex_unlock(&mdss_debug_lock);
return rc;
}
@@ -403,8 +420,10 @@ static ssize_t mdss_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&mdss_debug_lock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&mdss_debug_lock);
pr_debug("offset=%x cnt=%x\n", off, cnt);
@@ -424,15 +443,21 @@ static ssize_t mdss_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&mdss_debug_lock);
len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
- if (len < 0 || len >= sizeof(buf))
+ if (len < 0 || len >= sizeof(buf)) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
+ }
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
@@ -489,6 +514,8 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
return -ENODEV;
}
+ mutex_lock(&mdss_debug_lock);
+
if (!dbg->buf) {
char dump_buf[64];
char *ptr;
@@ -500,6 +527,7 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
if (!dbg->buf) {
pr_err("not enough memory to hold reg dump\n");
+ mutex_unlock(&mdss_debug_lock);
return -ENOMEM;
}
@@ -530,17 +558,21 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
dbg->buf_len = tot;
}
- if (*ppos >= dbg->buf_len)
+ if (*ppos >= dbg->buf_len) {
+ mutex_unlock(&mdss_debug_lock);
return 0; /* done reading */
+ }
len = min(count, dbg->buf_len - (size_t) *ppos);
if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
pr_err("failed to copy to user\n");
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
}
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index bd8e710870f7..226198efbeec 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -54,6 +54,8 @@ struct mdss_dp_attention_node {
#define DEFAULT_VIDEO_RESOLUTION HDMI_VFRMT_640x480p60_4_3
+static int mdss_dp_host_init(struct mdss_panel_data *pdata);
+static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp);
static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv);
static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata);
static inline void mdss_dp_link_maintenance(struct mdss_dp_drv_pdata *dp,
@@ -64,6 +66,8 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
enum notification_status status);
static int mdss_dp_process_phy_test_pattern_request(
struct mdss_dp_drv_pdata *dp);
+static int mdss_dp_send_audio_notification(
+ struct mdss_dp_drv_pdata *dp, int val);
static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
{
@@ -474,6 +478,12 @@ static int mdss_dp_clk_ctrl(struct mdss_dp_drv_pdata *dp_drv,
else
dp_drv->link_clks_on = enable;
+ pr_debug("%s clocks for %s\n",
+ enable ? "enable" : "disable",
+ __mdss_dp_pm_name(pm_type));
+ pr_debug("link_clks:%s core_clks:%s\n",
+ dp_drv->link_clks_on ? "on" : "off",
+ dp_drv->core_clks_on ? "on" : "off");
error:
return ret;
}
@@ -961,6 +971,14 @@ static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv)
ret = -EINVAL;
} else {
ret = 0;
+ /*
+ * The audio subsystem should only be notified once the DP
+ * controller is in SEND_VIDEO state. This will ensure that
+ * the DP audio engine is able to acknowledge the audio unmute
+ * request, which will result in the AFE port being configured
+ * correctly.
+ */
+ mdss_dp_send_audio_notification(dp_drv, true);
}
pr_debug("End--\n");
@@ -995,9 +1013,10 @@ static int dp_get_cable_status(struct platform_device *pdev, u32 vote)
return hpd;
}
-static bool mdss_dp_is_dvi_mode(struct mdss_dp_drv_pdata *dp)
+static bool mdss_dp_sink_audio_supp(struct mdss_dp_drv_pdata *dp)
{
- return hdmi_edid_is_dvi_mode(dp->panel_data.panel_info.edid_data);
+ return hdmi_edid_is_audio_supported(
+ dp->panel_data.panel_info.edid_data);
}
static int dp_audio_info_setup(struct platform_device *pdev,
@@ -1038,6 +1057,22 @@ static int dp_get_audio_edid_blk(struct platform_device *pdev,
return rc;
} /* dp_get_audio_edid_blk */
+static void dp_audio_teardown_done(struct platform_device *pdev)
+{
+ struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev);
+
+ if (!dp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ mdss_dp_audio_enable(&dp->ctrl_io, false);
+ /* Make sure the DP audio engine is disabled */
+ wmb();
+
+ pr_debug("audio engine disabled\n");
+} /* dp_audio_teardown_done */
+
static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
{
int ret = 0;
@@ -1059,6 +1094,8 @@ static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
dp_get_audio_edid_blk;
dp->ext_audio_data.codec_ops.cable_status =
dp_get_cable_status;
+ dp->ext_audio_data.codec_ops.teardown_done =
+ dp_audio_teardown_done;
if (!dp->pdev->dev.of_node) {
pr_err("%s cannot find dp dev.of_node\n", __func__);
@@ -1254,6 +1291,23 @@ exit:
return ret;
}
+static u32 mdss_dp_calc_max_pclk_rate(struct mdss_dp_drv_pdata *dp)
+{
+ u32 bpp = mdss_dp_get_bpp(dp);
+ u32 max_link_rate_khz = dp->dpcd.max_link_rate *
+ (DP_LINK_RATE_MULTIPLIER / 100);
+ u32 max_data_rate_khz = dp->dpcd.max_lane_count *
+ max_link_rate_khz * 8 / 10;
+ u32 max_pclk_rate_khz = max_data_rate_khz / bpp;
+
+ pr_debug("bpp=%d, max_lane_cnt=%d, max_link_rate=%dKHz\n", bpp,
+ dp->dpcd.max_lane_count, max_link_rate_khz);
+ pr_debug("max_data_rate=%dKHz, max_pclk_rate=%dKHz\n",
+ max_data_rate_khz, max_pclk_rate_khz);
+
+ return max_pclk_rate_khz;
+}
+
static void mdss_dp_set_clock_rate(struct mdss_dp_drv_pdata *dp,
char *name, u32 rate)
{
@@ -1284,6 +1338,11 @@ static int mdss_dp_enable_mainlink_clocks(struct mdss_dp_drv_pdata *dp)
if (dp->pixel_clk_rcg && dp->pixel_parent)
clk_set_parent(dp->pixel_clk_rcg, dp->pixel_parent);
+ if (dp->link_clks_on) {
+ pr_debug("link clocks already on\n");
+ return ret;
+ }
+
mdss_dp_set_clock_rate(dp, "ctrl_link_clk",
(dp->link_rate * DP_LINK_RATE_MULTIPLIER) / DP_KHZ_TO_HZ);
@@ -1308,6 +1367,11 @@ static int mdss_dp_enable_mainlink_clocks(struct mdss_dp_drv_pdata *dp)
*/
static void mdss_dp_disable_mainlink_clocks(struct mdss_dp_drv_pdata *dp_drv)
{
+ if (!dp_drv->link_clks_on) {
+ pr_debug("link clocks already off\n");
+ return;
+ }
+
mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false);
}
@@ -1347,7 +1411,7 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
static int mdss_dp_setup_main_link(struct mdss_dp_drv_pdata *dp, bool train)
{
int ret = 0;
- int ready = 0;
+ bool mainlink_ready = false;
pr_debug("enter\n");
mdss_dp_mainlink_ctrl(&dp->ctrl_io, true);
@@ -1380,8 +1444,8 @@ send_video:
mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO);
mdss_dp_wait4video_ready(dp);
- ready = mdss_dp_mainlink_ready(dp, BIT(0));
- pr_debug("main link %s\n", ready ? "READY" : "NOT READY");
+ mainlink_ready = mdss_dp_mainlink_ready(dp);
+ pr_debug("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
end:
return ret;
@@ -1448,8 +1512,14 @@ exit_loop:
pr_debug("end\n");
- /* Send a connect notification */
- if (!mdss_dp_is_phy_test_pattern_requested(dp_drv))
+ /*
+ * Send a connect notification to clients except when processing link
+ * training and electrical compliance tests. There is no need to send
+ * a notification in these testing use cases as there is no
+ * expectation of receiving a video signal as part of the test.
+ */
+ if (!mdss_dp_is_phy_test_pattern_requested(dp_drv) &&
+ !mdss_dp_is_link_training_requested(dp_drv))
mdss_dp_notify_clients(dp_drv, NOTIFY_CONNECT_IRQ_HPD);
return ret;
@@ -1513,8 +1583,14 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv)
link_training:
dp_drv->power_on = true;
- while (-EAGAIN == mdss_dp_setup_main_link(dp_drv, true))
+ while (-EAGAIN == mdss_dp_setup_main_link(dp_drv, true)) {
pr_debug("MAIN LINK TRAINING RETRY\n");
+ mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false);
+ /* Disable DP mainlink clocks */
+ mdss_dp_disable_mainlink_clocks(dp_drv);
+ /* Enable DP mainlink clocks with reduced link rate */
+ mdss_dp_enable_mainlink_clocks(dp_drv);
+ }
dp_drv->cont_splash = 0;
@@ -1539,10 +1615,28 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
panel_data);
if (dp_drv->power_on) {
+ /*
+ * Acknowledge the connection event if link training has already
+ * been done. This will unblock the external display thread and
+ * allow the driver to progress. For example, in the case of
+ * video test pattern requests, to send the test response and
+ * start transmitting the test pattern.
+ */
+ mdss_dp_ack_state(dp_drv, true);
pr_debug("Link already setup, return\n");
return 0;
}
+ /*
+ * During device suspend, host_deinit() is called
+ * to release DP resources. PM_RESUME can be
+ * called for any module wake-up. To avoid multiple host
+ * init/deinit during unrelated resume/suspend events,
+ * add host initialization call before DP power-on.
+ */
+ if (!dp_drv->dp_initialized)
+ mdss_dp_host_init(pdata);
+
return mdss_dp_on_hpd(dp_drv);
}
@@ -1590,25 +1684,7 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_audio_enable(&dp_drv->ctrl_io, false);
- mdss_dp_irq_disable(dp_drv);
-
- mdss_dp_config_gpios(dp_drv, false);
- mdss_dp_pinctrl_set_state(dp_drv, false);
-
- /*
- * The global reset will need DP link ralated clocks to be
- * running. Add the global reset just before disabling the
- * link clocks and core clocks.
- */
- mdss_dp_ctrl_reset(&dp_drv->ctrl_io);
-
- /* Make sure DP is disabled before clk disable */
- wmb();
- mdss_dp_disable_mainlink_clocks(dp_drv);
- mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false);
-
- mdss_dp_regulator_ctrl(dp_drv, false);
- dp_drv->dp_initialized = false;
+ mdss_dp_host_deinit(dp_drv);
dp_drv->power_on = false;
dp_drv->sink_info_read = false;
@@ -1639,26 +1715,49 @@ int mdss_dp_off(struct mdss_panel_data *pdata)
return mdss_dp_off_hpd(dp);
}
-static int mdss_dp_send_cable_notification(
+static int mdss_dp_send_audio_notification(
struct mdss_dp_drv_pdata *dp, int val)
{
int ret = 0;
u32 flags = 0;
if (!dp) {
- DEV_ERR("%s: invalid input\n", __func__);
+ pr_err("invalid input\n");
ret = -EINVAL;
goto end;
}
- flags |= MSM_EXT_DISP_HPD_VIDEO;
-
- if (!mdss_dp_is_dvi_mode(dp) || dp->audio_test_req) {
+ if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) {
dp->audio_test_req = false;
+ pr_debug("sending audio notification\n");
flags |= MSM_EXT_DISP_HPD_AUDIO;
+
+ if (dp->ext_audio_data.intf_ops.hpd)
+ ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
+ dp->ext_audio_data.type, val, flags);
+ } else {
+ pr_debug("sink does not support audio\n");
}
+end:
+ return ret;
+}
+
+static int mdss_dp_send_video_notification(
+ struct mdss_dp_drv_pdata *dp, int val)
+{
+ int ret = 0;
+ u32 flags = 0;
+
+ if (!dp) {
+ pr_err("invalid input\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ flags |= MSM_EXT_DISP_HPD_VIDEO;
+
if (dp->ext_audio_data.intf_ops.hpd)
ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
dp->ext_audio_data.type, val, flags);
@@ -1673,6 +1772,19 @@ static void mdss_dp_set_default_resolution(struct mdss_dp_drv_pdata *dp)
DEFAULT_VIDEO_RESOLUTION, true);
}
+static void mdss_dp_set_default_link_parameters(struct mdss_dp_drv_pdata *dp)
+{
+ const int default_max_link_rate = 0x6;
+ const int default_max_lane_count = 1;
+
+ dp->dpcd.max_lane_count = default_max_lane_count;
+ dp->dpcd.max_link_rate = default_max_link_rate;
+
+ pr_debug("max_link_rate = 0x%x, max_lane_count= 0x%x\n",
+ dp->dpcd.max_link_rate,
+ dp->dpcd.max_lane_count);
+}
+
static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
{
struct mdss_dp_drv_pdata *dp_drv = NULL;
@@ -1701,8 +1813,6 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
dp_drv->edid_buf = edid_init_data.buf;
dp_drv->edid_buf_size = edid_init_data.buf_size;
- mdss_dp_set_default_resolution(dp_drv);
-
return 0;
}
@@ -1776,6 +1886,49 @@ vreg_error:
}
/**
+ * mdss_dp_host_deinit() - Uninitialize DP controller
+ * @dp: Display Port Driver data
+ *
+ * Perform required steps to uninitialize DP controller
+ * and its resources.
+ */
+static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp)
+{
+ if (!dp) {
+ pr_err("Invalid input data\n");
+ return -EINVAL;
+ }
+
+ if (!dp->dp_initialized) {
+ pr_debug("%s: host deinit done already\n", __func__);
+ return 0;
+ }
+
+ mdss_dp_irq_disable(dp);
+
+ mdss_dp_config_gpios(dp, false);
+ mdss_dp_pinctrl_set_state(dp, false);
+
+ /*
+ * The global reset will need DP link ralated clocks to be
+ * running. Add the global reset just before disabling the
+ * link clocks and core clocks.
+ */
+ mdss_dp_ctrl_reset(&dp->ctrl_io);
+
+ /* Make sure DP is disabled before clk disable */
+ wmb();
+ mdss_dp_disable_mainlink_clocks(dp);
+ mdss_dp_clk_ctrl(dp, DP_CORE_PM, false);
+
+ mdss_dp_regulator_ctrl(dp, false);
+ dp->dp_initialized = false;
+ pr_debug("Host deinitialized successfully\n");
+
+ return 0;
+}
+
+/**
* mdss_dp_notify_clients() - notifies DP clients of cable connection
* @dp: Display Port Driver data
* @status: HPD notification status requested
@@ -1804,7 +1957,7 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
goto invalid_request;
/* Follow the same programming as for NOTIFY_CONNECT */
mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_cable_notification(dp, true);
+ mdss_dp_send_video_notification(dp, true);
break;
case NOTIFY_CONNECT:
if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) ||
@@ -1812,16 +1965,18 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
NOTIFY_DISCONNECT_IRQ_HPD))
goto invalid_request;
mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_cable_notification(dp, true);
+ mdss_dp_send_video_notification(dp, true);
break;
case NOTIFY_DISCONNECT:
- mdss_dp_send_cable_notification(dp, false);
+ mdss_dp_send_audio_notification(dp, false);
+ mdss_dp_send_video_notification(dp, false);
break;
case NOTIFY_DISCONNECT_IRQ_HPD:
if (dp->hpd_notification_status == NOTIFY_DISCONNECT)
goto invalid_request;
- mdss_dp_send_cable_notification(dp, false);
+ mdss_dp_send_audio_notification(dp, false);
+ mdss_dp_send_video_notification(dp, false);
if (!IS_ERR_VALUE(ret) && ret) {
reinit_completion(&dp->irq_comp);
ret = wait_for_completion_timeout(&dp->irq_comp,
@@ -1865,31 +2020,51 @@ end:
static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
{
int ret;
+ u32 max_pclk_khz;
if (dp->sink_info_read)
return 0;
pr_debug("start\n");
- mdss_dp_dpcd_cap_read(dp);
+ ret = mdss_dp_dpcd_cap_read(dp);
+ if (ret || !mdss_dp_aux_is_link_rate_valid(dp->dpcd.max_link_rate) ||
+ !mdss_dp_aux_is_lane_count_valid(dp->dpcd.max_lane_count)) {
+ /*
+ * If there is an error in parsing DPCD or if DPCD reports
+ * unsupported link parameters then set the default link
+ * parameters and continue to read EDID.
+ */
+ pr_err("dpcd read failed, set failsafe parameters\n");
+ mdss_dp_set_default_link_parameters(dp);
+ }
ret = mdss_dp_edid_read(dp);
if (ret) {
- pr_debug("edid read error, setting default resolution\n");
-
- mdss_dp_set_default_resolution(dp);
+ pr_err("edid read error, setting default resolution\n");
goto notify;
}
+ max_pclk_khz = mdss_dp_calc_max_pclk_rate(dp);
+ hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data,
+ min(dp->max_pclk_khz, max_pclk_khz));
+
ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data);
if (ret) {
- pr_err("edid parse failed\n");
+ pr_err("edid parse failed, setting default resolution\n");
goto notify;
}
dp->sink_info_read = true;
notify:
+ if (ret) {
+ /* set failsafe parameters */
+ pr_info("falling back to failsafe mode\n");
+ mdss_dp_set_default_resolution(dp);
+ mdss_dp_set_default_link_parameters(dp);
+ }
+
/* Check if there is a PHY_TEST_PATTERN request when we get HPD high.
* Update the DP driver with the test parameters including link rate,
* lane count, voltage level, and pre-emphasis level. Do not notify
@@ -2307,7 +2482,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev,
} else {
dp_send_events(dp, EV_USBPD_DISCOVER_MODES);
}
- } else if (!dp->hpd && dp->power_on) {
+ } else if (!dp->hpd) {
mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT);
}
end:
@@ -2686,6 +2861,22 @@ static void mdss_dp_update_hdcp_info(struct mdss_dp_drv_pdata *dp)
}
}
+/**
+ * mdss_dp_reset_panel_info() - reset the panel_info data
+ * @dp: Display Port Driver data
+ *
+ * This function will reset the panel resolution to
+ * HDMI_VFRMT_UNKNOWN if the sink device is not connected. This will help
+ * to reconfigure the panel resolution during cable connect event.
+ */
+static void mdss_dp_reset_panel_info(struct mdss_dp_drv_pdata *dp)
+{
+ if (dp->suspend_vic != HDMI_VFRMT_UNKNOWN) {
+ dp->suspend_vic = HDMI_VFRMT_UNKNOWN;
+ dp_init_panel_info(dp, dp->suspend_vic);
+ }
+}
+
static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
int event, void *arg)
{
@@ -2705,11 +2896,10 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
switch (event) {
case MDSS_EVENT_UNBLANK:
+ mdss_dp_ack_state(dp, true);
rc = mdss_dp_on(pdata);
break;
case MDSS_EVENT_PANEL_ON:
- mdss_dp_ack_state(dp, true);
-
mdss_dp_update_hdcp_info(dp);
if (dp_is_hdcp_enabled(dp)) {
@@ -2754,6 +2944,31 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
case MDSS_EVENT_CHECK_PARAMS:
rc = mdss_dp_check_params(dp, arg);
break;
+ case MDSS_EVENT_SUSPEND:
+ /*
+ * Make sure DP host_deinit is called
+ * when DP host is initialized but not
+ * powered ON.
+ * For example, this scenerio happens
+ * when you connect DP sink while the
+ * device is in suspend state.
+ */
+ if ((!dp->power_on) && (dp->dp_initialized))
+ rc = mdss_dp_host_deinit(dp);
+
+ /*
+ * For DP suspend/resume use case, CHECK_PARAMS is
+ * not called if the cable status is not changed.
+ * Store the sink resolution in suspend and configure
+ * the resolution during DP resume path.
+ */
+ if (dp->power_on)
+ dp->suspend_vic = dp->vic;
+ break;
+ case MDSS_EVENT_RESUME:
+ if (dp->suspend_vic != HDMI_VFRMT_UNKNOWN)
+ dp_init_panel_info(dp, dp->suspend_vic);
+ break;
default:
pr_debug("unhandled event=%d\n", event);
break;
@@ -2889,8 +3104,6 @@ static int mdss_dp_event_thread(void *data)
return -EINVAL;
ev_data = (struct mdss_dp_event_data *)data;
- init_waitqueue_head(&ev_data->event_q);
- spin_lock_init(&ev_data->event_lock);
while (!kthread_should_stop()) {
wait_event(ev_data->event_q,
@@ -3049,6 +3262,9 @@ static void mdss_dp_event_cleanup(struct mdss_dp_drv_pdata *dp)
static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
{
+ init_waitqueue_head(&dp->dp_event.event_q);
+ spin_lock_init(&dp->dp_event.event_lock);
+
dp->ev_thread = kthread_run(mdss_dp_event_thread,
(void *)&dp->dp_event, "mdss_dp_event");
if (IS_ERR(dp->ev_thread)) {
@@ -3108,6 +3324,27 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
} else {
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
}
+
+ /*
+ * If cable is disconnected during device suspend,
+ * reset the panel resolution to HDMI_VFRMT_UNKNOWN
+ * so that new resolution is configured during
+ * cable connect event
+ */
+ if ((!dp_drv->power_on) && (!dp_drv->dp_initialized))
+ mdss_dp_reset_panel_info(dp_drv);
+
+ /*
+ * If a cable/dongle is connected to the TX device but
+ * no sink device is connected, we call host
+ * initialization where orientation settings are
+ * configured. When the cable/dongle is disconnect,
+ * call host de-initialization to make sure
+ * we re-configure the orientation settings during
+ * the next connect event.
+ */
+ if ((!dp_drv->power_on) && (dp_drv->dp_initialized))
+ mdss_dp_host_deinit(dp_drv);
}
static int mdss_dp_validate_callback(u8 cmd,
@@ -3604,6 +3841,16 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
pr_debug("Attention: Notified clients\n");
+ /*
+ * When a DP adaptor is connected and if sink is
+ * disconnected during device suspend,
+ * reset the panel resolution to HDMI_VFRMT_UNKNOWN
+ * so that new resolution is configured during
+ * connect event.
+ */
+ if ((!dp_drv->power_on) && (!dp_drv->dp_initialized))
+ mdss_dp_reset_panel_info(dp_drv);
+
/**
* Manually turn off the DP controller if we are in PHY
* testing mode.
@@ -3826,11 +4073,10 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->hpd_irq_on = false;
mdss_dp_reset_test_data(dp_drv);
init_completion(&dp_drv->irq_comp);
+ dp_drv->suspend_vic = HDMI_VFRMT_UNKNOWN;
pr_debug("done\n");
- dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
-
return 0;
probe_err:
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index d6f5d160aef2..4decb26ea073 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -450,6 +450,7 @@ struct mdss_dp_drv_pdata {
bool link_clks_on;
bool power_on;
bool sink_info_read;
+ u32 suspend_vic;
bool hpd;
bool psm_enabled;
bool audio_test_req;
@@ -1037,7 +1038,7 @@ static inline void mdss_dp_reset_frame_crc_data(struct mdss_dp_crc_data *crc)
void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp);
-void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);
+int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);
int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp);
void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *dp);
int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp);
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index ca07e80d6613..8566b1d6985a 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -826,9 +826,9 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
return ret;
}
-static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
- int len)
+int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
{
+ int const len = 16; /* read 16 bytes */
char *bp;
char data;
struct dpcd_cap *cap;
@@ -838,8 +838,15 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
rlen = dp_aux_read_buf(ep, 0, len, 0);
if (rlen <= 0) {
pr_err("edp aux read failed\n");
- return;
+ return rlen;
+ }
+
+ if (rlen != len) {
+ pr_debug("Read size expected(%d) bytes, actual(%d) bytes\n",
+ len, rlen);
+ return -EINVAL;
}
+
rp = &ep->rxp;
cap = &ep->dpcd;
bp = rp->data;
@@ -849,15 +856,11 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
data = *bp++; /* byte 0 */
cap->major = (data >> 4) & 0x0f;
cap->minor = data & 0x0f;
- if (--rlen <= 0)
- return;
pr_debug("version: %d.%d\n", cap->major, cap->minor);
data = *bp++; /* byte 1 */
/* 162, 270 and 540 MB, symbol rate, NOT bit rate */
cap->max_link_rate = data;
- if (--rlen <= 0)
- return;
pr_debug("link_rate=%d\n", cap->max_link_rate);
data = *bp++; /* byte 2 */
@@ -873,8 +876,6 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
data &= 0x0f;
cap->max_lane_count = data;
- if (--rlen <= 0)
- return;
pr_debug("lane_count=%d\n", cap->max_lane_count);
data = *bp++; /* byte 3 */
@@ -887,14 +888,10 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap->flags |= DPCD_NO_AUX_HANDSHAKE;
pr_debug("NO Link Training\n");
}
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 4 */
cap->num_rx_port = (data & BIT(0)) + 1;
pr_debug("rx_ports=%d", cap->num_rx_port);
- if (--rlen <= 0)
- return;
data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
cap->downstream_port.dfp_present = data & BIT(0);
@@ -907,13 +904,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n",
cap->downstream_port.format_conversion,
cap->downstream_port.detailed_cap_info_available);
- if (--rlen <= 0)
- return;
bp += 1; /* Skip Byte 6 */
- rlen -= 1;
- if (rlen <= 0)
- return;
data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
cap->downstream_port.dfp_count = data & 0x7;
@@ -923,34 +915,23 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap->downstream_port.dfp_count,
cap->downstream_port.msa_timing_par_ignored);
pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 8 */
if (data & BIT(1)) {
cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
pr_debug("edid presented\n");
}
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 9 */
cap->rx_port0_buf_size = (data + 1) * 32;
pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size);
- if (--rlen <= 0)
- return;
bp += 2; /* skip 10, 11 port1 capability */
- rlen -= 2;
- if (rlen <= 0)
- return;
data = *bp++; /* byte 12 */
cap->i2c_speed_ctrl = data;
if (cap->i2c_speed_ctrl > 0)
pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl);
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 13 */
cap->scrambler_reset = data & BIT(0);
@@ -962,8 +943,6 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
pr_debug("enhanced_framing=%d\n",
cap->enhanced_frame);
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 14 */
if (data == 0)
@@ -974,6 +953,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap->training_read_interval);
dp_sink_parse_sink_count(ep);
+
+ return 0;
}
int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len)
@@ -2247,12 +2228,8 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
else
pattern = 0x02;
- dp_write(ep->base + DP_STATE_CTRL, 0x0);
- /* Make sure to clear the current pattern before starting a new one */
- wmb();
-
- dp_host_train_set(ep, pattern);
mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(ep);
+ dp_host_train_set(ep, pattern);
dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
do {
@@ -2383,11 +2360,6 @@ clear:
return ret;
}
-void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
-{
- dp_sink_capability_read(ep, 16);
-}
-
void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep)
{
dp_sink_parse_sink_count(ep);
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index 3b9242448198..ea492f54054c 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -177,23 +177,22 @@ void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable)
writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL);
}
-int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which)
+bool mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp)
{
u32 data;
int cnt = 10;
+ int const mainlink_ready_bit = BIT(0);
while (--cnt) {
/* DP_MAINLINK_READY */
data = readl_relaxed(dp->base + DP_MAINLINK_READY);
- if (data & which) {
- pr_debug("which=%x ready\n", which);
- return 1;
- }
+ if (data & mainlink_ready_bit)
+ return true;
udelay(1000);
}
- pr_err("which=%x NOT ready\n", which);
+ pr_err("mainlink not ready\n");
- return 0;
+ return false;
}
/* DP Configuration controller*/
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index b3b15a3579fa..8f19e7cdf3cf 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -291,7 +291,7 @@ void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map);
-int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which);
+bool mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp);
void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io,
struct mdss_panel_info *pinfo);
void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 4aa14422899f..17722eac3006 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -34,7 +34,6 @@
#include "mdss_dsi_phy.h"
#include "mdss_dba_utils.h"
-#define XO_CLK_RATE 19200000
#define CMDLINE_DSI_CTL_NUM_STRING_LEN 2
/* Master structure to hold all the information about the DSI/panel */
@@ -1464,15 +1463,6 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)
if (mipi->init_delay)
usleep_range(mipi->init_delay, mipi->init_delay);
- if (mipi->force_clk_lane_hs) {
- u32 tmp;
-
- tmp = MIPI_INP((ctrl_pdata->ctrl_base) + 0xac);
- tmp |= (1<<28);
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xac, tmp);
- wmb();
- }
-
if (pdata->panel_info.type == MIPI_CMD_PANEL)
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF);
@@ -1576,10 +1566,12 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
mdss_dsi_clk_ctrl(sctrl, sctrl->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON);
- if (mdss_dsi_is_panel_on_lp(pdata)) {
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_LP) {
pr_debug("%s: dsi_unblank with panel always on\n", __func__);
if (ctrl_pdata->low_power_config)
ret = ctrl_pdata->low_power_config(pdata, false);
+ if (!ret)
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_LP;
goto error;
}
@@ -1644,6 +1636,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)
pr_debug("%s: low power state requested\n", __func__);
if (ctrl_pdata->low_power_config)
ret = ctrl_pdata->low_power_config(pdata, true);
+ if (!ret)
+ ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_LP;
goto error;
}
@@ -1686,7 +1680,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)
}
ATRACE_END("dsi_panel_off");
}
- ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ ctrl_pdata->ctrl_state &= ~(CTRL_STATE_PANEL_INIT |
+ CTRL_STATE_PANEL_LP);
}
error:
@@ -1866,7 +1861,7 @@ static void __mdss_dsi_dyn_refresh_config(
static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
{
- u32 esc_clk_rate = XO_CLK_RATE;
+ u32 esc_clk_rate_hz;
u32 pipe_delay, pipe_delay2 = 0, pll_delay;
u32 hsync_period = 0;
u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio;
@@ -1893,9 +1888,11 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
pinfo = &pdata->panel_info;
pd = &(pinfo->mipi.dsi_phy_db);
- pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate);
- byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate);
- hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) / esc_clk_rate);
+ esc_clk_rate_hz = ctrl_pdata->esc_clk_rate_hz;
+ pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate_hz);
+ byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate_hz);
+ hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) /
+ esc_clk_rate_hz);
hsync_period = mdss_panel_get_htotal(pinfo, true);
pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio;
@@ -1917,7 +1914,7 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
((pd->timing[4] >> 1) + 1)) / hr_bit_to_esc_ratio);
/* 130 us pll delay recommended by h/w doc */
- pll_delay = ((130 * esc_clk_rate) / 1000000) * 2;
+ pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + DSI_DYNAMIC_REFRESH_PIPE_DELAY,
pipe_delay);
@@ -2510,6 +2507,15 @@ static int mdss_dsi_register_mdp_callback(struct mdss_dsi_ctrl_pdata *ctrl,
return 0;
}
+static int mdss_dsi_register_clamp_handler(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct mdss_intf_ulp_clamp *clamp_handler)
+{
+ mutex_lock(&ctrl->mutex);
+ ctrl->clamp_handler = clamp_handler;
+ mutex_unlock(&ctrl->mutex);
+ return 0;
+}
+
static struct device_node *mdss_dsi_get_fb_node_cb(struct platform_device *pdev)
{
struct device_node *fb_node;
@@ -2550,8 +2556,6 @@ static void mdss_dsi_timing_db_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON);
MIPI_OUTP((ctrl->ctrl_base + 0x1e8), enable);
wmb(); /* ensure timing db is disabled */
- MIPI_OUTP((ctrl->ctrl_base + 0x1e4), enable);
- wmb(); /* ensure timing flush is disabled */
mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle,
MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF);
}
@@ -2696,6 +2700,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_register_mdp_callback(ctrl_pdata,
(struct mdss_intf_recovery *)arg);
break;
+ case MDSS_EVENT_REGISTER_CLAMP_HANDLER:
+ rc = mdss_dsi_register_clamp_handler(ctrl_pdata,
+ (struct mdss_intf_ulp_clamp *)arg);
+ break;
case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
mode = (u32)(unsigned long) arg;
mdss_dsi_switch_mode(pdata, mode);
@@ -3054,7 +3062,7 @@ static int mdss_dsi_set_clk_rates(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
rc = mdss_dsi_clk_set_link_rate(ctrl_pdata->dsi_clk_handle,
MDSS_DSI_LINK_ESC_CLK,
- 19200000,
+ ctrl_pdata->esc_clk_rate_hz,
MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON);
if (rc) {
pr_err("%s: dsi_esc_clk - clk_set_rate failed\n",
@@ -4233,6 +4241,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev,
pr_debug("%s: pclk=%d, bclk=%d\n", __func__,
ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate);
+ ctrl_pdata->esc_clk_rate_hz = pinfo->esc_clk_rate_hz;
+ pr_debug("%s: esc clk=%d\n", __func__,
+ ctrl_pdata->esc_clk_rate_hz);
rc = mdss_dsi_get_dt_vreg_data(&ctrl_pdev->dev, pan_node,
&ctrl_pdata->panel_power_data, DSI_PANEL_PM);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index df24352ff87d..2a76466abf3e 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -166,6 +166,7 @@ enum dsi_pm_type {
#define CTRL_STATE_PANEL_INIT BIT(0)
#define CTRL_STATE_MDP_ACTIVE BIT(1)
#define CTRL_STATE_DSI_ACTIVE BIT(2)
+#define CTRL_STATE_PANEL_LP BIT(3)
#define DSI_NON_BURST_SYNCH_PULSE 0
#define DSI_NON_BURST_SYNCH_EVENT 1
@@ -476,6 +477,7 @@ struct mdss_dsi_ctrl_pdata {
u32 byte_clk_rate;
u32 pclk_rate_bkp;
u32 byte_clk_rate_bkp;
+ u32 esc_clk_rate_hz;
bool refresh_clk_rate; /* flag to recalculate clk_rate */
struct dss_module_power panel_power_data;
struct dss_module_power power_data[DSI_MAX_PM]; /* for 8x10 */
@@ -483,6 +485,7 @@ struct mdss_dsi_ctrl_pdata {
struct mdss_hw *dsi_hw;
struct mdss_intf_recovery *recovery;
struct mdss_intf_recovery *mdp_callback;
+ struct mdss_intf_ulp_clamp *clamp_handler;
struct dsi_panel_cmds on_cmds;
struct dsi_panel_cmds post_dms_on_cmds;
@@ -696,6 +699,8 @@ void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsc_desc *dsc);
void mdss_dsi_dfps_config_8996(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+ u32 bits, int set);
void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off,
u32 mask, u32 val);
int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index d57558a1b52d..9f4b7eb52492 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -41,6 +41,10 @@
#define LANE_SWAP_CTRL 0x0B0
#define LOGICAL_LANE_SWAP_CTRL 0x310
+#define MAX_BTA_WAIT_RETRY 5
+
+#define CEIL(x, y) (((x) + ((y)-1)) / (y))
+
struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
struct mdss_hw mdss_dsi0_hw = {
@@ -598,7 +602,7 @@ error:
return rc;
}
-static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
u32 bits, int set)
{
u32 data;
@@ -609,6 +613,7 @@ static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
else
data &= ~bits;
MIPI_OUTP(ctrl->ctrl_base + 0x0ac, data);
+ wmb(); /* make sure write happens */
}
@@ -764,6 +769,8 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
u32 data0, data1, mask = 0, data_lane_en = 0;
struct mdss_dsi_ctrl_pdata *ctrl0, *ctrl1;
u32 ln0, ln1, ln_ctrl0, ln_ctrl1, i;
+ int rc = 0;
+
/*
* Add 2 ms delay suggested by HW team.
* Check clk lane stop state after every 200 us
@@ -785,9 +792,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
ctrl1 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_1);
- if (ctrl0->recovery)
- ctrl0->recovery->fxn(ctrl0->recovery->data,
+ if (ctrl0->recovery) {
+ rc = ctrl0->recovery->fxn(ctrl0->recovery->data,
MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/*
* Disable PHY contention detection and receive.
* Configure the strength ctrl 1 register.
@@ -877,9 +890,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
*/
udelay(200);
} else {
- if (ctrl->recovery)
- ctrl->recovery->fxn(ctrl->recovery->data,
+ if (ctrl->recovery) {
+ rc = ctrl->recovery->fxn(ctrl->recovery->data,
MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/* Disable PHY contention detection and receive */
MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0);
@@ -1423,6 +1442,34 @@ void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl)
mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, pdata);
}
+static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ int ret = 0;
+ u32 v_total = 0, v_blank = 0, sleep_ms = 0, fps = 0;
+ struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info;
+
+ if (ctrl->panel_mode == DSI_CMD_MODE)
+ return ret;
+
+ if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) {
+ mdss_dsi_wait4video_done(ctrl);
+ v_total = mdss_panel_get_vtotal(pinfo);
+ v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
+ if (pinfo->dynamic_fps && pinfo->current_fps)
+ fps = pinfo->current_fps;
+ else
+ fps = pinfo->mipi.frame_rate;
+
+ sleep_ms = CEIL((v_blank * 1000), (v_total * fps)) + 1;
+ /* delay sleep_ms to skip BLLP */
+ if (sleep_ms)
+ udelay(sleep_ms * 1000);
+ ret = 1;
+ }
+
+ return ret;
+}
+
/**
* mdss_dsi_bta_status_check() - Check dsi panel status through bta check
* @ctrl_pdata: pointer to the dsi controller structure
@@ -1438,6 +1485,8 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
int ret = 0;
unsigned long flag;
int ignore_underflow = 0;
+ int retry_count = 0;
+ int in_blanking = 0;
if (ctrl_pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
@@ -1463,6 +1512,25 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
reinit_completion(&ctrl_pdata->bta_comp);
mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM);
spin_unlock_irqrestore(&ctrl_pdata->mdp_lock, flag);
+wait:
+ mdss_dsi_wait4video_eng_busy(ctrl_pdata);
+ if (ctrl_pdata->panel_mode == DSI_VIDEO_MODE) {
+ in_blanking = ctrl_pdata->mdp_callback->fxn(
+ ctrl_pdata->mdp_callback->data,
+ MDP_INTF_CALLBACK_CHECK_LINE_COUNT);
+ /* Try for maximum of 5 attempts */
+ if (in_blanking && (retry_count < MAX_BTA_WAIT_RETRY)) {
+ pr_debug("%s: not in active region\n", __func__);
+ retry_count++;
+ goto wait;
+ }
+ }
+ if (retry_count == MAX_BTA_WAIT_RETRY)
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
+ "dsi0_phy", "dsi1_ctrl", "dsi1_phy",
+ "vbif", "vbif_nrt", "dbg_bus",
+ "vbif_dbg_bus", "panic");
+
/* mask out overflow errors */
if (ignore_underflow)
mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000);
@@ -2102,8 +2170,8 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
status = reg_val & DSI_INTR_CMD_DMA_DONE;
if (status) {
reg_val &= DSI_INTR_MASK_ALL;
- /* clear CMD DMA isr only */
- reg_val |= DSI_INTR_CMD_DMA_DONE;
+ /* clear CMD DMA and BTA_DONE isr only */
+ reg_val |= (DSI_INTR_CMD_DMA_DONE | DSI_INTR_BTA_DONE);
MIPI_OUTP(ctrl->ctrl_base + 0x0110, reg_val);
mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM);
complete(&ctrl->dma_comp);
@@ -2348,15 +2416,20 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
/* DSI_INTL_CTRL */
data = MIPI_INP((ctrl->ctrl_base) + 0x0110);
data &= DSI_INTR_TOTAL_MASK;
- data |= DSI_INTR_VIDEO_DONE_MASK;
- MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+ /* clear previous VIDEO_DONE interrupt */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, (data | DSI_INTR_VIDEO_DONE));
+ wmb(); /* ensure interrupt is cleared */
spin_lock_irqsave(&ctrl->mdp_lock, flag);
reinit_completion(&ctrl->video_comp);
mdss_dsi_enable_irq(ctrl, DSI_VIDEO_TERM);
spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
+ data |= DSI_INTR_VIDEO_DONE_MASK;
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+ wmb(); /* ensure interrupt is enabled */
+
wait_for_completion_timeout(&ctrl->video_comp,
msecs_to_jiffies(VSYNC_PERIOD * 4));
@@ -2366,23 +2439,6 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
}
-static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
-{
- int ret = 0;
-
- if (ctrl->panel_mode == DSI_CMD_MODE)
- return ret;
-
- if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) {
- mdss_dsi_wait4video_done(ctrl);
- /* delay 4 ms to skip BLLP */
- usleep_range(4000, 4000);
- ret = 1;
- }
-
- return ret;
-}
-
void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl)
{
unsigned long flag;
@@ -3010,6 +3066,13 @@ static bool mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl)
pr_err("%s: status=%x\n", __func__, status);
+ /*
+ * if DSI FIFO overflow is masked,
+ * do not report overflow error
+ */
+ if (MIPI_INP(base + 0x10c) & 0xf0000)
+ status = status & 0xaaaaffff;
+
if (status & 0x44440000) {/* DLNx_HS_FIFO_OVERFLOW */
dsi_send_events(ctrl, DSI_EV_DLNx_FIFO_OVERFLOW, 0);
/* Ignore FIFO EMPTY when overflow happens */
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 7cc9ce6e034d..6f20c0ed0455 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -365,13 +365,17 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) {
- if (ctrl_pdata->bklt_en_gpio_invert)
+ if (ctrl_pdata->bklt_en_gpio_invert) {
rc = gpio_direction_output(
ctrl_pdata->bklt_en_gpio, 0);
- else
+ gpio_set_value(
+ (ctrl_pdata->bklt_en_gpio), 0);
+ } else {
rc = gpio_direction_output(
ctrl_pdata->bklt_en_gpio, 1);
-
+ gpio_set_value(
+ (ctrl_pdata->bklt_en_gpio), 1);
+ }
if (rc) {
pr_err("%s: unable to set dir for bklt gpio\n",
__func__);
@@ -2819,6 +2823,13 @@ static int mdss_panel_parse_dt(struct device_node *np,
MSM_DBA_CHIP_NAME_MAX_LEN);
}
+ rc = of_property_read_u32(np,
+ "qcom,mdss-dsi-host-esc-clk-freq-hz",
+ &pinfo->esc_clk_rate_hz);
+ if (rc)
+ pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ;
+ pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz);
+
return 0;
error:
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index db27842eaccc..fc47de7692e7 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -301,12 +301,26 @@ static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
mdss_fb_set_backlight(mfd, bl_lvl);
mutex_unlock(&mfd->bl_lock);
}
+ mfd->bl_level_usr = bl_lvl;
+}
+
+static enum led_brightness mdss_fb_get_bl_brightness(
+ struct led_classdev *led_cdev)
+{
+ struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
+ enum led_brightness value;
+
+ MDSS_BL_TO_BRIGHT(value, mfd->bl_level_usr, mfd->panel_info->bl_max,
+ mfd->panel_info->brightness_max);
+
+ return value;
}
static struct led_classdev backlight_led = {
.name = "lcd-backlight",
.brightness = MDSS_MAX_BL_BRIGHTNESS / 2,
.brightness_set = mdss_fb_set_bl_brightness,
+ .brightness_get = mdss_fb_get_bl_brightness,
.max_brightness = MDSS_MAX_BL_BRIGHTNESS,
};
@@ -1262,6 +1276,8 @@ static int mdss_fb_probe(struct platform_device *pdev)
mfd->fb_imgType = MDP_RGBA_8888;
mfd->calib_mode_bl = 0;
mfd->unset_bl_level = U32_MAX;
+ mfd->bl_extn_level = -1;
+ mfd->bl_level_usr = backlight_led.brightness;
mfd->pdev = pdev;
@@ -3422,6 +3438,14 @@ int mdss_fb_atomic_commit(struct fb_info *info,
mfd->msm_fb_backup.disp_commit.l_roi = commit_v1->left_roi;
mfd->msm_fb_backup.disp_commit.r_roi = commit_v1->right_roi;
mfd->msm_fb_backup.disp_commit.flags = commit_v1->flags;
+ if (commit_v1->flags & MDP_COMMIT_UPDATE_BRIGHTNESS) {
+ MDSS_BRIGHT_TO_BL(mfd->bl_extn_level, commit_v1->bl_level,
+ mfd->panel_info->bl_max,
+ mfd->panel_info->brightness_max);
+ if (!mfd->bl_extn_level && commit_v1->bl_level)
+ mfd->bl_extn_level = 1;
+ } else
+ mfd->bl_extn_level = -1;
mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex);
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt);
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index d64580a35775..f046ff08cbf7 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -241,6 +241,10 @@ struct msm_mdp_interface {
out = (2 * (v) * (bl_max) + max_bright);\
do_div(out, 2 * max_bright);\
} while (0)
+#define MDSS_BL_TO_BRIGHT(out, v, bl_max, max_bright) do {\
+ out = ((v) * (max_bright));\
+ do_div(out, bl_max);\
+ } while (0)
struct mdss_fb_file_info {
struct file *file;
@@ -305,10 +309,12 @@ struct msm_fb_data_type {
u32 calib_mode_bl;
u32 ad_bl_level;
u32 bl_level;
+ int bl_extn_level;
u32 bl_scale;
u32 unset_bl_level;
bool allow_bl_update;
u32 bl_level_scaled;
+ u32 bl_level_usr;
struct mutex bl_lock;
struct mutex mdss_sysfs_lock;
bool ipc_resume;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_cec.c b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
index a424d987bb63..a4ed01210e04 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_cec.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -196,7 +196,7 @@ static void hdmi_cec_msg_recv(struct work_struct *work)
msg.sender_id, msg.recvr_id,
msg.frame_size);
- if (msg.frame_size < 1) {
+ if (msg.frame_size < 1 || msg.frame_size > MAX_CEC_FRAME_SIZE) {
DEV_ERR("%s: invalid message (frame length = %d)\n",
__func__, msg.frame_size);
return;
@@ -216,7 +216,7 @@ static void hdmi_cec_msg_recv(struct work_struct *work)
msg.operand[i] = data & 0xFF;
}
- for (; i < 14; i++)
+ for (; i < MAX_OPERAND_SIZE; i++)
msg.operand[i] = 0;
DEV_DBG("%s: opcode 0x%x, wakup_en %d, device_suspend %d\n", __func__,
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 3d8fe2ceebac..37c4be6135aa 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -137,6 +137,7 @@ struct hdmi_edid_ctrl {
u16 video_latency;
u32 present_3d;
u32 page_id;
+ bool basic_audio_supp;
u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE];
int adb_size;
u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
@@ -1289,6 +1290,14 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl,
return;
}
+ /* Check if sink supports basic audio */
+ if (in_buf[3] & BIT(6))
+ edid_ctrl->basic_audio_supp = true;
+ else
+ edid_ctrl->basic_audio_supp = false;
+ pr_debug("%s: basic audio supported: %s\n", __func__,
+ edid_ctrl->basic_audio_supp ? "true" : "false");
+
vsd = hdmi_edid_find_hfvsdb(in_buf);
if (vsd) {
@@ -1501,6 +1510,17 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl,
*/
active_h = ((((u32)data_buf[0x4] >> 0x4) & 0xF) << 8)
| data_buf[0x2];
+ /*
+ * It is possible that a sink might try to fit in the resolution
+ * which has an active_h of 4096 into a DTD. However, DTD has only
+ * 12 bit to represent active_h which would limit the maximum value
+ * to 4095. If such a case is detected, set the active_h explicitly
+ * to 4096.
+ */
+ if (active_h == 0xFFF) {
+ pr_debug("overriding h_active to 4096\n");
+ active_h++;
+ }
/*
* EDID_TIMING_DESC_H_BLANK[0x3]: Relative Offset to the EDID detailed
@@ -2620,6 +2640,24 @@ void hdmi_edid_config_override(void *input, bool enable,
}
}
+void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz)
+{
+ struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+
+ edid_ctrl->init_data.max_pclk_khz = max_pclk_khz;
+}
+
+bool hdmi_edid_is_audio_supported(void *input)
+{
+ struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+
+ /*
+ * return true if basic audio is supported or if an audio
+ * data block was successfully parsed.
+ */
+ return (edid_ctrl->basic_audio_supp || edid_ctrl->adb_size);
+}
+
void hdmi_edid_deinit(void *input)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
index 16efb6ee4014..557e9326a81d 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
@@ -79,5 +79,7 @@ void hdmi_edid_get_hdr_data(void *edid_ctrl,
struct hdmi_edid_hdr_data **hdr_data);
void hdmi_edid_config_override(void *input, bool enable,
struct hdmi_edid_override_data *data);
+void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz);
+bool hdmi_edid_is_audio_supported(void *input);
#endif /* __HDMI_EDID_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index f05d4cb2922a..42845f9ff192 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -380,6 +380,13 @@ static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl)
return hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID));
} /* hdmi_tx_is_dvi_mode */
+static inline u32 hdmi_tx_is_in_splash(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ return mdata->handoff_pending;
+}
+
static inline bool hdmi_tx_is_panel_on(struct hdmi_tx_ctrl *hdmi_ctrl)
{
return hdmi_ctrl->hpd_state && hdmi_ctrl->panel_power_on;
@@ -416,15 +423,27 @@ static inline void hdmi_tx_cec_device_suspend(struct hdmi_tx_ctrl *hdmi_ctrl)
}
static inline void hdmi_tx_send_cable_notification(
- struct hdmi_tx_ctrl *hdmi_ctrl, int val)
+ struct hdmi_tx_ctrl *hdmi_ctrl, int val, bool async)
{
if (hdmi_ctrl && hdmi_ctrl->ext_audio_data.intf_ops.hpd) {
u32 flags = 0;
- flags |= MSM_EXT_DISP_HPD_VIDEO;
+ if (async || hdmi_tx_is_in_splash(hdmi_ctrl)) {
+ flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO;
- if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
- flags |= MSM_EXT_DISP_HPD_AUDIO;
+ if (async) {
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
+ flags |= MSM_EXT_DISP_HPD_ASYNC_AUDIO;
+ } else
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
+ flags |= MSM_EXT_DISP_HPD_AUDIO;
+
+ } else {
+ flags |= MSM_EXT_DISP_HPD_VIDEO;
+
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
+ flags |= MSM_EXT_DISP_HPD_AUDIO;
+ }
hdmi_ctrl->ext_audio_data.intf_ops.hpd(hdmi_ctrl->ext_pdev,
hdmi_ctrl->ext_audio_data.type, val, flags);
@@ -859,7 +878,11 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev,
hdmi_tx_config_5v(hdmi_ctrl, false);
} else {
hdmi_tx_hpd_off(hdmi_ctrl);
- hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
+ /*
+ * No need to blocking wait for display/audio in this
+ * case since HAL is not up so no ACK can be expected.
+ */
+ hdmi_tx_send_cable_notification(hdmi_ctrl, 0, true);
}
break;
@@ -2339,7 +2362,7 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work)
mutex_unlock(&hdmi_ctrl->tx_lock);
- hdmi_tx_send_cable_notification(hdmi_ctrl, hdmi_ctrl->hpd_state);
+ hdmi_tx_send_cable_notification(hdmi_ctrl, hdmi_ctrl->hpd_state, false);
} /* hdmi_tx_hpd_int_work */
static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl)
@@ -3956,7 +3979,7 @@ static int hdmi_tx_post_evt_handle_resume(struct hdmi_tx_ctrl *hdmi_ctrl)
&hdmi_ctrl->hpd_int_done, HZ/10);
if (!timeout) {
pr_debug("cable removed during suspend\n");
- hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
+ hdmi_tx_send_cable_notification(hdmi_ctrl, 0, false);
}
}
@@ -3967,7 +3990,7 @@ static int hdmi_tx_post_evt_handle_panel_on(struct hdmi_tx_ctrl *hdmi_ctrl)
{
if (hdmi_ctrl->panel_suspend) {
pr_debug("panel suspend has triggered\n");
- hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
+ hdmi_tx_send_cable_notification(hdmi_ctrl, 0, false);
}
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 3cadfa4ecef7..171f44815430 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -2179,6 +2179,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED);
break;
case MDSS_MDP_HW_REV_320:
+ mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
+ mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
case MDSS_MDP_HW_REV_330:
mdata->max_target_zorder = 7; /* excluding base layer */
mdata->max_cursor_size = 512;
@@ -2206,6 +2208,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_TS_PREFILL, mdata->mdss_qos_map);
set_bit(MDSS_QOS_IB_NOCR, mdata->mdss_qos_map);
set_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, mdata->mdss_qos_map);
+ set_bit(MDSS_QOS_WB_QOS, mdata->mdss_qos_map);
+ set_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_YUV_CONFIG, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED,
mdata->mdss_caps_map);
@@ -2216,8 +2220,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED,
mdata->mdss_caps_map);
mdss_mdp_init_default_prefill_factors(mdata);
- mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
- mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
mdss_set_quirk(mdata, MDSS_QUIRK_MMSS_GDSC_COLLAPSE);
mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE);
mdss_set_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR);
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 36a866685f21..2fd047edd3e8 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -164,6 +164,14 @@ enum mdss_mdp_mixer_mux {
MDSS_MDP_MIXER_MUX_RIGHT,
};
+enum mdss_secure_transition {
+ SECURE_TRANSITION_NONE,
+ SD_NON_SECURE_TO_SECURE,
+ SD_SECURE_TO_NON_SECURE,
+ SC_NON_SECURE_TO_SECURE,
+ SC_SECURE_TO_NON_SECURE,
+};
+
static inline enum mdss_mdp_sspp_index get_pipe_num_from_ndx(u32 ndx)
{
u32 id;
@@ -421,6 +429,9 @@ struct mdss_mdp_ctl_intfs_ops {
/* to update lineptr, [1..yres] - enable, 0 - disable */
int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable);
int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *, bool enable);
+
+ /* to wait for vsync */
+ int (*wait_for_vsync_fnc)(struct mdss_mdp_ctl *ctl);
};
struct mdss_mdp_cwb {
@@ -650,6 +661,7 @@ struct mdss_mdp_img_data {
struct dma_buf *srcp_dma_buf;
struct dma_buf_attachment *srcp_attachment;
struct sg_table *srcp_table;
+ struct ion_handle *ihandle;
};
enum mdss_mdp_data_state {
@@ -691,6 +703,8 @@ struct pp_hist_col_info {
char __iomem *base;
u32 intr_shift;
u32 disp_num;
+ u32 expect_sum;
+ u32 next_sum;
struct mdss_mdp_ctl *ctl;
};
@@ -953,6 +967,8 @@ struct mdss_overlay_private {
struct kthread_worker worker;
struct kthread_work vsync_work;
struct task_struct *thread;
+
+ u8 secure_transition_state;
};
struct mdss_mdp_set_ot_params {
@@ -1976,6 +1992,7 @@ void mdss_mdp_disable_hw_irq(struct mdss_data_type *mdata);
void mdss_mdp_set_supported_formats(struct mdss_data_type *mdata);
int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer);
+void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num);
#ifdef CONFIG_FB_MSM_MDP_NONE
struct mdss_data_type *mdss_mdp_get_mdata(void)
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 7b0207de101a..49348e5e16a9 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -73,8 +73,18 @@ static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer,
static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
{
- u64 result = (val * (u64)numer);
- do_div(result, denom);
+ u64 result = val;
+
+ if (val) {
+ u64 temp = -1UL;
+
+ do_div(temp, val);
+ if (temp > numer) {
+ /* no overflow, so we can do the operation*/
+ result = (val * (u64)numer);
+ do_div(result, denom);
+ }
+ }
return result;
}
@@ -2263,8 +2273,8 @@ static bool __mdss_mdp_compare_bw(
__calc_bus_ib_quota(mdata, new_perf, is_nrt, new_perf->bw_ctl);
u64 old_ib =
__calc_bus_ib_quota(mdata, old_perf, is_nrt, old_perf->bw_ctl);
- u64 max_new_bw = max(new_perf->bw_ctl, new_ib);
- u64 max_old_bw = max(old_perf->bw_ctl, old_ib);
+ u64 new_ab = new_perf->bw_ctl;
+ u64 old_ab = old_perf->bw_ctl;
bool update_bw = false;
/*
@@ -2276,16 +2286,18 @@ static bool __mdss_mdp_compare_bw(
* 3. end of writeback/rotator session - last chance to
* non-realtime remove vote.
*/
- if ((params_changed && ((max_new_bw > max_old_bw) || /* ab and ib bw */
+ if ((params_changed &&
+ (((new_ib > old_ib) || (new_ab > old_ab)) ||
(new_perf->bw_writeback > old_perf->bw_writeback))) ||
- (!params_changed && ((max_new_bw < max_old_bw) ||
+ (!params_changed &&
+ (((new_ib < old_ib) || (new_ab < old_ab)) ||
(new_perf->bw_writeback < old_perf->bw_writeback))) ||
(stop_req && is_nrt))
update_bw = true;
trace_mdp_compare_bw(new_perf->bw_ctl, new_ib, new_perf->bw_writeback,
- max_new_bw, old_perf->bw_ctl, old_ib, old_perf->bw_writeback,
- max_old_bw, params_changed, update_bw);
+ old_perf->bw_ctl, old_ib, old_perf->bw_writeback,
+ params_changed, update_bw);
return update_bw;
}
@@ -4484,11 +4496,15 @@ end:
*/
static void mdss_mdp_pipe_reset(struct mdss_mdp_mixer *mixer, bool is_recovery)
{
- unsigned long pipe_map = mixer->pipe_mapped;
+ unsigned long pipe_map;
u32 bit = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
bool sw_rst_avail = mdss_mdp_pipe_is_sw_reset_available(mdata);
+ if (!mixer)
+ return;
+
+ pipe_map = mixer->pipe_mapped;
pr_debug("pipe_map=0x%lx\n", pipe_map);
for_each_set_bit_from(bit, &pipe_map, MAX_PIPES_PER_LM) {
struct mdss_mdp_pipe *pipe;
@@ -5704,6 +5720,15 @@ static void mdss_mdp_force_border_color(struct mdss_mdp_ctl *ctl)
ctl->mixer_right->params_changed++;
}
+static bool mdss_mdp_handle_backlight_extn(struct mdss_mdp_ctl *ctl)
+{
+ if (ctl->intf_type == MDSS_INTF_DSI && !ctl->is_video_mode &&
+ ctl->mfd->bl_extn_level >= 0)
+ return true;
+ else
+ return false;
+}
+
int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
struct mdss_mdp_commit_cb *commit_cb)
{
@@ -5872,6 +5897,15 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
if (ctl->ops.wait_pingpong && !mdata->serialize_wait4pp)
mdss_mdp_display_wait4pingpong(ctl, false);
+ /*
+ * If backlight needs to change, wait for 1 vsync before setting
+ * PCC and kickoff
+ */
+ if (mdss_mdp_handle_backlight_extn(ctl) &&
+ ctl->ops.wait_for_vsync_fnc) {
+ ret = ctl->ops.wait_for_vsync_fnc(ctl);
+ }
+
/* Moved pp programming to post ping pong */
if (!ctl->is_video_mode && ctl->mfd &&
ctl->mfd->dcm_state != DTM_ENTER) {
@@ -6020,6 +6054,18 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
if (ret)
pr_warn("ctl %d error displaying frame\n", ctl->num);
+ /* update backlight in commit */
+ if (mdss_mdp_handle_backlight_extn(ctl)) {
+ if (!IS_CALIB_MODE_BL(ctl->mfd) && (!ctl->mfd->ext_bl_ctrl ||
+ !ctl->mfd->bl_level)) {
+ mutex_lock(&ctl->mfd->bl_lock);
+ mdss_fb_set_backlight(ctl->mfd,
+ ctl->mfd->bl_extn_level);
+ ctl->mfd->bl_level_usr = ctl->mfd->bl_extn_level;
+ mutex_unlock(&ctl->mfd->bl_lock);
+ }
+ }
+
ctl->play_cnt++;
ATRACE_END("flush_kickoff");
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 294e05c2fbb0..7d495232c198 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -634,15 +634,6 @@ enum mdss_mdp_dspp_index {
#define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_SAT_MASK BIT(30)
#define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_VAL_MASK BIT(31)
-enum mdss_mpd_intf_index {
- MDSS_MDP_NO_INTF,
- MDSS_MDP_INTF0,
- MDSS_MDP_INTF1,
- MDSS_MDP_INTF2,
- MDSS_MDP_INTF3,
- MDSS_MDP_MAX_INTF
-};
-
#define MDSS_MDP_REG_INTF_TIMING_ENGINE_EN 0x000
#define MDSS_MDP_REG_INTF_CONFIG 0x004
#define MDSS_MDP_REG_INTF_HSYNC_CTL 0x008
@@ -703,6 +694,8 @@ enum mdss_mpd_intf_index {
#define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F
#define MDSS_MDP_PANEL_FORMAT_RGB666 0x212A
+#define MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE 0x064
+
#define MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB BIT(7)
enum mdss_mdp_pingpong_index {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index dfd6226ce602..c9ce56fb96a4 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -103,6 +103,7 @@ struct mdss_mdp_cmd_ctx {
struct mdss_intf_recovery intf_recovery;
struct mdss_intf_recovery intf_mdp_callback;
+ struct mdss_intf_ulp_clamp intf_clamp_handler;
struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
u32 pp_timeout_report_cnt;
bool pingpong_split_slave;
@@ -1197,7 +1198,7 @@ static int mdss_mdp_cmd_wait4readptr(struct mdss_mdp_cmd_ctx *ctx)
return rc;
}
-static void mdss_mdp_cmd_intf_callback(void *data, int event)
+static int mdss_mdp_cmd_intf_callback(void *data, int event)
{
struct mdss_mdp_cmd_ctx *ctx = data;
struct mdss_mdp_pp_tear_check *te = NULL;
@@ -1206,11 +1207,11 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
if (!data) {
pr_err("%s: invalid ctx\n", __func__);
- return;
+ return -EINVAL;
}
if (!ctx->ctl)
- return;
+ return -EINVAL;
switch (event) {
case MDP_INTF_CALLBACK_DSI_WAIT:
@@ -1222,7 +1223,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
* just return
*/
if (ctx->intf_stopped || !is_pingpong_split(ctx->ctl->mfd))
- return;
+ return -EINVAL;
atomic_inc(&ctx->rdptr_cnt);
/* enable clks and rd_ptr interrupt */
@@ -1231,7 +1232,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
mixer = mdss_mdp_mixer_get(ctx->ctl, MDSS_MDP_MIXER_MUX_LEFT);
if (!mixer) {
pr_err("%s: null mixer\n", __func__);
- return;
+ return -EINVAL;
}
/* wait for read pointer */
@@ -1255,6 +1256,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
pr_debug("%s: unhandled event=%d\n", __func__, event);
break;
}
+ return 0;
}
static void mdss_mdp_cmd_lineptr_done(void *arg)
@@ -1280,7 +1282,33 @@ static void mdss_mdp_cmd_lineptr_done(void *arg)
spin_unlock(&ctx->clk_lock);
}
-static void mdss_mdp_cmd_intf_recovery(void *data, int event)
+static int mdss_mdp_cmd_intf_clamp_ctrl(void *data, int intf_num, bool enable)
+{
+ struct mdss_mdp_ctl *ctl = data;
+ char __iomem *ctx_base = NULL;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx_base =
+ (char __iomem *)mdss_mdp_intf_get_ctx_base(ctl, intf_num);
+ if (!ctx_base) {
+ pr_err("%s: invalid ctx\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: intf num = %d, enable = %d\n",
+ __func__, intf_num, enable);
+
+ writel_relaxed(enable, (ctx_base + MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE));
+ wmb(); /* ensure clamp is enabled */
+
+ return 0;
+}
+
+static int mdss_mdp_cmd_intf_recovery(void *data, int event)
{
struct mdss_mdp_cmd_ctx *ctx = data;
unsigned long flags;
@@ -1288,11 +1316,11 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (!data) {
pr_err("%s: invalid ctx\n", __func__);
- return;
+ return -EINVAL;
}
if (!ctx->ctl)
- return;
+ return -EINVAL;
/*
* Currently, only intf_fifo_underflow is
@@ -1302,7 +1330,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (event != MDP_INTF_DSI_CMD_FIFO_UNDERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
- return;
+ return -EPERM;
}
if (atomic_read(&ctx->koff_cnt)) {
@@ -1326,6 +1354,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (notify_frame_timeout)
mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_TIMEOUT);
+ return 0;
}
static void mdss_mdp_cmd_pingpong_done(void *arg)
@@ -2917,6 +2946,41 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl,
}
}
+int mdss_mdp_cmd_wait4_vsync(struct mdss_mdp_ctl *ctl)
+{
+ int rc = 0;
+ struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
+
+ if (!ctx) {
+ pr_err("invalid context to wait for vsync\n");
+ return rc;
+ }
+
+ atomic_inc(&ctx->rdptr_cnt);
+
+ /* enable clks and rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, true);
+
+ /* wait for read pointer */
+ MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
+ pr_debug("%s: wait for vsync cnt:%d\n",
+ __func__, atomic_read(&ctx->rdptr_cnt));
+
+ rc = mdss_mdp_cmd_wait4readptr(ctx);
+
+ /* wait for 1ms to make sure we are out from trigger window */
+ usleep_range(1000, 1010);
+
+ /* disable rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, false);
+
+ MDSS_XLOG(ctl->num);
+ pr_debug("%s: out from wait for rd_ptr ctl:%d\n", __func__, ctl->num);
+
+ return rc;
+}
+
+
/*
* There are 3 partial update possibilities
* left only ==> enable left pingpong_done
@@ -3170,6 +3234,14 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
ctx->default_pp_num, NULL, NULL);
memset(ctx, 0, sizeof(*ctx));
+ /* intf stopped, no more kickoff */
+ ctx->intf_stopped = 1;
+ /*
+ * Restore clamp handler as it might get called later
+ * when DSI panel events are handled.
+ */
+ ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctl;
return 0;
}
@@ -3310,12 +3382,21 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
(void *)&ctx->intf_mdp_callback,
CTL_INTF_EVENT_FLAG_DEFAULT);
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
mdss_mdp_tearcheck_enable(ctl, true);
ctx->intf_stopped = 0;
if (sctx)
sctx->intf_stopped = 0;
-
+ /*
+ * Tearcheck was disabled while entering LP2 state.
+ * Enable it back to allow updates in LP1 state.
+ */
+ mdss_mdp_tearcheck_enable(ctl, true);
goto end;
}
}
@@ -3364,6 +3445,10 @@ panel_events:
goto end;
}
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+
pr_debug("%s: turn off panel\n", __func__);
ctl->intf_ctx[MASTER_CTX] = NULL;
ctl->intf_ctx[SLAVE_CTX] = NULL;
@@ -3373,6 +3458,7 @@ panel_events:
ctl->ops.add_vsync_handler = NULL;
ctl->ops.remove_vsync_handler = NULL;
ctl->ops.reconfigure = NULL;
+ ctl->ops.wait_for_vsync_fnc = NULL;
end:
if (!IS_ERR_VALUE(ret)) {
@@ -3496,6 +3582,14 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
ctx->intf_mdp_callback.fxn = mdss_mdp_cmd_intf_callback;
ctx->intf_mdp_callback.data = ctx;
+ ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctl;
+
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
ctx->intf_stopped = 0;
pr_debug("%s: ctx=%pK num=%d aux=%d\n", __func__, ctx,
@@ -3773,6 +3867,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming;
ctl->ops.update_lineptr = mdss_mdp_cmd_update_lineptr;
ctl->ops.panel_disable_cfg = mdss_mdp_cmd_panel_disable_cfg;
+ ctl->ops.wait_for_vsync_fnc = mdss_mdp_cmd_wait4_vsync;
pr_debug("%s:-\n", __func__);
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 5173567a3420..a3511a1a07ef 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -83,6 +83,8 @@ struct mdss_mdp_video_ctx {
struct mutex vsync_mtx;
struct list_head vsync_handlers;
struct mdss_intf_recovery intf_recovery;
+ struct mdss_intf_recovery intf_mdp_callback;
+ struct mdss_intf_ulp_clamp intf_clamp_handler;
struct work_struct early_wakeup_dfps_work;
atomic_t lineptr_ref;
@@ -149,6 +151,23 @@ static int mdss_mdp_intf_intr2index(u32 intr_type)
return index;
}
+void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num)
+{
+ struct mdss_mdp_video_ctx *head;
+ int i = 0;
+
+ if (!ctl)
+ return NULL;
+
+ head = ctl->mdata->video_intf;
+ for (i = 0; i < ctl->mdata->nintf; i++) {
+ if (head[i].intf_num == intf_num)
+ return (void *)head[i].base;
+ }
+
+ return NULL;
+}
+
int mdss_mdp_set_intf_intr_callback(struct mdss_mdp_video_ctx *ctx,
u32 intr_type, void (*fnc_ptr)(void *), void *arg)
{
@@ -315,7 +334,30 @@ int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata,
return 0;
}
-static void mdss_mdp_video_intf_recovery(void *data, int event)
+static int mdss_mdp_video_intf_clamp_ctrl(void *data, int intf_num, bool enable)
+{
+ struct mdss_mdp_video_ctx *ctx = data;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ if (intf_num != ctx->intf_num) {
+ pr_err("%s: invalid intf num\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: ctx intf num = %d, enable = %d\n",
+ __func__, ctx->intf_num, enable);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE, enable);
+ wmb(); /* ensure clamp is enabled */
+
+ return 0;
+}
+
+static int mdss_mdp_video_intf_recovery(void *data, int event)
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_ctl *ctl = data;
@@ -327,7 +369,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
if (!data) {
pr_err("%s: invalid ctl\n", __func__);
- return;
+ return -EINVAL;
}
/*
@@ -338,7 +380,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
if (event != MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
- return;
+ return -EPERM;
}
ctx = ctl->intf_ctx[MASTER_CTX];
@@ -353,7 +395,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */
if (!clk_rate) {
pr_err("Unable to get proper clk_rate\n");
- return;
+ return -EINVAL;
}
/*
* calculate clk_period as pico second to maintain good
@@ -363,7 +405,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate);
if (!clk_period) {
pr_err("Unable to calculate clock period\n");
- return;
+ return -EINVAL;
}
min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
active_lns_cnt = pinfo->yres;
@@ -389,7 +431,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
!ctx->timegen_en) {
pr_warn("Target is in suspend or shutdown pending\n");
mutex_unlock(&ctl->offlock);
- return;
+ return -EPERM;
}
line_cnt = mdss_mdp_video_line_count(ctl);
@@ -399,7 +441,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
pr_debug("%s, Needed lines left line_cnt=%d\n",
__func__, line_cnt);
mutex_unlock(&ctl->offlock);
- return;
+ return 0;
} else {
pr_warn("line count is less. line_cnt = %d\n",
line_cnt);
@@ -449,7 +491,7 @@ static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl,
if (sctl)
sctx = (struct mdss_mdp_video_ctx *)
sctl->intf_ctx[MASTER_CTX];
- mdss_mdp_video_timegen_flush(ctl, ctx);
+ mdss_mdp_video_timegen_flush(ctl, sctx);
MDSS_XLOG(pinfo->min_fps, pinfo->default_fps, avr_vtotal);
}
@@ -1416,6 +1458,8 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps)
}
}
+ /* add HW recommended delay to handle panel_vsync */
+ udelay(2000);
mutex_lock(&ctl->offlock);
pdata = ctl->panel_data;
if (pdata == NULL) {
@@ -1545,7 +1589,8 @@ exit_dfps:
if (rc < 0)
pr_err("Error in dfps_wait: %d\n", rc);
}
-
+ /* add HW recommended delay to handle panel_vsync */
+ udelay(2000);
/* Disable interface timing double buffer */
rc = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_DSI_TIMING_DB_CTRL,
@@ -1665,13 +1710,43 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
return 0;
}
+static int mdss_mdp_video_splash_handoff(struct mdss_mdp_ctl *ctl)
+{
+ int i, ret = 0;
+ u32 data, flush;
+
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ if (ret) {
+ pr_err("%s:ctl%d failed to handle 'CONT_SPLASH_BEGIN' event\n"
+ , __func__, ctl->num);
+ return ret;
+ }
+
+ /* clear up mixer0 and mixer1 */
+ flush = 0;
+ for (i = 0; i < 2; i++) {
+ data = mdss_mdp_ctl_read(ctl,
+ MDSS_MDP_REG_CTL_LAYER(i));
+ if (data) {
+ mdss_mdp_ctl_write(ctl,
+ MDSS_MDP_REG_CTL_LAYER(i),
+ MDSS_MDP_LM_BORDER_COLOR);
+ flush |= (0x40 << i);
+ }
+ }
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush);
+
+ return ret;
+}
+
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
bool handoff)
{
struct mdss_panel_data *pdata;
- int i, ret = 0, off;
- u32 data, flush;
- struct mdss_mdp_video_ctx *ctx;
+ int ret = 0, off;
+ struct mdss_mdp_video_ctx *ctx, *sctx = NULL;
struct mdss_mdp_ctl *sctl;
if (!ctl) {
@@ -1695,41 +1770,43 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
pdata->panel_info.cont_splash_enabled = 0;
sctl = mdss_mdp_get_split_ctl(ctl);
- if (sctl)
+ if (sctl) {
sctl->panel_data->panel_info.cont_splash_enabled = 0;
- else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd))
+ sctx = (struct mdss_mdp_video_ctx *) sctl->intf_ctx[MASTER_CTX];
+ } else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) {
ctl->panel_data->next->panel_info.cont_splash_enabled = 0;
+ sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX];
+ }
if (!handoff) {
- ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
- NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
- if (ret) {
- pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n"
- , __func__);
- return ret;
- }
+ ret = mdss_mdp_video_splash_handoff(ctl);
- /* clear up mixer0 and mixer1 */
- flush = 0;
- for (i = 0; i < 2; i++) {
- data = mdss_mdp_ctl_read(ctl,
- MDSS_MDP_REG_CTL_LAYER(i));
- if (data) {
- mdss_mdp_ctl_write(ctl,
- MDSS_MDP_REG_CTL_LAYER(i),
- MDSS_MDP_LM_BORDER_COLOR);
- flush |= (0x40 << i);
- }
- }
- mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush);
+ if (!ret && sctl)
+ ret = mdss_mdp_video_splash_handoff(sctl);
+
+ if (ret)
+ return ret;
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+
+ if (sctx)
+ mdp_video_write(sctx,
+ MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+
+ mdss_mdp_video_timegen_flush(ctl, sctx);
+
/* wait for 1 VSYNC for the pipe to be unstaged */
msleep(20);
ret = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ if (!ret && sctl)
+ ret = mdss_mdp_ctl_intf_event(sctl,
+ MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
}
return ret;
@@ -1782,7 +1859,7 @@ static void mdss_mdp_fetch_end_config(struct mdss_mdp_video_ctx *ctx,
static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
struct mdss_mdp_ctl *ctl)
{
- int fetch_start, fetch_enable, v_total, h_total;
+ int fetch_start = 0, fetch_enable, v_total, h_total;
struct mdss_data_type *mdata;
struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
@@ -1791,6 +1868,15 @@ static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
pinfo->prg_fet = mdss_mdp_get_prefetch_lines(pinfo);
if (!pinfo->prg_fet) {
pr_debug("programmable fetch is not needed/supported\n");
+
+ fetch_enable = mdp_video_read(ctx, MDSS_MDP_REG_INTF_CONFIG);
+ fetch_enable &= ~BIT(31);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_CONFIG, fetch_enable);
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_PROG_FETCH_START,
+ fetch_start);
+
+ MDSS_XLOG(ctx->intf_num, fetch_enable, fetch_start);
return;
}
@@ -1888,6 +1974,58 @@ static void mdss_mdp_handoff_programmable_fetch(struct mdss_mdp_ctl *ctl,
}
}
+static int mdss_mdp_video_intf_callback(void *data, int event)
+{
+ struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *ctl = data;
+ struct mdss_panel_info *pinfo;
+ u32 line_cnt, min_ln_cnt, active_lns_cnt, line_buff = 50;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx = ctl->intf_ctx[MASTER_CTX];
+ pr_debug("%s: ctl num = %d, event = %d\n",
+ __func__, ctl->num, event);
+
+ if (!ctl->is_video_mode)
+ return 0;
+
+ pinfo = &ctl->panel_data->panel_info;
+ min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
+ active_lns_cnt = pinfo->yres;
+
+ switch (event) {
+ case MDP_INTF_CALLBACK_CHECK_LINE_COUNT:
+ if (!ctl || !ctx || !ctx->timegen_en) {
+ pr_debug("%s: no need to check for active line\n",
+ __func__);
+ goto end;
+ }
+
+ line_cnt = mdss_mdp_video_line_count(ctl);
+
+ if ((line_cnt >= min_ln_cnt) && (line_cnt <
+ (min_ln_cnt + active_lns_cnt - line_buff))) {
+ pr_debug("%s: line count is within active range=%d\n",
+ __func__, line_cnt);
+ goto end;
+ } else {
+ pr_debug("line count is less. line_cnt = %d\n",
+ line_cnt);
+ return -EPERM;
+ }
+ break;
+ default:
+ pr_debug("%s: unhandled event!\n", __func__);
+ break;
+ }
+end:
+ return 0;
+}
+
static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_video_ctx *ctx, struct mdss_panel_info *pinfo)
{
@@ -1921,6 +2059,20 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
pr_err("Failed to register intf recovery handler\n");
return -EINVAL;
}
+
+ ctx->intf_mdp_callback.fxn = mdss_mdp_video_intf_callback;
+ ctx->intf_mdp_callback.data = ctl;
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_MDP_CALLBACK,
+ (void *)&ctx->intf_mdp_callback,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ ctx->intf_clamp_handler.fxn = mdss_mdp_video_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctx;
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
} else {
ctx->intf_recovery.fxn = NULL;
ctx->intf_recovery.data = NULL;
@@ -2340,6 +2492,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
ctl->ops.early_wake_up_fnc = mdss_mdp_video_early_wake_up;
ctl->ops.update_lineptr = mdss_mdp_video_lineptr_ctrl;
ctl->ops.avr_ctrl_fnc = mdss_mdp_video_avr_ctrl;
+ ctl->ops.wait_for_vsync_fnc = NULL;
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
index 5b284e624c7f..87ed56028edd 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
@@ -602,9 +602,14 @@ int mdss_mdp_writeback_prepare_cwb(struct mdss_mdp_ctl *ctl,
mdss_mdp_irq_enable(MDSS_MDP_IRQ_TYPE_CWB_OVERFLOW, CWB_PPB_1);
}
- if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map))
+ if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map)) {
+ u32 reg = 0;
+
+ reg = MDSS_VBIF_READ(ctl->mdata,
+ MDSS_VBIF_WRITE_GATHER_EN, false);
MDSS_VBIF_WRITE(ctl->mdata, MDSS_VBIF_WRITE_GATHER_EN,
- BIT(6), false);
+ reg | BIT(6), false);
+ }
if (ctl->mdata->default_ot_wr_limit || ctl->mdata->default_ot_rd_limit)
mdss_mdp_set_ot_limit_wb(ctx, false);
@@ -1030,9 +1035,14 @@ static int mdss_mdp_writeback_display(struct mdss_mdp_ctl *ctl, void *arg)
return ret;
}
- if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map))
+ if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map)) {
+ u32 reg = 0;
+
+ reg = MDSS_VBIF_READ(ctl->mdata,
+ MDSS_VBIF_WRITE_GATHER_EN, false);
MDSS_VBIF_WRITE(ctl->mdata, MDSS_VBIF_WRITE_GATHER_EN,
- BIT(6), false);
+ reg | BIT(6), false);
+ }
mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
mdss_mdp_writeback_intr_done, ctl);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index c9e32d69d444..09a34223c2a5 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -1123,6 +1123,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
int ret = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u64 flags;
+ bool is_right_blend = false;
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -1221,6 +1222,15 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
goto end;
}
+ /* scaling is not allowed for solid_fill layers */
+ if ((pipe->flags & MDP_SOLID_FILL) &&
+ ((pipe->src.w != pipe->dst.w) ||
+ (pipe->src.h != pipe->dst.h))) {
+ pr_err("solid fill pipe:%d cannot have scaling\n", pipe->num);
+ ret = -EINVAL;
+ goto end;
+ }
+
/*
* unstage the pipe if it's current z_order does not match with new
* z_order because client may only call the validate.
@@ -1234,6 +1244,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
* staging, same pipe will be stagged on both layer mixers.
*/
if (mdata->has_src_split) {
+ is_right_blend = pipe->is_right_blend;
if (left_blend_pipe) {
if (__validate_pipe_priorities(left_blend_pipe, pipe)) {
pr_err("priority limitation. left:%d rect:%d, right:%d rect:%d\n",
@@ -1245,7 +1256,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
goto end;
} else {
pr_debug("pipe%d is a right_pipe\n", pipe->num);
- pipe->is_right_blend = true;
+ is_right_blend = true;
}
} else if (pipe->is_right_blend) {
/*
@@ -1254,7 +1265,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
*/
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
- pipe->is_right_blend = false;
+ is_right_blend = false;
}
if (is_split_lm(mfd) && __layer_needs_src_split(layer)) {
@@ -1280,6 +1291,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
}
pipe->src_split_req = false;
}
+ pipe->is_right_blend = is_right_blend;
}
pipe->multirect.mode = vinfo->multirect.mode;
@@ -2261,6 +2273,78 @@ static int __validate_multirect(struct msm_fb_data_type *mfd,
return 0;
}
+static int __check_source_split(struct mdp_input_layer *layer_list,
+ struct mdss_mdp_pipe **pipe_list, u32 index,
+ u32 left_lm_w, struct mdss_mdp_pipe **left_blend_pipe)
+{
+ int i = index - 1;
+ struct mdp_input_layer *curr, *prev;
+ struct mdp_rect *left, *right;
+ bool match = false;
+ struct mdss_mdp_pipe *left_pipe = NULL;
+
+ /*
+ * check if current layer is at same z_order as any of the
+ * previous layers, and fail if any or both are async layers,
+ * as async layers should have unique z_order.
+ *
+ * If it has same z_order and qualifies as a right blend,
+ * pass a pointer to the pipe representing previous overlay or
+ * in other terms left blend layer.
+ *
+ * Following logic of selecting left_blend has an inherent
+ * assumption that layer list is sorted on dst_x within a
+ * same z_order. Otherwise it will fail based on z_order checks.
+ */
+ curr = &layer_list[index];
+
+ while (i >= 0) {
+ if (layer_list[i].z_order == curr->z_order) {
+ pr_debug("z=%d found match @ %d of %d\n",
+ curr->z_order, i, index);
+ match = true;
+ break;
+ }
+ i--;
+ }
+
+ if (match) {
+ left_pipe = pipe_list[i];
+ prev = &layer_list[i];
+ left = &prev->dst_rect;
+ right = &curr->dst_rect;
+
+ if ((curr->flags & MDP_LAYER_ASYNC)
+ || (prev->flags & MDP_LAYER_ASYNC)) {
+ curr->error_code = -EINVAL;
+ pr_err("async curr should have unique z_order\n");
+ return curr->error_code;
+ }
+
+ /*
+ * check if curr is right blend by checking it's
+ * directly to the right.
+ */
+ if (((left->x + left->w) == right->x) &&
+ (left->y == right->y) && (left->h == right->h)) {
+ *left_blend_pipe = left_pipe;
+ MDSS_XLOG(curr->z_order, i, index);
+ }
+
+ /*
+ * if the curr is right at the left lm boundary and
+ * src split is not required then right blend is not
+ * required as it will lie only on the left mixer
+ */
+ if (!__layer_needs_src_split(prev) &&
+ ((left->x + left->w) == left_lm_w))
+ *left_blend_pipe = NULL;
+ }
+
+ return 0;
+}
+
+
/*
* __validate_layers() - validate input layers
* @mfd: Framebuffer data structure for display
@@ -2291,13 +2375,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_mdp_mixer *mixer = NULL;
- struct mdp_input_layer *layer, *prev_layer, *layer_list;
+ struct mdp_input_layer *layer, *layer_list;
struct mdss_mdp_validate_info_t *validate_info_list = NULL;
bool is_single_layer = false, force_validate;
enum layer_pipe_q pipe_q_type;
enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0};
enum mdss_mdp_pipe_rect rect_num;
struct mdp_destination_scaler_data *ds_data;
+ struct mdss_mdp_pipe *pipe_list[MAX_LAYER_COUNT] = {0};
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
if (ret)
@@ -2369,49 +2454,10 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
dst_x = layer->dst_rect.x;
left_blend_pipe = NULL;
- prev_layer = (i > 0) ? &layer_list[i - 1] : NULL;
- /*
- * check if current layer is at same z_order as
- * previous one, and fail if any or both are async layers,
- * as async layers should have unique z_order.
- *
- * If it has same z_order and qualifies as a right blend,
- * pass a pointer to the pipe representing previous overlay or
- * in other terms left blend layer.
- *
- * Following logic of selecting left_blend has an inherent
- * assumption that layer list is sorted on dst_x within a
- * same z_order. Otherwise it will fail based on z_order checks.
- */
- if (prev_layer && (prev_layer->z_order == layer->z_order)) {
- struct mdp_rect *left = &prev_layer->dst_rect;
- struct mdp_rect *right = &layer->dst_rect;
-
- if ((layer->flags & MDP_LAYER_ASYNC)
- || (prev_layer->flags & MDP_LAYER_ASYNC)) {
- ret = -EINVAL;
- layer->error_code = ret;
- pr_err("async layer should have unique z_order\n");
- goto validate_exit;
- }
-
- /*
- * check if layer is right blend by checking it's
- * directly to the right.
- */
- if (((left->x + left->w) == right->x) &&
- (left->y == right->y) && (left->h == right->h))
- left_blend_pipe = pipe;
-
- /*
- * if the layer is right at the left lm boundary and
- * src split is not required then right blend is not
- * required as it will lie only on the left mixer
- */
- if (!__layer_needs_src_split(prev_layer) &&
- ((left->x + left->w) == left_lm_w))
- left_blend_pipe = NULL;
- }
+ if ((i > 0) &&
+ __check_source_split(layer_list, pipe_list, i, left_lm_w,
+ &left_blend_pipe))
+ goto validate_exit;
if (!is_split_lm(mfd) || __layer_needs_src_split(layer))
z = LAYER_ZORDER_BOTH;
@@ -2456,6 +2502,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
else
left_plist[left_cnt++] = pipe;
+ pipe_list[i] = pipe;
+
if (layer->flags & MDP_LAYER_PP) {
memcpy(&pipe->pp_cfg, layer->pp_info,
sizeof(struct mdp_overlay_pp_params));
@@ -2548,15 +2596,18 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
else
left_plist[left_cnt++] = pipe;
+ pipe_list[i] = pipe;
+
pr_debug("id:0x%x flags:0x%x dst_x:%d\n",
layer->pipe_ndx, layer->flags, layer->dst_rect.x);
layer->z_order -= MDSS_MDP_STAGE_0;
}
ds_data = commit->dest_scaler;
- if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) &&
- ds_data && (ds_data->flags & MDP_DESTSCALER_ENABLE) &&
- commit->dest_scaler_cnt) {
+
+ if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)
+ && ds_data && commit->dest_scaler_cnt
+ && (ds_data->flags & MDP_DESTSCALER_ENABLE)) {
/*
* Find out which DS block to use based on DS commit info
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index 5d80c80ebcef..8eb12d764be3 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -48,6 +48,12 @@
#define BUF_POOL_SIZE 32
+#define DFPS_DATA_MAX_HFP 8192
+#define DFPS_DATA_MAX_HBP 8192
+#define DFPS_DATA_MAX_HPW 8192
+#define DFPS_DATA_MAX_FPS 0x7fffffff
+#define DFPS_DATA_MAX_CLK_RATE 250000
+
static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd);
@@ -520,6 +526,99 @@ static int __mdss_mdp_validate_pxl_extn(struct mdss_mdp_pipe *pipe)
return 0;
}
+static int __mdss_mdp_validate_qseed3_cfg(struct mdss_mdp_pipe *pipe)
+{
+ int plane;
+
+ for (plane = 0; plane < MAX_PLANES; plane++) {
+ u32 hor_req_pixels, hor_fetch_pixels;
+ u32 vert_req_pixels, vert_fetch_pixels;
+ u32 src_w = pipe->src.w;
+ u32 src_h = pipe->src.h;
+
+ /*
+ * plane 1 and 2 are for chroma and are same. While configuring
+ * HW, programming only one of the chroma components is
+ * sufficient.
+ */
+ if (plane == 2)
+ continue;
+
+ /*
+ * For chroma plane, width is half for the following sub sampled
+ * formats. Except in case of decimation, where hardware avoids
+ * 1 line of decimation instead of downsampling.
+ */
+ if (plane == 1 && !pipe->horz_deci &&
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1)))
+ src_w >>= 1;
+
+ if (plane == 1 && !pipe->vert_deci &&
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2)))
+ src_h >>= 1;
+
+ hor_req_pixels = pipe->scaler.num_ext_pxls_left[plane];
+
+ /**
+ * libscaler provides the fetch values before decimation
+ * and the rpt values are always 0, since qseed3 block
+ * internally does the repeat.
+ */
+ hor_fetch_pixels = DECIMATED_DIMENSION(src_w +
+ (int8_t)(pipe->scaler.left_ftch[plane]
+ & 0xFF) +
+ (int8_t)(pipe->scaler.right_ftch[plane]
+ & 0xFF),
+ pipe->horz_deci);
+
+ vert_req_pixels = pipe->scaler.num_ext_pxls_top[plane];
+
+ vert_fetch_pixels = DECIMATED_DIMENSION(src_h +
+ (int8_t)(pipe->scaler.top_ftch[plane]
+ & 0xFF)+
+ (int8_t)(pipe->scaler.btm_ftch[plane]
+ & 0xFF),
+ pipe->vert_deci);
+
+ if ((hor_req_pixels != hor_fetch_pixels) ||
+ (hor_fetch_pixels > pipe->img_width) ||
+ (vert_req_pixels != vert_fetch_pixels) ||
+ (vert_fetch_pixels > pipe->img_height)) {
+ pr_err("err: plane=%d h_req:%d h_fetch:%d v_req:%d v_fetch:%d src_img[%d %d]\n",
+
+ plane,
+ hor_req_pixels, hor_fetch_pixels,
+ vert_req_pixels, vert_fetch_pixels,
+ pipe->img_width, pipe->img_height);
+ pipe->scaler.enable = 0;
+ return -EINVAL;
+ }
+ /*
+ * alpha plane can only be scaled using bilinear or pixel
+ * repeat/drop, src_width and src_height are only specified
+ * for Y and UV plane
+ */
+ if (plane != 3) {
+ if ((hor_req_pixels !=
+ pipe->scaler.src_width[plane]) ||
+ (vert_req_pixels !=
+ pipe->scaler.src_height[plane])) {
+ pr_err("roi_w[%d]=%d, scaler:[%d, %d], src_img:[%d, %d]\n",
+ plane, pipe->scaler.roi_w[plane],
+ pipe->scaler.src_width[plane],
+ pipe->scaler.src_height[plane],
+ pipe->img_width, pipe->img_height);
+ pipe->scaler.enable = 0;
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
{
u32 src;
@@ -528,8 +627,11 @@ int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
mdata = mdss_mdp_get_mdata();
if (pipe->scaler.enable) {
- if (!test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
+ if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
+ rc = __mdss_mdp_validate_qseed3_cfg(pipe);
+ else
rc = __mdss_mdp_validate_pxl_extn(pipe);
+
return rc;
}
@@ -2223,14 +2325,12 @@ set_roi:
}
/*
- * Enables/disable secure (display or camera) sessions
+ * Check if there is any change in secure state and store it.
*/
-static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
+static void __overlay_set_secure_transition_state(struct msm_fb_data_type *mfd)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_mdp_pipe *pipe;
- int ret = 0;
int sd_in_pipe = 0;
int sc_in_pipe = 0;
u64 pipes_flags = 0;
@@ -2251,25 +2351,53 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
MDSS_XLOG(sd_in_pipe, sc_in_pipe, pipes_flags,
mdp5_data->sc_enabled, mdp5_data->sd_enabled);
pr_debug("sd:%d sd_in_pipe:%d sc:%d sc_in_pipe:%d flags:0x%llx\n",
- mdp5_data->sd_enabled, sd_in_pipe,
- mdp5_data->sc_enabled, sc_in_pipe, pipes_flags);
+ mdp5_data->sd_enabled, sd_in_pipe,
+ mdp5_data->sc_enabled, sc_in_pipe, pipes_flags);
+
+ /* Reset the secure transition state */
+ mdp5_data->secure_transition_state = SECURE_TRANSITION_NONE;
/*
- * Return early in only two conditions:
- * 1. All the features are already disabled and state remains
- * disabled for the pipes.
- * 2. One of the features is already enabled and state remains
- * enabled for the pipes.
- */
+ * Secure transition would be NONE in two conditions:
+ * 1. All the features are already disabled and state remains
+ * disabled for the pipes.
+ * 2. One of the features is already enabled and state remains
+ * enabled for the pipes.
+ */
if (!sd_in_pipe && !mdp5_data->sd_enabled &&
!sc_in_pipe && !mdp5_data->sc_enabled)
- return ret;
+ return;
else if ((sd_in_pipe && mdp5_data->sd_enabled) ||
(sc_in_pipe && mdp5_data->sc_enabled))
+ return;
+
+ /* Secure Display */
+ if (!mdp5_data->sd_enabled && sd_in_pipe)
+ mdp5_data->secure_transition_state |= SD_NON_SECURE_TO_SECURE;
+ else if (mdp5_data->sd_enabled && !sd_in_pipe)
+ mdp5_data->secure_transition_state |= SD_SECURE_TO_NON_SECURE;
+
+ /* Secure Camera */
+ if (!mdp5_data->sc_enabled && sc_in_pipe)
+ mdp5_data->secure_transition_state |= SC_NON_SECURE_TO_SECURE;
+ else if (mdp5_data->sc_enabled && !sc_in_pipe)
+ mdp5_data->secure_transition_state |= SC_SECURE_TO_NON_SECURE;
+}
+
+/*
+ * Enable/disable secure (display or camera) sessions
+ */
+static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ int ret = 0;
+
+ if (mdp5_data->secure_transition_state == SECURE_TRANSITION_NONE)
return ret;
/* Secure Display */
- if (!mdp5_data->sd_enabled && sd_in_pipe) {
+ if (mdp5_data->secure_transition_state == SD_NON_SECURE_TO_SECURE) {
if (!mdss_get_sd_client_cnt()) {
MDSS_XLOG(0x11);
/*wait for ping pong done */
@@ -2291,7 +2419,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
mdp5_data->sd_enabled = 1;
mdss_update_sd_client(mdp5_data->mdata, true);
- } else if (mdp5_data->sd_enabled && !sd_in_pipe) {
+ } else if (mdp5_data->secure_transition_state ==
+ SD_SECURE_TO_NON_SECURE) {
/* disable the secure display on last client */
if (mdss_get_sd_client_cnt() == 1) {
MDSS_XLOG(0x22);
@@ -2309,11 +2438,9 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
/* Secure Camera */
- if (!mdp5_data->sc_enabled && sc_in_pipe) {
+ if (mdp5_data->secure_transition_state == SC_NON_SECURE_TO_SECURE) {
if (!mdss_get_sc_client_cnt()) {
MDSS_XLOG(0x33);
- if (ctl->ops.wait_pingpong)
- mdss_mdp_display_wait4pingpong(ctl, true);
ret = mdss_mdp_secure_session_ctrl(1,
MDP_SECURE_CAMERA_OVERLAY_SESSION);
if (ret) {
@@ -2323,7 +2450,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
mdp5_data->sc_enabled = 1;
mdss_update_sc_client(mdp5_data->mdata, true);
- } else if (mdp5_data->sc_enabled && !sc_in_pipe) {
+ } else if (mdp5_data->secure_transition_state ==
+ SC_SECURE_TO_NON_SECURE) {
/* disable the secure camera on last client */
if (mdss_get_sc_client_cnt() == 1) {
MDSS_XLOG(0x44);
@@ -2402,15 +2530,23 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
list_move(&pipe->list, &mdp5_data->pipes_destroy);
}
+ __overlay_set_secure_transition_state(mfd);
/*
* go to secure state if required, this should be done
* after moving the buffers from the previous commit to
- * destroy list
+ * destroy list.
+ * For video mode panels, secure display/camera should be disabled
+ * after flushing the new buffer. Skip secure disable here for those
+ * cases.
*/
- ret = __overlay_secure_ctrl(mfd);
- if (IS_ERR_VALUE(ret)) {
- pr_err("secure operation failed %d\n", ret);
- goto commit_fail;
+ if (!((mfd->panel_info->type == MIPI_VIDEO_PANEL) &&
+ ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) ||
+ (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE)))) {
+ ret = __overlay_secure_ctrl(mfd);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("secure operation failed %d\n", ret);
+ goto commit_fail;
+ }
}
/* call this function before any registers programming */
@@ -2482,6 +2618,17 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
mutex_lock(&mdp5_data->ov_lock);
+ /* Disable secure display/camera for video mode panels */
+ if ((mfd->panel_info->type == MIPI_VIDEO_PANEL) &&
+ ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) ||
+ (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE))) {
+ ret = __overlay_secure_ctrl(mfd);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("secure operation failed %d\n", ret);
+ goto commit_fail;
+ }
+ }
+
mdss_fb_update_notify_update(mfd);
commit_fail:
ATRACE_BEGIN("overlay_cleanup");
@@ -3375,6 +3522,13 @@ static ssize_t dynamic_fps_sysfs_wta_dfps(struct device *dev,
return count;
}
+ if (data.hfp > DFPS_DATA_MAX_HFP || data.hbp > DFPS_DATA_MAX_HBP ||
+ data.hpw > DFPS_DATA_MAX_HPW || data.fps > DFPS_DATA_MAX_FPS ||
+ data.clk_rate > DFPS_DATA_MAX_CLK_RATE){
+ pr_err("Data values out of bound.\n");
+ return -EINVAL;
+ }
+
rc = mdss_mdp_dfps_update_params(mfd, pdata, &data);
if (rc) {
pr_err("failed to set dfps params\n");
@@ -4268,12 +4422,21 @@ static int mdss_mdp_hw_cursor_pipe_update(struct msm_fb_data_type *mfd,
start_y = 0;
}
+ if ((img->width > mdata->max_cursor_size) ||
+ (img->height > mdata->max_cursor_size) ||
+ (img->depth != 32) || (start_x >= xres) ||
+ (start_y >= yres)) {
+ pr_err("Invalid cursor image coordinates\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
roi.w = min(xres - start_x, img->width - roi.x);
roi.h = min(yres - start_y, img->height - roi.y);
if ((roi.w > mdata->max_cursor_size) ||
- (roi.h > mdata->max_cursor_size) ||
- (img->depth != 32) || (start_x >= xres) || (start_y >= yres)) {
+ (roi.h > mdata->max_cursor_size)) {
+ pr_err("Invalid cursor ROI size\n");
ret = -EINVAL;
goto done;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index 1fe8fe6f7be8..f10d4fb60f52 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -2146,6 +2146,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
unsigned long flag;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 intr_mask;
+ u32 expected_sum = 0;
if (!mdata)
return -EPERM;
@@ -2156,6 +2157,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
block_type = DSPP;
op_flags = BIT(16);
hist_info = &mdss_pp_res->dspp_hist[mix->num];
+ expected_sum = mix->width * mix->height;
base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block));
if (IS_ERR(base)) {
ret = -EPERM;
@@ -2207,6 +2209,15 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
else if (hist_info->col_en)
*op |= op_flags;
+ if (hist_info->col_en) {
+ if (!hist_info->expect_sum) {
+ hist_info->expect_sum = expected_sum;
+ } else if (hist_info->expect_sum != expected_sum) {
+ hist_info->expect_sum = 0;
+ hist_info->next_sum = expected_sum;
+ }
+ }
+
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
mutex_unlock(&hist_info->hist_mutex);
error:
@@ -5276,8 +5287,7 @@ exit:
static int pp_hist_collect(struct mdp_histogram_data *hist,
struct pp_hist_col_info *hist_info,
- char __iomem *ctl_base, u32 expect_sum,
- u32 block)
+ char __iomem *ctl_base, u32 block)
{
int ret = 0;
int sum = 0;
@@ -5318,11 +5328,16 @@ static int pp_hist_collect(struct mdp_histogram_data *hist,
if (sum < 0) {
pr_err("failed to get the hist data, sum = %d\n", sum);
ret = sum;
- } else if (expect_sum && sum != expect_sum) {
+ } else if (hist_info->expect_sum && sum != hist_info->expect_sum) {
pr_err_ratelimited("hist error: bin sum incorrect! (%d/%d)\n",
- sum, expect_sum);
+ sum, hist_info->expect_sum);
ret = -EINVAL;
}
+
+ if (hist_info->next_sum) {
+ hist_info->expect_sum = hist_info->next_sum;
+ hist_info->next_sum = 0;
+ }
hist_collect_exit:
mutex_unlock(&hist_info->hist_mutex);
return ret;
@@ -5387,8 +5402,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
mdata->mixer_intf[dspp_num].height);
if (ret)
temp_ret = ret;
- ret = pp_hist_collect(hist, hists[i], ctl_base,
- exp_sum, DSPP);
+ ret = pp_hist_collect(hist, hists[i], ctl_base, DSPP);
if (ret)
pr_err_ratelimited("hist error: dspp[%d] collect %d\n",
dspp_num, ret);
@@ -5487,7 +5501,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
if (ret)
temp_ret = ret;
ret = pp_hist_collect(hist, hist_info, ctl_base,
- exp_sum, SSPP_VIG);
+ SSPP_VIG);
if (ret)
pr_debug("hist error: pipe[%d] collect: %d\n",
pipe->num, ret);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_trace.h b/drivers/video/fbdev/msm/mdss_mdp_trace.h
index b79b4c70f5dc..db2f85cb0361 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_trace.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_trace.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -297,22 +297,20 @@ TRACE_EVENT(mdp_perf_update_bus,
TRACE_EVENT(mdp_compare_bw,
TP_PROTO(unsigned long long new_ab, unsigned long long new_ib,
- unsigned long long new_wb, unsigned long long new_max,
+ unsigned long long new_wb,
unsigned long long old_ab, unsigned long long old_ib,
- unsigned long long old_wb, unsigned long long old_max,
+ unsigned long long old_wb,
u32 params_changed, bool update_bw),
- TP_ARGS(new_ab, new_ib, new_wb, new_max,
- old_ab, old_ib, old_wb, old_max,
+ TP_ARGS(new_ab, new_ib, new_wb,
+ old_ab, old_ib, old_wb,
params_changed, update_bw),
TP_STRUCT__entry(
__field(u64, new_ab)
__field(u64, new_ib)
__field(u64, new_wb)
- __field(u64, new_max)
__field(u64, old_ab)
__field(u64, old_ib)
__field(u64, old_wb)
- __field(u64, old_max)
__field(u32, params_changed)
__field(bool, update_bw)
),
@@ -320,18 +318,16 @@ TRACE_EVENT(mdp_compare_bw,
__entry->new_ab = new_ab;
__entry->new_ib = new_ib;
__entry->new_wb = new_wb;
- __entry->new_max = new_max;
__entry->old_ab = old_ab;
__entry->old_ib = old_ib;
__entry->old_wb = old_wb;
- __entry->old_max = old_max;
__entry->params_changed = params_changed;
__entry->update_bw = update_bw;
),
- TP_printk("[ab,ib,wb,max] new[%llu, %llu, %llu, %llu] old[%llu, %llu, %llu, %llu] parm:%d ret:%d",
+ TP_printk("[ab,ib,wb]new[%llu,%llu,%llu]old[%llu,%llu,%llu]chgd:%d %d",
__entry->new_ab, __entry->new_ib, __entry->new_wb,
- __entry->new_max, __entry->old_ab, __entry->old_ib,
- __entry->old_wb, __entry->old_max, __entry->params_changed,
+ __entry->old_ab, __entry->old_ib,
+ __entry->old_wb, __entry->params_changed,
__entry->update_bw)
);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index c14840ffd08d..d0bf61679f61 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -974,7 +974,9 @@ static int mdss_mdp_put_img(struct mdss_mdp_img_data *data, bool rotator,
* be filled due to map call which will be unmapped above.
*
*/
- pr_debug("skip memory unmapping for secure display/camera content\n");
+ if (data->ihandle)
+ ion_free(iclient, data->ihandle);
+ pr_debug("free memory handle for secure display/camera content\n");
} else {
return -ENOMEM;
}
@@ -1053,19 +1055,18 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
ret = 0;
goto done;
} else {
- struct ion_handle *ihandle = NULL;
struct sg_table *sg_ptr = NULL;
+ data->ihandle = ion_import_dma_buf(iclient,
+ img->memory_id);
+ if (IS_ERR_OR_NULL(data->ihandle)) {
+ ret = -EINVAL;
+ pr_err("ion import buffer failed\n");
+ data->ihandle = NULL;
+ goto done;
+ }
do {
- ihandle = ion_import_dma_buf(iclient,
- img->memory_id);
- if (IS_ERR_OR_NULL(ihandle)) {
- ret = -EINVAL;
- pr_err("ion import buffer failed\n");
- break;
- }
-
- sg_ptr = ion_sg_table(iclient, ihandle);
+ sg_ptr = ion_sg_table(iclient, data->ihandle);
if (sg_ptr == NULL) {
pr_err("ion sg table get failed\n");
ret = -EINVAL;
@@ -1091,8 +1092,6 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
ret = 0;
} while (0);
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(iclient, ihandle);
return ret;
}
}
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index e466c0097540..92413e078244 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -29,6 +29,9 @@ struct panel_id {
#define DEFAULT_FRAME_RATE 60
#define DEFAULT_ROTATOR_FRAME_RATE 120
#define ROTATOR_LOW_FRAME_RATE 30
+
+#define MDSS_DSI_MAX_ESC_CLK_RATE_HZ 19200000
+
#define MDSS_DSI_RST_SEQ_LEN 10
/* worst case prefill lines for all chipsets including all vertical blank */
#define MDSS_MDP_MAX_PREFILL_FETCH 25
@@ -112,12 +115,6 @@ enum {
};
enum {
- MDSS_PANEL_BLANK_BLANK = 0,
- MDSS_PANEL_BLANK_UNBLANK,
- MDSS_PANEL_BLANK_LOW_POWER,
-};
-
-enum {
MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0,
MDSS_PANEL_LOW_PERSIST_MODE_ON,
};
@@ -192,10 +189,16 @@ struct mdss_panel_cfg {
enum {
MDP_INTF_CALLBACK_DSI_WAIT,
+ MDP_INTF_CALLBACK_CHECK_LINE_COUNT,
};
struct mdss_intf_recovery {
- void (*fxn)(void *ctx, int event);
+ int (*fxn)(void *ctx, int event);
+ void *data;
+};
+
+struct mdss_intf_ulp_clamp {
+ int (*fxn)(void *ctx, int intf_num, bool enable);
void *data;
};
@@ -304,6 +307,7 @@ enum mdss_intf_events {
MDSS_EVENT_UPDATE_PANEL_PPM,
MDSS_EVENT_DSI_TIMING_DB_CTRL,
MDSS_EVENT_AVR_MODE,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
MDSS_EVENT_MAX,
};
@@ -360,6 +364,8 @@ static inline char *mdss_panel_intf_event_to_string(int event)
return INTF_EVENT_STR(MDSS_EVENT_REGISTER_RECOVERY_HANDLER);
case MDSS_EVENT_REGISTER_MDP_CALLBACK:
return INTF_EVENT_STR(MDSS_EVENT_REGISTER_MDP_CALLBACK);
+ case MDSS_EVENT_REGISTER_CLAMP_HANDLER:
+ return INTF_EVENT_STR(MDSS_EVENT_REGISTER_CLAMP_HANDLER);
case MDSS_EVENT_DSI_PANEL_STATUS:
return INTF_EVENT_STR(MDSS_EVENT_DSI_PANEL_STATUS);
case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
@@ -915,6 +921,9 @@ struct mdss_panel_info {
/* HDR properties of display panel*/
struct mdss_panel_hdr_properties hdr_properties;
+
+ /* esc clk recommended for the panel */
+ u32 esc_clk_rate_hz;
};
struct mdss_panel_timing {
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 0307d570ff64..fdd1c0153ce0 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -2065,10 +2065,12 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr,
return ret;
}
+ mutex_lock(&mgr->lock);
perf = mdss_rotator_find_session(private, config.session_id);
if (!perf) {
pr_err("No session with id=%u could be found\n",
config.session_id);
+ mutex_unlock(&mgr->lock);
return -EINVAL;
}
@@ -2091,6 +2093,7 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr,
config.output.format);
done:
ATRACE_END(__func__);
+ mutex_unlock(&mgr->lock);
return ret;
}
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index e6151b4c75a1..03e78733d168 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1863,8 +1863,10 @@ static int mdss_dsi_ulps_config_default(struct mdss_dsi_ctrl_pdata *ctrl,
* to be in stop state.
*/
MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16);
+ wmb(); /* ensure lanes are put to stop state */
MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0);
+ wmb(); /* ensure lanes are in proper state */
lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
}
@@ -1983,6 +1985,7 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl,
struct mipi_panel_info *mipi = NULL;
u32 clamp_reg, regval = 0;
u32 clamp_reg_off;
+ u32 intf_num = 0;
if (!ctrl) {
pr_err("%s: invalid input\n", __func__);
@@ -1994,6 +1997,21 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl,
return -EINVAL;
}
+ /*
+ * For DSI HW version 2.1.0 ULPS_CLAMP register
+ * is moved to interface level.
+ */
+ if (ctrl->shared_data->hw_rev == MDSS_DSI_HW_REV_201) {
+ intf_num = ctrl->ndx ? MDSS_MDP_INTF2 : MDSS_MDP_INTF1;
+ if (ctrl->clamp_handler) {
+ ctrl->clamp_handler->fxn(ctrl->clamp_handler->data,
+ intf_num, enable);
+ pr_debug("%s: ndx: %d enable: %d\n",
+ __func__, ctrl->ndx, enable);
+ }
+ return 0;
+ }
+
clamp_reg_off = ctrl->shared_data->ulps_clamp_ctrl_off;
mipi = &ctrl->panel_data.panel_info.mipi;
@@ -2256,6 +2274,8 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
pdata = &ctrl->panel_data;
if ((clk & MDSS_DSI_LINK_CLK) && (new_state == MDSS_DSI_CLK_OFF)) {
+ if (pdata->panel_info.mipi.force_clk_lane_hs)
+ mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 0);
/*
* If ULPS feature is enabled, enter ULPS first.
* However, when blanking the panel, we should enter ULPS
@@ -2371,6 +2391,8 @@ int mdss_dsi_post_clkon_cb(void *priv,
goto error;
}
}
+ if (pdata->panel_info.mipi.force_clk_lane_hs)
+ mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1);
}
error:
return rc;