drm/msm/sde: add OT support for source pipe and writeback

OT limit controls the number of outstanding requests on bus client
interface.  This patch adds OT limit control for source pipe and
writeback bus clients.

Change-Id: I66c0ede9da3b292538fec83ba62ccd453ee1ec37
Signed-off-by: Alan Kwong <akwong@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index e3c1df8..353ee69 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -130,6 +130,7 @@
 	sde/sde_hw_pingpong.o \
 	sde/sde_hw_top.o \
 	sde/sde_hw_interrupts.o \
+	sde/sde_hw_vbif.o \
 	sde/sde_formats.o
 
 obj-$(CONFIG_DRM_MSM) += display-manager/display_manager.o
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index e325190..0fc33a0 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -51,6 +51,31 @@
 }
 
 /**
+ * sde_encoder_phys_wb_set_ot_limit - set OT limit for writeback interface
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void sde_encoder_phys_wb_set_ot_limit(
+		struct sde_encoder_phys *phys_enc)
+{
+	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+	struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
+	struct sde_vbif_set_ot_params ot_params;
+
+	memset(&ot_params, 0, sizeof(ot_params));
+	ot_params.xin_id = hw_wb->caps->xin_id;
+	ot_params.num = hw_wb->idx - WB_0;
+	ot_params.width = wb_enc->wb_roi.w;
+	ot_params.height = wb_enc->wb_roi.h;
+	ot_params.is_wfd = true;
+	ot_params.frame_rate = phys_enc->cached_mode.vrefresh;
+	ot_params.vbif_idx = hw_wb->caps->vbif_idx;
+	ot_params.clk_ctrl = hw_wb->caps->clk_ctrl;
+	ot_params.rd = false;
+
+	sde_vbif_set_ot_limit(phys_enc->sde_kms, &ot_params);
+}
+
+/**
  * sde_encoder_phys_wb_set_traffic_shaper - set traffic shaper for writeback
  * @phys_enc:	Pointer to physical encoder
  */
@@ -438,6 +463,8 @@
 	SDE_DEBUG("[fb_fmt:%x,%llx]\n", fb->pixel_format,
 			fb->modifier[0]);
 
+	sde_encoder_phys_wb_set_ot_limit(phys_enc);
+
 	sde_encoder_phys_wb_set_traffic_shaper(phys_enc);
 
 	sde_encoder_phys_setup_cdm(phys_enc, fb, wb_enc->wb_fmt, wb_roi);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index ded8b6a..00c7a29 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -191,6 +191,16 @@
 };
 
 /**
+ * VBIF sub-blocks and features
+ * @SDE_VBIF_QOS_OTLIM        VBIF supports OT Limit
+ * @SDE_VBIF_MAX              maximum value
+ */
+enum {
+	SDE_VBIF_QOS_OTLIM = 0x1,
+	SDE_VBIF_MAX
+};
+
+/**
  * MACRO SDE_HW_BLK_INFO - information of HW blocks inside SDE
  * @id:                enum identifying this block
  * @base:              register base offset to mdss
@@ -339,15 +349,50 @@
 	SDE_HW_BLK_INFO;
 };
 
+/**
+ * sde_clk_ctrl_type - Defines top level clock control signals
+ */
+enum sde_clk_ctrl_type {
+	SDE_CLK_CTRL_NONE,
+	SDE_CLK_CTRL_VIG0,
+	SDE_CLK_CTRL_VIG1,
+	SDE_CLK_CTRL_VIG2,
+	SDE_CLK_CTRL_VIG3,
+	SDE_CLK_CTRL_VIG4,
+	SDE_CLK_CTRL_RGB0,
+	SDE_CLK_CTRL_RGB1,
+	SDE_CLK_CTRL_RGB2,
+	SDE_CLK_CTRL_RGB3,
+	SDE_CLK_CTRL_DMA0,
+	SDE_CLK_CTRL_DMA1,
+	SDE_CLK_CTRL_CURSOR0,
+	SDE_CLK_CTRL_CURSOR1,
+	SDE_CLK_CTRL_WB0,
+	SDE_CLK_CTRL_WB1,
+	SDE_CLK_CTRL_WB2,
+	SDE_CLK_CTRL_MAX,
+};
+
+/* struct sde_clk_ctrl_reg : Clock control register
+ * @reg_off:           register offset
+ * @bit_off:           bit offset
+ */
+struct sde_clk_ctrl_reg {
+	u32 reg_off;
+	u32 bit_off;
+};
+
 /* struct sde_mdp_cfg : MDP TOP-BLK instance info
  * @id:                index identifying this block
  * @base:              register base offset to mdss
  * @features           bit mask identifying sub-blocks/features
  * @highest_bank_bit:  UBWC parameter
+ * @clk_ctrls          clock control register definition
  */
 struct sde_mdp_cfg {
 	SDE_HW_BLK_INFO;
 	u32 highest_bank_bit;
+	struct sde_clk_ctrl_reg clk_ctrls[SDE_CLK_CTRL_MAX];
 };
 
 /* struct sde_mdp_cfg : MDP TOP-BLK instance info
@@ -365,10 +410,14 @@
  * @base               register offset of this block
  * @features           bit mask identifying sub-blocks/features
  * @sblk:              SSPP sub-blocks information
+ * @xin_id:            bus client identifier
+ * @clk_ctrl           clock control identifier
  */
 struct sde_sspp_cfg {
 	SDE_HW_BLK_INFO;
 	const struct sde_sspp_sub_blks *sblk;
+	u32 xin_id;
+	enum sde_clk_ctrl_type clk_ctrl;
 };
 
 /**
@@ -451,11 +500,17 @@
  * @features           bit mask identifying sub-blocks/features
  * @sblk               sub-block information
  * @format_list: Pointer to list of supported formats
+ * @vbif_idx           vbif identifier
+ * @xin_id             client interface identifier
+ * @clk_ctrl           clock control identifier
  */
 struct sde_wb_cfg {
 	SDE_HW_BLK_INFO;
 	const struct sde_wb_sub_blocks *sblk;
 	const struct sde_format_extended *format_list;
+	u32 vbif_idx;
+	u32 xin_id;
+	enum sde_clk_ctrl_type clk_ctrl;
 };
 
 /**
@@ -469,6 +524,47 @@
 };
 
 /**
+ * struct sde_vbif_dynamic_ot_cfg - dynamic OT setting
+ * @pps                pixel per seconds
+ * @ot_limit           OT limit to use up to specified pixel per second
+ */
+struct sde_vbif_dynamic_ot_cfg {
+	u64 pps;
+	u32 ot_limit;
+};
+
+/**
+ * struct sde_vbif_dynamic_ot_tbl - dynamic OT setting table
+ * @count              length of cfg
+ * @cfg                pointer to array of configuration settings with
+ *                     ascending requirements
+ */
+struct sde_vbif_dynamic_ot_tbl {
+	u32 count;
+	const struct sde_vbif_dynamic_ot_cfg *cfg;
+};
+
+/**
+ * struct sde_vbif_cfg - information of VBIF blocks
+ * @id                 enum identifying this block
+ * @base               register offset of this block
+ * @features           bit mask identifying sub-blocks/features
+ * @ot_rd_limit        default OT read limit
+ * @ot_wr_limit        default OT write limit
+ * @xin_halt_timeout   maximum time (in usec) for xin to halt
+ * @dynamic_ot_rd_tbl  dynamic OT read configuration table
+ * @dynamic_ot_wr_tbl  dynamic OT write configuration table
+ */
+struct sde_vbif_cfg {
+	SDE_HW_BLK_INFO;
+	u32 default_ot_rd_limit;
+	u32 default_ot_wr_limit;
+	u32 xin_halt_timeout;
+	struct sde_vbif_dynamic_ot_tbl dynamic_ot_rd_tbl;
+	struct sde_vbif_dynamic_ot_tbl dynamic_ot_wr_tbl;
+};
+
+/**
  * struct sde_mdss_cfg - information of MDSS HW
  * This is the main catalog data structure representing
  * this HW version. Contains number of instances,
@@ -509,6 +605,9 @@
 
 	u32 ad_count;
 	struct sde_ad_cfg ad[MAX_BLOCKS];
+
+	u32 vbif_count;
+	struct sde_vbif_cfg vbif[MAX_BLOCKS];
 	/* Add additional block data structures here */
 };
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
index d8c6fa3..a397d67 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog_8996.c
@@ -62,6 +62,9 @@
 #define DECIMATION_17X_MAX_H	4
 #define DECIMATION_17X_MAX_V	4
 
+#define RES_1080p		((u64)(1088*1920))
+#define RES_UHD			((u64)(3840*2160))
+
 static const struct sde_format_extended plane_formats[] = {
 	{DRM_FORMAT_ARGB8888, 0},
 	{DRM_FORMAT_ABGR8888, 0},
@@ -372,6 +375,12 @@
 		.maxlinewidth = 4096,
 	};
 
+	static const struct sde_vbif_dynamic_ot_cfg dynamic_ot_cfg[] = {
+		{RES_1080p * 30, 2},
+		{RES_1080p * 60, 4},
+		{RES_UHD * 30, 16},
+	};
+
 	/* Setup Register maps and defaults */
 	*cfg = (struct sde_mdss_cfg){
 		.mdss_count = 1,
@@ -381,7 +390,38 @@
 		.mdp_count = 1,
 		.mdp = {
 			{.id = MDP_TOP, .base = 0x00001000, .features = 0,
-				.highest_bank_bit = 0x2},
+				.highest_bank_bit = 0x2,
+				.clk_ctrls[SDE_CLK_CTRL_VIG0] = {
+					.reg_off = 0x2AC, .bit_off = 0},
+				.clk_ctrls[SDE_CLK_CTRL_VIG1] = {
+					.reg_off = 0x2B4, .bit_off = 0},
+				.clk_ctrls[SDE_CLK_CTRL_VIG2] = {
+					.reg_off = 0x2BC, .bit_off = 0},
+				.clk_ctrls[SDE_CLK_CTRL_VIG3] = {
+					.reg_off = 0x2C4, .bit_off = 0},
+				.clk_ctrls[SDE_CLK_CTRL_RGB0] = {
+					.reg_off = 0x2AC, .bit_off = 4},
+				.clk_ctrls[SDE_CLK_CTRL_RGB1] = {
+					.reg_off = 0x2B4, .bit_off = 4},
+				.clk_ctrls[SDE_CLK_CTRL_RGB2] = {
+					.reg_off = 0x2BC, .bit_off = 4},
+				.clk_ctrls[SDE_CLK_CTRL_RGB3] = {
+					.reg_off = 0x2C4, .bit_off = 4},
+				.clk_ctrls[SDE_CLK_CTRL_DMA0] = {
+					.reg_off = 0x2AC, .bit_off = 8},
+				.clk_ctrls[SDE_CLK_CTRL_DMA1] = {
+					.reg_off = 0x2B4, .bit_off = 8},
+				.clk_ctrls[SDE_CLK_CTRL_CURSOR0] = {
+					.reg_off = 0x3A8, .bit_off = 16},
+				.clk_ctrls[SDE_CLK_CTRL_CURSOR1] = {
+					.reg_off = 0x3B0, .bit_off = 16},
+				.clk_ctrls[SDE_CLK_CTRL_WB0] = {
+					.reg_off = 0x2BC, .bit_off = 8},
+				.clk_ctrls[SDE_CLK_CTRL_WB1] = {
+					.reg_off = 0x2BC, .bit_off = 12},
+				.clk_ctrls[SDE_CLK_CTRL_WB2] = {
+					.reg_off = 0x2BC, .bit_off = 16},
+			},
 		},
 		.ctl_count = 5,
 		.ctl = {
@@ -403,32 +443,56 @@
 		.sspp_count = 12,
 		.sspp = {
 			{.id = SSPP_VIG0, .base = 0x00005000,
-			.features = VIG_17X_MASK, .sblk = &vig_layer},
+			.features = VIG_17X_MASK, .sblk = &vig_layer,
+			.xin_id = 0,
+			.clk_ctrl = SDE_CLK_CTRL_VIG0},
 			{.id = SSPP_VIG1, .base = 0x00007000,
-			.features = VIG_17X_MASK, .sblk = &vig_layer},
+			.features = VIG_17X_MASK, .sblk = &vig_layer,
+			.xin_id = 4,
+			.clk_ctrl = SDE_CLK_CTRL_VIG1},
 			{.id = SSPP_VIG2, .base = 0x00009000,
-			.features = VIG_17X_MASK, .sblk = &vig_layer},
+			.features = VIG_17X_MASK, .sblk = &vig_layer,
+			.xin_id = 8,
+			.clk_ctrl = SDE_CLK_CTRL_VIG2},
 			{.id = SSPP_VIG3, .base = 0x0000b000,
-			.features = VIG_17X_MASK, .sblk = &vig_layer},
+			.features = VIG_17X_MASK, .sblk = &vig_layer,
+			.xin_id = 12,
+			.clk_ctrl = SDE_CLK_CTRL_VIG3},
 
 			{.id = SSPP_RGB0, .base = 0x00015000,
-			.features = RGB_17X_MASK, .sblk = &layer},
+			.features = RGB_17X_MASK, .sblk = &layer,
+			.xin_id = 1,
+			.clk_ctrl = SDE_CLK_CTRL_RGB0},
 			{.id = SSPP_RGB1, .base = 0x00017000,
-			.features = RGB_17X_MASK, .sblk = &layer},
+			.features = RGB_17X_MASK, .sblk = &layer,
+			.xin_id = 5,
+			.clk_ctrl = SDE_CLK_CTRL_RGB1},
 			{.id = SSPP_RGB2, .base = 0x00019000,
-			.features = RGB_17X_MASK, .sblk = &layer},
+			.features = RGB_17X_MASK, .sblk = &layer,
+			.xin_id = 9,
+			.clk_ctrl = SDE_CLK_CTRL_RGB2},
 			{.id = SSPP_RGB3, .base = 0x0001B000,
-			.features = RGB_17X_MASK, .sblk = &layer},
+			.features = RGB_17X_MASK, .sblk = &layer,
+			.xin_id = 13,
+			.clk_ctrl = SDE_CLK_CTRL_RGB3},
 
 			{.id = SSPP_DMA0, .base = 0x00025000,
-			.features = DMA_17X_MASK, .sblk = &dma},
+			.features = DMA_17X_MASK, .sblk = &dma,
+			.xin_id = 2,
+			.clk_ctrl = SDE_CLK_CTRL_DMA0},
 			{.id = SSPP_DMA1, .base = 0x00027000,
-			.features = DMA_17X_MASK, .sblk = &dma},
+			.features = DMA_17X_MASK, .sblk = &dma,
+			.xin_id = 10,
+			.clk_ctrl = SDE_CLK_CTRL_DMA1},
 
 			{.id = SSPP_CURSOR0, .base = 0x00035000,
-			.features = CURSOR_17X_MASK, .sblk = &cursor},
+			.features = CURSOR_17X_MASK, .sblk = &cursor,
+			.xin_id = 7,
+			.clk_ctrl = SDE_CLK_CTRL_CURSOR0},
 			{.id = SSPP_CURSOR1, .base = 0x00037000,
-			.features = CURSOR_17X_MASK, .sblk = &cursor},
+			.features = CURSOR_17X_MASK, .sblk = &cursor,
+			.xin_id = 7,
+			.clk_ctrl = SDE_CLK_CTRL_CURSOR1},
 		},
 		.mixer_count = 6,
 		.mixer = {
@@ -517,21 +581,59 @@
 			{.id = WB_0, .base = 0x00065000,
 				.features = WB01_17X_MASK,
 				.sblk = &wb0,
-				.format_list = wb0_formats},
+				.format_list = wb0_formats,
+				.vbif_idx = VBIF_NRT,
+				.xin_id = 3,
+				.clk_ctrl = SDE_CLK_CTRL_WB0},
 			{.id = WB_1, .base = 0x00065800,
 				.features = WB01_17X_MASK,
 				.sblk = &wb0,
-				.format_list = wb0_formats},
+				.format_list = wb0_formats,
+				.vbif_idx = VBIF_NRT,
+				.xin_id = 11,
+				.clk_ctrl = SDE_CLK_CTRL_WB1},
 			{.id = WB_2, .base = 0x00066000,
 				.features = WB2_17X_MASK,
 				.sblk = &wb2,
-				.format_list = wb2_formats},
+				.format_list = wb2_formats,
+				.vbif_idx = VBIF_NRT,
+				.xin_id = 6,
+				.clk_ctrl = SDE_CLK_CTRL_WB2},
 		},
 		.ad_count = 2,
 		.ad = {
 			{.id = AD_0, .base = 0x00079000},
 			{.id = AD_1, .base = 0x00079800},
 		},
+		.vbif_count = 2,
+		.vbif = {
+			{.id = VBIF_0,
+				.base = 0, /* 0x000B0000 */
+				.features = BIT(SDE_VBIF_QOS_OTLIM),
+				.default_ot_rd_limit = 32,
+				.default_ot_wr_limit = 16,
+				.xin_halt_timeout = 0x4000,
+				.dynamic_ot_rd_tbl = {
+					.count = ARRAY_SIZE(dynamic_ot_cfg),
+					.cfg = dynamic_ot_cfg},
+				.dynamic_ot_wr_tbl = {
+					.count = ARRAY_SIZE(dynamic_ot_cfg),
+					.cfg = dynamic_ot_cfg},
+			},
+			{.id = VBIF_1,
+				.base = 0, /* 0x000B8000 */
+				.features = BIT(SDE_VBIF_QOS_OTLIM),
+				.default_ot_rd_limit = 32,
+				.default_ot_wr_limit = 16,
+				.xin_halt_timeout = 0x4000,
+				.dynamic_ot_rd_tbl = {
+					.count = ARRAY_SIZE(dynamic_ot_cfg),
+					.cfg = dynamic_ot_cfg},
+				.dynamic_ot_wr_tbl = {
+					.count = ARRAY_SIZE(dynamic_ot_cfg),
+					.cfg = dynamic_ot_cfg},
+			},
+		},
 	};
 	return 0;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index db16e9b..2f3aa9a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -426,5 +426,6 @@
 #define SDE_DBG_MASK_SSPP     (1 << 7)
 #define SDE_DBG_MASK_WB       (1 << 8)
 #define SDE_DBG_MASK_TOP      (1 << 9)
+#define SDE_DBG_MASK_VBIF     (1 << 10)
 
 #endif  /* _SDE_HW_MDSS_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index 9e31187..8feca9b 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -108,12 +108,41 @@
 	SDE_REG_WRITE(c, offset, ts_control);
 }
 
+static bool sde_hw_setup_clk_force_ctrl(struct sde_hw_mdp *mdp,
+		enum sde_clk_ctrl_type clk_ctrl, bool enable)
+{
+	struct sde_hw_blk_reg_map *c = &mdp->hw;
+	u32 reg_off, bit_off;
+	u32 reg_val, new_val;
+	bool clk_forced_on;
+
+	if (clk_ctrl <= SDE_CLK_CTRL_NONE || clk_ctrl >= SDE_CLK_CTRL_MAX)
+		return false;
+
+	reg_off = mdp->cap->clk_ctrls[clk_ctrl].reg_off;
+	bit_off = mdp->cap->clk_ctrls[clk_ctrl].bit_off;
+
+	reg_val = SDE_REG_READ(c, reg_off);
+
+	if (enable)
+		new_val = reg_val | BIT(bit_off);
+	else
+		new_val = reg_val & ~BIT(bit_off);
+
+	SDE_REG_WRITE(c, reg_off, new_val);
+
+	clk_forced_on = !(reg_val & BIT(bit_off));
+
+	return clk_forced_on;
+}
+
 static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops,
 		unsigned long cap)
 {
 	ops->setup_split_pipe = sde_hw_setup_split_pipe_control;
 	ops->setup_cdm_output = sde_hw_setup_cdm_output;
 	ops->setup_traffic_shaper = sde_hw_setup_traffic_shaper;
+	ops->setup_clk_force_ctrl = sde_hw_setup_clk_force_ctrl;
 }
 
 static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.h b/drivers/gpu/drm/msm/sde/sde_hw_top.h
index d310afd..7359a77 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.h
@@ -93,6 +93,16 @@
 	 */
 	void (*setup_traffic_shaper)(struct sde_hw_mdp *mdp,
 			struct traffic_shaper_cfg *cfg);
+
+	/**
+	 * setup_clk_force_ctrl - set clock force control
+	 * @mdp: mdp top context driver
+	 * @clk_ctrl: clock to be controlled
+	 * @enable: force on enable
+	 * @return: if the clock is forced-on by this function
+	 */
+	bool (*setup_clk_force_ctrl)(struct sde_hw_mdp *mdp,
+			enum sde_clk_ctrl_type clk_ctrl, bool enable);
 };
 
 struct sde_hw_mdp {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
new file mode 100644
index 0000000..76473fa
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
@@ -0,0 +1,165 @@
+/* Copyright (c) 2015-2016, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sde_hwio.h"
+#include "sde_hw_catalog.h"
+#include "sde_hw_vbif.h"
+
+#define VBIF_VERSION			0x0000
+#define VBIF_CLK_FORCE_CTRL0		0x0008
+#define VBIF_CLK_FORCE_CTRL1		0x000C
+#define VBIF_QOS_REMAP_00		0x0020
+#define VBIF_QOS_REMAP_01		0x0024
+#define VBIF_QOS_REMAP_10		0x0028
+#define VBIF_QOS_REMAP_11		0x002C
+#define VBIF_WRITE_GATHTER_EN		0x00AC
+#define VBIF_IN_RD_LIM_CONF0		0x00B0
+#define VBIF_IN_RD_LIM_CONF1		0x00B4
+#define VBIF_IN_RD_LIM_CONF2		0x00B8
+#define VBIF_IN_WR_LIM_CONF0		0x00C0
+#define VBIF_IN_WR_LIM_CONF1		0x00C4
+#define VBIF_IN_WR_LIM_CONF2		0x00C8
+#define VBIF_OUT_RD_LIM_CONF0		0x00D0
+#define VBIF_OUT_WR_LIM_CONF0		0x00D4
+#define VBIF_XIN_HALT_CTRL0		0x0200
+#define VBIF_XIN_HALT_CTRL1		0x0204
+
+static void sde_hw_set_limit_conf(struct sde_hw_vbif *vbif,
+		u32 xin_id, bool rd, u32 limit)
+{
+	struct sde_hw_blk_reg_map *c = &vbif->hw;
+	u32 reg_val;
+	u32 reg_off;
+	u32 bit_off;
+
+	if (rd)
+		reg_off = VBIF_IN_RD_LIM_CONF0;
+	else
+		reg_off = VBIF_IN_WR_LIM_CONF0;
+
+	reg_off += (xin_id / 4) * 4;
+	bit_off = (xin_id % 4) * 8;
+	reg_val = SDE_REG_READ(c, reg_off);
+	reg_val &= ~(0xFF << bit_off);
+	reg_val |= (limit) << bit_off;
+	SDE_REG_WRITE(c, reg_off, reg_val);
+}
+
+static u32 sde_hw_get_limit_conf(struct sde_hw_vbif *vbif,
+		u32 xin_id, bool rd)
+{
+	struct sde_hw_blk_reg_map *c = &vbif->hw;
+	u32 reg_val;
+	u32 reg_off;
+	u32 bit_off;
+	u32 limit;
+
+	if (rd)
+		reg_off = VBIF_IN_RD_LIM_CONF0;
+	else
+		reg_off = VBIF_IN_WR_LIM_CONF0;
+
+	reg_off += (xin_id / 4) * 4;
+	bit_off = (xin_id % 4) * 8;
+	reg_val = SDE_REG_READ(c, reg_off);
+	limit = (reg_val >> bit_off) & 0xFF;
+
+	return limit;
+}
+
+static void sde_hw_set_halt_ctrl(struct sde_hw_vbif *vbif,
+		u32 xin_id, bool enable)
+{
+	struct sde_hw_blk_reg_map *c = &vbif->hw;
+	u32 reg_val;
+
+	reg_val = SDE_REG_READ(c, VBIF_XIN_HALT_CTRL0);
+
+	if (enable)
+		reg_val |= BIT(xin_id);
+	else
+		reg_val &= ~BIT(xin_id);
+
+	SDE_REG_WRITE(c, VBIF_XIN_HALT_CTRL0, reg_val);
+}
+
+static bool sde_hw_get_halt_ctrl(struct sde_hw_vbif *vbif,
+		u32 xin_id)
+{
+	struct sde_hw_blk_reg_map *c = &vbif->hw;
+	u32 reg_val;
+
+	reg_val = SDE_REG_READ(c, VBIF_XIN_HALT_CTRL1);
+
+	return (reg_val & BIT(xin_id)) ? true : false;
+}
+
+static void _setup_vbif_ops(struct sde_hw_vbif_ops *ops,
+		unsigned long cap)
+{
+	ops->set_limit_conf = sde_hw_set_limit_conf;
+	ops->get_limit_conf = sde_hw_get_limit_conf;
+	ops->set_halt_ctrl = sde_hw_set_halt_ctrl;
+	ops->get_halt_ctrl = sde_hw_get_halt_ctrl;
+}
+
+static const struct sde_vbif_cfg *_top_offset(enum sde_vbif vbif,
+		const struct sde_mdss_cfg *m,
+		void __iomem *addr,
+		struct sde_hw_blk_reg_map *b)
+{
+	int i;
+
+	for (i = 0; i < m->vbif_count; i++) {
+		if (vbif == m->vbif[i].id) {
+			b->base_off = addr;
+			b->blk_off = m->vbif[i].base;
+			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_VBIF;
+			return &m->vbif[i];
+		}
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
+struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx,
+		void __iomem *addr,
+		const struct sde_mdss_cfg *m)
+{
+	struct sde_hw_vbif *c;
+	const struct sde_vbif_cfg *cfg;
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return ERR_PTR(-ENOMEM);
+
+	cfg = _top_offset(idx, m, addr, &c->hw);
+	if (IS_ERR_OR_NULL(cfg)) {
+		kfree(c);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/*
+	 * Assign ops
+	 */
+	c->idx = idx;
+	c->cap = cfg;
+	_setup_vbif_ops(&c->ops, c->cap->features);
+
+	return c;
+}
+
+void sde_hw_vbif_destroy(struct sde_hw_vbif *vbif)
+{
+	kfree(vbif);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.h b/drivers/gpu/drm/msm/sde/sde_hw_vbif.h
new file mode 100644
index 0000000..de7fac0
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.h
@@ -0,0 +1,90 @@
+/* Copyright (c) 2015-2016, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SDE_HW_VBIF_H
+#define _SDE_HW_VBIF_H
+
+#include "sde_hw_catalog.h"
+#include "sde_hw_mdss.h"
+#include "sde_hw_util.h"
+
+struct sde_hw_vbif;
+
+/**
+ * struct sde_hw_vbif_ops : Interface to the VBIF hardware driver functions
+ *  Assumption is these functions will be called after clocks are enabled
+ */
+struct sde_hw_vbif_ops {
+	/**
+	 * set_limit_conf - set transaction limit config
+	 * @vbif: vbif context driver
+	 * @xin_id: client interface identifier
+	 * @rd: true for read limit; false for write limit
+	 * @limit: outstanding transaction limit
+	 */
+	void (*set_limit_conf)(struct sde_hw_vbif *vbif,
+			u32 xin_id, bool rd, u32 limit);
+
+	/**
+	 * get_limit_conf - get transaction limit config
+	 * @vbif: vbif context driver
+	 * @xin_id: client interface identifier
+	 * @rd: true for read limit; false for write limit
+	 * @return: outstanding transaction limit
+	 */
+	u32 (*get_limit_conf)(struct sde_hw_vbif *vbif,
+			u32 xin_id, bool rd);
+
+	/**
+	 * set_halt_ctrl - set halt control
+	 * @vbif: vbif context driver
+	 * @xin_id: client interface identifier
+	 * @enable: halt control enable
+	 */
+	void (*set_halt_ctrl)(struct sde_hw_vbif *vbif,
+			u32 xin_id, bool enable);
+
+	/**
+	 * get_halt_ctrl - get halt control
+	 * @vbif: vbif context driver
+	 * @xin_id: client interface identifier
+	 * @return: halt control enable
+	 */
+	bool (*get_halt_ctrl)(struct sde_hw_vbif *vbif,
+			u32 xin_id);
+};
+
+struct sde_hw_vbif {
+	/* base */
+	struct sde_hw_blk_reg_map hw;
+
+	/* vbif */
+	enum sde_vbif idx;
+	const struct sde_vbif_cfg *cap;
+
+	/* ops */
+	struct sde_hw_vbif_ops ops;
+};
+
+/**
+ * sde_hw_vbif_init - initializes the vbif driver for the passed interface idx
+ * @idx:  Interface index for which driver object is required
+ * @addr: Mapped register io address of MDSS
+ * @m:    Pointer to mdss catalog data
+ */
+struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx,
+		void __iomem *addr,
+		const struct sde_mdss_cfg *m);
+
+void sde_hw_vbif_destroy(struct sde_hw_vbif *vbif);
+
+#endif /*_SDE_HW_VBIF_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index a255303..c8fe90a 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -10,6 +10,8 @@
  * GNU General Public License for more details.
  */
 
+#define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
+
 #include <drm/drm_crtc.h>
 #include <linux/debugfs.h>
 
@@ -20,6 +22,7 @@
 #include "sde_hw_mdss.h"
 #include "sde_hw_util.h"
 #include "sde_hw_intf.h"
+#include "sde_hw_vbif.h"
 
 #define CREATE_TRACE_POINTS
 #include "sde_trace.h"
@@ -324,6 +327,14 @@
 static void sde_destroy(struct msm_kms *kms)
 {
 	struct sde_kms *sde_kms = to_sde_kms(kms);
+	int i;
+
+	for (i = 0; i < sde_kms->catalog->vbif_count; i++) {
+		u32 vbif_idx = sde_kms->catalog->vbif[i].id;
+
+		if ((vbif_idx < VBIF_MAX) && sde_kms->hw_vbif[vbif_idx])
+			sde_hw_vbif_destroy(sde_kms->hw_vbif[vbif_idx]);
+	}
 
 	sde_debugfs_destroy(sde_kms);
 	sde_irq_domain_fini(sde_kms);
@@ -359,6 +370,198 @@
 	sde_kms->core_rev = readl_relaxed(sde_kms->mmio + 0x0);
 }
 
+/**
+ * _sde_vbif_wait_for_xin_halt - wait for the xin to halt
+ * @vbif:	Pointer to hardware vbif driver
+ * @xin_id:	Client interface identifier
+ * @return:	0 if success; error code otherwise
+ */
+static int _sde_vbif_wait_for_xin_halt(struct sde_hw_vbif *vbif, u32 xin_id)
+{
+	ktime_t timeout;
+	bool status;
+	int rc;
+
+	if (!vbif || !vbif->cap || !vbif->ops.get_halt_ctrl) {
+		SDE_ERROR("invalid arguments vbif %d\n", vbif != 0);
+		return -EINVAL;
+	}
+
+	timeout = ktime_add_us(ktime_get(), vbif->cap->xin_halt_timeout);
+	for (;;) {
+		status = vbif->ops.get_halt_ctrl(vbif, xin_id);
+		if (status)
+			break;
+		if (ktime_compare_safe(ktime_get(), timeout) > 0) {
+			status = vbif->ops.get_halt_ctrl(vbif, xin_id);
+			break;
+		}
+		usleep_range(501, 1000);
+	}
+
+	if (!status) {
+		rc = -ETIMEDOUT;
+		SDE_ERROR("VBIF %d client %d not halting. TIMEDOUT.\n",
+				vbif->idx - VBIF_0, xin_id);
+	} else {
+		rc = 0;
+		SDE_DEBUG("VBIF %d client %d is halted\n",
+				vbif->idx - VBIF_0, xin_id);
+	}
+
+	return rc;
+}
+
+/**
+ * _sde_vbif_apply_dynamic_ot_limit - determine OT based on usecase parameters
+ * @vbif:	Pointer to hardware vbif driver
+ * @ot_lim:	Pointer to OT limit to be modified
+ * @params:	Pointer to usecase parameters
+ */
+static void _sde_vbif_apply_dynamic_ot_limit(struct sde_hw_vbif *vbif,
+		u32 *ot_lim, struct sde_vbif_set_ot_params *params)
+{
+	u64 pps;
+	const struct sde_vbif_dynamic_ot_tbl *tbl;
+	u32 i;
+
+	if (!vbif || !(vbif->cap->features & BIT(SDE_VBIF_QOS_OTLIM)))
+		return;
+
+	/* Dynamic OT setting done only for WFD */
+	if (!params->is_wfd)
+		return;
+
+	pps = params->frame_rate;
+	pps *= params->width;
+	pps *= params->height;
+
+	tbl = params->rd ? &vbif->cap->dynamic_ot_rd_tbl :
+			&vbif->cap->dynamic_ot_wr_tbl;
+
+	for (i = 0; i < tbl->count; i++) {
+		if (pps <= tbl->cfg[i].pps) {
+			*ot_lim = tbl->cfg[i].ot_limit;
+			break;
+		}
+	}
+
+	SDE_DEBUG("vbif:%d xin:%d w:%d h:%d fps:%d pps:%llu ot:%u\n",
+			vbif->idx - VBIF_0, params->xin_id,
+			params->width, params->height, params->frame_rate,
+			pps, *ot_lim);
+}
+
+/**
+ * _sde_vbif_get_ot_limit - get OT based on usecase & configuration parameters
+ * @vbif:	Pointer to hardware vbif driver
+ * @params:	Pointer to usecase parameters
+ * @return:	OT limit
+ */
+static u32 _sde_vbif_get_ot_limit(struct sde_hw_vbif *vbif,
+	struct sde_vbif_set_ot_params *params)
+{
+	u32 ot_lim = 0;
+	u32 val;
+
+	if (!vbif || !vbif->cap) {
+		SDE_ERROR("invalid arguments vbif %d\n", vbif != 0);
+		return -EINVAL;
+	}
+
+	if (vbif->cap->default_ot_wr_limit && !params->rd)
+		ot_lim = vbif->cap->default_ot_wr_limit;
+	else if (vbif->cap->default_ot_rd_limit && params->rd)
+		ot_lim = vbif->cap->default_ot_rd_limit;
+
+	/*
+	 * If default ot is not set from dt/catalog,
+	 * then do not configure it.
+	 */
+	if (ot_lim == 0)
+		goto exit;
+
+	/* Modify the limits if the target and the use case requires it */
+	_sde_vbif_apply_dynamic_ot_limit(vbif, &ot_lim, params);
+
+	if (vbif && vbif->ops.get_limit_conf) {
+		val = vbif->ops.get_limit_conf(vbif,
+				params->xin_id, params->rd);
+		if (val == ot_lim)
+			ot_lim = 0;
+	}
+
+exit:
+	SDE_DEBUG("vbif:%d xin:%d ot_lim:%d\n",
+			vbif->idx - VBIF_0, params->xin_id, ot_lim);
+	return ot_lim;
+}
+
+/**
+ * sde_vbif_set_ot_limit - set OT based on usecase & configuration parameters
+ * @vbif:	Pointer to hardware vbif driver
+ * @params:	Pointer to usecase parameters
+ *
+ * Note this function would block waiting for bus halt.
+ */
+void sde_vbif_set_ot_limit(struct sde_kms *sde_kms,
+		struct sde_vbif_set_ot_params *params)
+{
+	struct sde_hw_vbif *vbif = NULL;
+	struct sde_hw_mdp *mdp;
+	bool forced_on = false;
+	u32 ot_lim;
+	int ret, i;
+
+	if (!sde_kms) {
+		SDE_ERROR("invalid arguments\n");
+		return;
+	}
+	mdp = sde_kms->hw_mdp;
+
+	for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) {
+		if (sde_kms->hw_vbif[i] &&
+				sde_kms->hw_vbif[i]->idx == params->vbif_idx)
+			vbif = sde_kms->hw_vbif[i];
+	}
+
+	if (!vbif || !mdp) {
+		SDE_ERROR("invalid arguments vbif %d mdp %d\n",
+				vbif != 0, mdp != 0);
+		return;
+	}
+
+	if (!mdp->ops.setup_clk_force_ctrl ||
+			!vbif->ops.set_limit_conf ||
+			!vbif->ops.set_halt_ctrl)
+		return;
+
+	ot_lim = _sde_vbif_get_ot_limit(vbif, params) & 0xFF;
+
+	if (ot_lim == 0)
+		goto exit;
+
+	trace_sde_perf_set_ot(params->num, params->xin_id, ot_lim,
+		params->vbif_idx);
+
+	forced_on = mdp->ops.setup_clk_force_ctrl(mdp, params->clk_ctrl, true);
+
+	vbif->ops.set_limit_conf(vbif, params->xin_id, params->rd, ot_lim);
+
+	vbif->ops.set_halt_ctrl(vbif, params->xin_id, true);
+
+	ret = _sde_vbif_wait_for_xin_halt(vbif, params->xin_id);
+	if (ret)
+		MSM_EVT(sde_kms->dev, vbif->idx, params->xin_id);
+
+	vbif->ops.set_halt_ctrl(vbif, params->xin_id, false);
+
+	if (forced_on)
+		mdp->ops.setup_clk_force_ctrl(mdp, params->clk_ctrl, false);
+exit:
+	return;
+}
+
 int sde_mmu_init(struct sde_kms *sde_kms)
 {
 	struct msm_mmu *mmu;
@@ -463,6 +666,7 @@
 {
 	struct msm_drm_private *priv;
 	struct sde_kms *sde_kms;
+	int i;
 	int rc;
 
 	if (!dev) {
@@ -519,6 +723,25 @@
 	if (rc)
 		goto catalog_err;
 
+	for (i = 0; i < sde_kms->catalog->vbif_count; i++) {
+		u32 vbif_idx = sde_kms->catalog->vbif[i].id;
+
+		sde_kms->hw_vbif[i] = sde_hw_vbif_init(vbif_idx,
+				sde_kms->vbif[vbif_idx], sde_kms->catalog);
+		if (IS_ERR_OR_NULL(sde_kms->hw_vbif[vbif_idx])) {
+			SDE_ERROR("failed to init vbif %d\n", vbif_idx);
+			sde_kms->hw_vbif[vbif_idx] = NULL;
+			goto catalog_err;
+		}
+	}
+
+	sde_kms->hw_mdp = sde_rm_get_mdp(&sde_kms->rm);
+	if (IS_ERR_OR_NULL(sde_kms->hw_mdp)) {
+		SDE_ERROR("failed to get hw_mdp\n");
+		sde_kms->hw_mdp = NULL;
+		goto catalog_err;
+	}
+
 	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
 
 	/*
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index 460491e..e8a6988 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -140,6 +140,9 @@
 	struct sde_irq irq_obj;
 
 	struct sde_rm rm;
+
+	struct sde_hw_vbif *hw_vbif[VBIF_MAX];
+	struct sde_hw_mdp *hw_mdp;
 };
 
 struct vsync_info {
@@ -149,6 +152,18 @@
 
 #define to_sde_kms(x) container_of(x, struct sde_kms, base)
 
+struct sde_vbif_set_ot_params {
+	u32 xin_id;
+	u32 num;
+	u32 width;
+	u32 height;
+	u32 frame_rate;
+	bool rd;
+	bool is_wfd;
+	u32 vbif_idx;
+	u32 clk_ctrl;
+};
+
 struct sde_plane_state {
 	struct drm_plane_state base;
 
@@ -618,4 +633,12 @@
  */
 void sde_encoders_init(struct drm_device *dev);
 
+/**
+ * sde_vbif_set_ot_limit - set OT limit for vbif client
+ * @sde_kms:	SDE handler
+ * @params:	Pointer to OT configuration parameters
+ */
+void sde_vbif_set_ot_limit(struct sde_kms *sde_kms,
+		struct sde_vbif_set_ot_params *params);
+
 #endif /* __sde_kms_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index a67f63d..421d031 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -407,6 +407,52 @@
 			&psde->pipe_qos_cfg);
 }
 
+/**
+ * _sde_plane_set_ot_limit - set OT limit for the given plane
+ * @plane:		Pointer to drm plane
+ * @crtc:		Pointer to drm crtc
+ */
+static void _sde_plane_set_ot_limit(struct drm_plane *plane,
+		struct drm_crtc *crtc)
+{
+	struct sde_plane *psde;
+	struct sde_vbif_set_ot_params ot_params;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+
+	if (!plane || !plane->dev || !crtc) {
+		SDE_ERROR("invalid arguments plane %d crtc %d\n",
+				plane != 0, crtc != 0);
+		return;
+	}
+
+	priv = plane->dev->dev_private;
+	if (!priv || !priv->kms) {
+		SDE_ERROR("invalid KMS reference\n");
+		return;
+	}
+
+	sde_kms = to_sde_kms(priv->kms);
+	psde = to_sde_plane(plane);
+	if (!psde->pipe_hw) {
+		SDE_ERROR("invalid pipe reference\n");
+		return;
+	}
+
+	memset(&ot_params, 0, sizeof(ot_params));
+	ot_params.xin_id = psde->pipe_hw->cap->xin_id;
+	ot_params.num = psde->pipe_hw->idx - SSPP_NONE;
+	ot_params.width = psde->pipe_cfg.src_rect.w;
+	ot_params.height = psde->pipe_cfg.src_rect.h;
+	ot_params.is_wfd = !psde->is_rt_pipe;
+	ot_params.frame_rate = crtc->mode.vrefresh;
+	ot_params.vbif_idx = VBIF_RT;
+	ot_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl;
+	ot_params.rd = true;
+
+	sde_vbif_set_ot_limit(sde_kms, &ot_params);
+}
+
 /* helper to update a state's input fence pointer from the property */
 static void _sde_plane_set_input_fence(struct drm_plane *plane,
 		struct sde_plane_state *pstate, uint64_t fd)
@@ -1115,8 +1161,10 @@
 	_sde_plane_set_qos_lut(plane, fb);
 	_sde_plane_set_danger_lut(plane, fb);
 
-	if (plane->type != DRM_PLANE_TYPE_CURSOR)
+	if (plane->type != DRM_PLANE_TYPE_CURSOR) {
 		_sde_plane_set_qos_ctrl(plane, true, SDE_PLANE_QOS_PANIC_CTRL);
+		_sde_plane_set_ot_limit(plane, crtc);
+	}
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_trace.h b/drivers/gpu/drm/msm/sde/sde_trace.h
index ba1181c..950145c 100644
--- a/drivers/gpu/drm/msm/sde/sde_trace.h
+++ b/drivers/gpu/drm/msm/sde/sde_trace.h
@@ -72,6 +72,26 @@
 			__entry->safe_lut)
 );
 
+TRACE_EVENT(sde_perf_set_ot,
+	TP_PROTO(u32 pnum, u32 xin_id, u32 rd_lim, u32 vbif_idx),
+	TP_ARGS(pnum, xin_id, rd_lim, vbif_idx),
+	TP_STRUCT__entry(
+			__field(u32, pnum)
+			__field(u32, xin_id)
+			__field(u32, rd_lim)
+			__field(u32, vbif_idx)
+	),
+	TP_fast_assign(
+			__entry->pnum = pnum;
+			__entry->xin_id = xin_id;
+			__entry->rd_lim = rd_lim;
+			__entry->vbif_idx = vbif_idx;
+	),
+	TP_printk("pnum:%d xin_id:%d ot:%d vbif:%d",
+			__entry->pnum, __entry->xin_id, __entry->rd_lim,
+			__entry->vbif_idx)
+)
+
 #endif /* _SDE_TRACE_H_ */
 
 /* This part must be outside protection */