diff options
| author | Arun KS <arunks@codeaurora.org> | 2017-04-06 15:45:04 +0530 |
|---|---|---|
| committer | Arun KS <arunks@codeaurora.org> | 2017-04-06 15:45:04 +0530 |
| commit | e8a49b120cbc3a0472aebcd0f8b06cf95f775a54 (patch) | |
| tree | 7b58ec70803bbfda534ed2a8b50d321a1ecb22ba /drivers/video | |
| parent | 9a0d24cf9f0e1d4cad7cf92f7e50939fb605e075 (diff) | |
| parent | a3851309dbf7e919b27e2ec927ba3f6350347dff (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')
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; |
