Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a353735 authored by Ingrid Gallardo's avatar Ingrid Gallardo
Browse files

msm: mdss: add sysfs to generate the misr crc during interrupts



This change add a sysfs node to enable the misr crc generation
in batch mode. Once this is enabled, driver will collect the
crc numbers during each of the interfaces interrupt,
vsync interrupt for video mode panels and ping pong done
for cmd mode panels.

To enable/disable the crc generation, respectively:
echo 1 > /sys/class/graphics/fb0/msm_misr_en
echo 0 > /sys/class/graphics/fb0/msm_misr_en
To collect the last crc generated:
cat /sys/class/graphics/fb0/msm_misr_en
To enable the ftrace to collect the crc for all frames:
echo 1 > <debugfs>/tracing/events/mdss/mdp_misr_crc/enable

CRs-Fixed: 964076
Change-Id: I47fb47b772aef097bc9af3b58a6c79565bcdc872
Signed-off-by: default avatarIngrid Gallardo <ingridg@codeaurora.org>
parent 24788c3d
Loading
Loading
Loading
Loading
+83 −11
Original line number Diff line number Diff line
@@ -1171,6 +1171,7 @@ static struct mdss_mdp_misr_map {
	u32 value_reg;
	u32 crc_op_mode;
	u32 crc_index;
	u32 last_misr;
	bool use_ping;
	bool is_ping_full;
	bool is_pong_full;
@@ -1183,6 +1184,7 @@ static struct mdss_mdp_misr_map {
		.value_reg = MDSS_MDP_LP_MISR_SIGN_DSI0,
		.crc_op_mode = 0,
		.crc_index = 0,
		.last_misr = 0,
		.use_ping = true,
		.is_ping_full = false,
		.is_pong_full = false,
@@ -1192,6 +1194,7 @@ static struct mdss_mdp_misr_map {
		.value_reg = MDSS_MDP_LP_MISR_SIGN_DSI1,
		.crc_op_mode = 0,
		.crc_index = 0,
		.last_misr = 0,
		.use_ping = true,
		.is_ping_full = false,
		.is_pong_full = false,
@@ -1201,6 +1204,7 @@ static struct mdss_mdp_misr_map {
		.value_reg = MDSS_MDP_LP_MISR_SIGN_EDP,
		.crc_op_mode = 0,
		.crc_index = 0,
		.last_misr = 0,
		.use_ping = true,
		.is_ping_full = false,
		.is_pong_full = false,
@@ -1210,6 +1214,7 @@ static struct mdss_mdp_misr_map {
		.value_reg = MDSS_MDP_LP_MISR_SIGN_HDMI,
		.crc_op_mode = 0,
		.crc_index = 0,
		.last_misr = 0,
		.use_ping = true,
		.is_ping_full = false,
		.is_pong_full = false,
@@ -1219,6 +1224,7 @@ static struct mdss_mdp_misr_map {
		.value_reg = MDSS_MDP_LP_MISR_SIGN_MDP,
		.crc_op_mode = 0,
		.crc_index = 0,
		.last_misr = 0,
		.use_ping = true,
		.is_ping_full = false,
		.is_pong_full = false,
@@ -1226,7 +1232,8 @@ static struct mdss_mdp_misr_map {
};

static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id,
		struct mdss_mdp_ctl *ctl, struct mdss_data_type *mdata)
		struct mdss_mdp_ctl *ctl, struct mdss_data_type *mdata,
		bool is_video_mode)
{
	struct mdss_mdp_misr_map *map;
	struct mdss_mdp_mixer *mixer;
@@ -1259,7 +1266,7 @@ static inline struct mdss_mdp_misr_map *mdss_misr_get_map(u32 block_id,

				if ((block_id == DISPLAY_MISR_DSI0 ||
				     block_id == DISPLAY_MISR_DSI1) &&
				     (ctl && !ctl->is_video_mode)) {
				     !is_video_mode) {
					ctrl_reg = intf_base +
						MDSS_MDP_INTF_CMD_MISR_CTRL;
					value_reg = intf_base +
@@ -1337,6 +1344,33 @@ static bool switch_mdp_misr_offset(struct mdss_mdp_misr_map *map, u32 mdp_rev,
	return use_mdp_up_misr;
}

void mdss_misr_disable(struct mdss_data_type *mdata,
			struct mdp_misr *req,
			struct mdss_mdp_ctl *ctl)
{
	struct mdss_mdp_misr_map *map;

	map = mdss_misr_get_map(req->block_id, ctl, mdata,
		ctl->is_video_mode);

	/* clear the map data */
	memset(map->crc_ping, 0, sizeof(map->crc_ping));
	memset(map->crc_pong, 0, sizeof(map->crc_pong));
	map->crc_index = 0;
	map->use_ping = true;
	map->is_ping_full = false;
	map->is_pong_full = false;
	map->crc_op_mode = 0;
	map->last_misr = 0;

	/* disable MISR and clear the status */
	writel_relaxed(MDSS_MDP_MISR_CTRL_STATUS_CLEAR,
			mdata->mdp_base + map->ctrl_reg);

	/* make sure status is clear */
	wmb();
}

int mdss_misr_set(struct mdss_data_type *mdata,
			struct mdp_misr *req,
			struct mdss_mdp_ctl *ctl)
@@ -1351,7 +1385,8 @@ int mdss_misr_set(struct mdss_data_type *mdata,
	pr_debug("req[block:%d frame:%d op_mode:%d]\n",
		req->block_id, req->frame_count, req->crc_op_mode);

	map = mdss_misr_get_map(req->block_id, ctl, mdata);
	map = mdss_misr_get_map(req->block_id, ctl, mdata,
		ctl->is_video_mode);

	if (!map) {
		pr_err("Invalid MISR Block=%d\n", req->block_id);
@@ -1447,7 +1482,8 @@ char *get_misr_block_name(int misr_block_id)

int mdss_misr_get(struct mdss_data_type *mdata,
			struct mdp_misr *resp,
			struct mdss_mdp_ctl *ctl)
			struct mdss_mdp_ctl *ctl,
			bool is_video_mode)
{
	struct mdss_mdp_misr_map *map;
	struct mdss_mdp_mixer *mixer;
@@ -1458,7 +1494,8 @@ int mdss_misr_get(struct mdss_data_type *mdata,
	pr_debug("req[block:%d frame:%d op_mode:%d]\n",
		resp->block_id, resp->frame_count, resp->crc_op_mode);

	map = mdss_misr_get_map(resp->block_id, ctl, mdata);
	map = mdss_misr_get_map(resp->block_id, ctl, mdata,
		is_video_mode);
	if (!map) {
		pr_err("Invalid MISR Block=%d\n", resp->block_id);
		return -EINVAL;
@@ -1534,31 +1571,37 @@ int mdss_misr_get(struct mdss_data_type *mdata,
}

/* This function is expected to be called from interrupt context */
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id)
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id,
	bool is_video_mode)
{
	struct mdss_mdp_misr_map *map;
	u32 status = 0;
	u32 crc = 0x0BAD0BAD;
	bool crc_stored = false;

	map = mdss_misr_get_map(block_id, NULL, mdata);
	map = mdss_misr_get_map(block_id, NULL, mdata, is_video_mode);
	if (!map || (map->crc_op_mode != MISR_OP_BM))
		return;

	switch_mdp_misr_offset(map, mdata->mdp_rev, block_id);

	status = readl_relaxed(mdata->mdp_base + map->ctrl_reg);

	if (MDSS_MDP_MISR_CTRL_STATUS & status) {

		crc = readl_relaxed(mdata->mdp_base + map->value_reg);
		map->last_misr = crc; /* cache crc to get it from sysfs */

		if (map->use_ping) {
			if (map->is_ping_full) {
				pr_err("PING Buffer FULL\n");
				pr_err_once("PING Buffer FULL\n");
			} else {
				map->crc_ping[map->crc_index] = crc;
				crc_stored = true;
			}
		} else {
			if (map->is_pong_full) {
				pr_err("PONG Buffer FULL\n");
				pr_err_once("PONG Buffer FULL\n");
			} else {
				map->crc_pong[map->crc_index] = crc;
				crc_stored = true;
@@ -1584,7 +1627,7 @@ void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id)
					map->is_pong_full ? "FULL" : "EMPTRY");
			}
		} else {
			pr_err("CRC(%d) Not saved\n", crc);
			pr_err_once("CRC(%d) Not saved\n", crc);
		}

		if (mdata->mdp_rev < MDSS_MDP_HW_REV_105) {
@@ -1593,7 +1636,9 @@ void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id)
			writel_relaxed(MISR_CRC_BATCH_CFG,
				mdata->mdp_base + map->ctrl_reg);
		}

	} else if (0 == status) {

		if (mdata->mdp_rev < MDSS_MDP_HW_REV_105)
			writel_relaxed(MISR_CRC_BATCH_CFG,
					mdata->mdp_base + map->ctrl_reg);
@@ -1601,15 +1646,42 @@ void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id)
			writel_relaxed(MISR_CRC_BATCH_CFG |
					MDSS_MDP_LP_MISR_CTRL_FREE_RUN_MASK,
					mdata->mdp_base + map->ctrl_reg);

		pr_debug("$$ Batch CRC Start $$\n");
	}

	pr_debug("$$ Vsync Count = %d, CRC=0x%x Indx = %d$$\n",
		vsync_count, crc, map->crc_index);
	trace_mdp_misr_crc(block_id, vsync_count, crc);

	if (MAX_VSYNC_COUNT == vsync_count) {
		pr_err("RESET vsync_count(%d)\n", vsync_count);
		pr_debug("RESET vsync_count(%d)\n", vsync_count);
		vsync_count = 0;
	} else {
		vsync_count += 1;
	}

}

int mdss_dump_misr_data(char **buf, u32 size)
{
	struct mdss_mdp_misr_map  *dsi0_map;
	struct mdss_mdp_misr_map  *dsi1_map;
	struct mdss_mdp_misr_map  *hdmi_map;
	int ret;

	dsi0_map = &mdss_mdp_misr_table[DISPLAY_MISR_DSI0];
	dsi1_map = &mdss_mdp_misr_table[DISPLAY_MISR_DSI1];
	hdmi_map = &mdss_mdp_misr_table[DISPLAY_MISR_HDMI];

	ret = scnprintf(*buf, PAGE_SIZE,
			"\tDSI0 mode:%02d MISR:0x%08x\n"
			"\tDSI1 mode:%02d MISR:0x%08x\n"
			"\tHDMI mode:%02d MISR:0x%08x\n",
			dsi0_map->crc_op_mode, dsi0_map->last_misr,
			dsi1_map->crc_op_mode, dsi1_map->last_misr,
			hdmi_map->crc_op_mode, hdmi_map->last_misr
			);

	return ret;
}
+21 −6
Original line number Diff line number Diff line
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-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
@@ -146,11 +146,18 @@ void mdss_debug_register_dump_range(struct platform_device *pdev,
	const char *name_prop);
int panel_debug_register_base(const char *name, void __iomem *base,
				    size_t max_offset);
int mdss_misr_set(struct mdss_data_type *mdata, struct mdp_misr *req,
int mdss_misr_set(struct mdss_data_type *mdata,
			struct mdp_misr *req,
			struct mdss_mdp_ctl *ctl);
int mdss_misr_get(struct mdss_data_type *mdata, struct mdp_misr *resp,
int mdss_misr_get(struct mdss_data_type *mdata,
			struct mdp_misr *resp,
			struct mdss_mdp_ctl *ctl,
			bool is_video_mode);
void mdss_misr_disable(struct mdss_data_type *mdata,
			struct mdp_misr *req,
			struct mdss_mdp_ctl *ctl);
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id);
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id,
	bool is_video_mode);

int mdss_create_xlog_debug(struct mdss_debug_data *mdd);
void mdss_xlog(const char *name, int line, int flag, ...);
@@ -178,10 +185,16 @@ static inline int mdss_misr_set(struct mdss_data_type *mdata,
{ return 0; }
static inline int mdss_misr_get(struct mdss_data_type *mdata,
					struct mdp_misr *resp,
					struct mdss_mdp_ctl *ctl)
					struct mdss_mdp_ctl *ctl,
					bool is_video_mode)
{ return 0; }
static inline void mdss_misr_disable(struct mdss_data_type *mdata,
					struct mdp_misr *req,
					struct mdss_mdp_ctl *ctl)
{ return; }

static inline void mdss_misr_crc_collect(struct mdss_data_type *mdata,
						int block_id) { }
					int block_id, bool is_video_mode) { }

static inline int create_xlog_debug(struct mdss_data_type *mdata) { return 0; }
static inline void mdss_xlog_dump(void) { }
@@ -192,6 +205,8 @@ static inline void mdss_xlog_tout_handler_default(bool queue,
	const char *name, ...) { }
#endif

int mdss_dump_misr_data(char **buf, u32 size);

static inline int mdss_debug_register_io(const char *name,
		struct dss_io_data *io_data, struct mdss_debug_base **dbg_blk)
{
+103 −1
Original line number Diff line number Diff line
@@ -3114,6 +3114,106 @@ static ssize_t mdss_mdp_cmd_autorefresh_store(struct device *dev,
}


/* Print the last CRC Value read for batch mode */
static ssize_t mdss_mdp_misr_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	ssize_t ret = 0;
	struct fb_info *fbi = dev_get_drvdata(dev);
	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
	struct mdss_mdp_ctl *ctl;

	if (!mfd) {
		pr_err("Invalid mfd structure\n");
		return -EINVAL;
	}

	ctl = mfd_to_ctl(mfd);
	if (!ctl) {
		pr_err("Invalid ctl structure\n");
		return -EINVAL;
	}

	ret = mdss_dump_misr_data(&buf, PAGE_SIZE);

	return ret;
}

/*
 * Enable crc batch mode. By enabling this mode through sysfs
 * driver will keep collecting the misr in ftrace during interrupts,
 * until disabled.
 */
static ssize_t mdss_mdp_misr_store(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t len)
{
	int enable_misr, rc;
	struct fb_info *fbi = dev_get_drvdata(dev);
	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();
	struct mdss_mdp_ctl *ctl;
	struct mdp_misr req, sreq;

	if (!mfd) {
		pr_err("Invalid mfd structure\n");
		rc = -EINVAL;
		return rc;
	}

	ctl = mfd_to_ctl(mfd);
	if (!ctl) {
		pr_err("Invalid ctl structure\n");
		rc = -EINVAL;
		return rc;
	}

	rc = kstrtoint(buf, 10, &enable_misr);
	if (rc) {
		pr_err("kstrtoint failed. rc=%d\n", rc);
		return rc;
	}

	pr_debug("intf_type:%d enable:%d\n", ctl->intf_type, enable_misr);
	if (ctl->intf_type == MDSS_INTF_DSI) {

		req.block_id = DISPLAY_MISR_DSI0;
		req.crc_op_mode = MISR_OP_BM;
		req.frame_count = 1;
		if (is_panel_split(mfd)) {

			sreq.block_id = DISPLAY_MISR_DSI1;
			sreq.crc_op_mode = MISR_OP_BM;
			sreq.frame_count = 1;
		}
	} else if (ctl->intf_type == MDSS_INTF_HDMI) {

		req.block_id = DISPLAY_MISR_HDMI;
		req.crc_op_mode = MISR_OP_BM;
		req.frame_count = 1;
	} else {
		pr_err("misr not supported fo this fb:%d\n", mfd->index);
	}

	if (enable_misr) {
		mdss_misr_set(mdata, &req , ctl);

		if (is_panel_split(mfd))
			mdss_misr_set(mdata, &sreq , ctl);

	} else {
		mdss_misr_disable(mdata, &req, ctl);

		if (is_panel_split(mfd))
			mdss_misr_disable(mdata, &sreq , ctl);
	}

	pr_debug("misr %s\n", enable_misr ? "enabled" : "disabled");

	return len;
}

static DEVICE_ATTR(msm_misr_en, S_IRUGO | S_IWUSR,
	mdss_mdp_misr_show, mdss_mdp_misr_store);
static DEVICE_ATTR(msm_cmd_autorefresh_en, S_IRUGO | S_IWUSR,
	mdss_mdp_cmd_autorefresh_show, mdss_mdp_cmd_autorefresh_store);
static DEVICE_ATTR(vsync_event, S_IRUGO, mdss_mdp_vsync_show_event, NULL);
@@ -3130,6 +3230,7 @@ static struct attribute *mdp_overlay_sysfs_attrs[] = {
	&dev_attr_vsync_event.attr,
	&dev_attr_ad.attr,
	&dev_attr_dyn_pu.attr,
	&dev_attr_msm_misr_en.attr,
	&dev_attr_msm_cmd_autorefresh_en.attr,
	&dev_attr_hist_event.attr,
	&dev_attr_bl_event.attr,
@@ -3954,7 +4055,8 @@ static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd,
		ctl = mfd_to_ctl(mfd);
		if (!ctl || mdss_fb_is_power_off(mfd))
			return -EPERM;
		ret = mdss_misr_get(mdata, &metadata->data.misr_request, ctl);
		ret = mdss_misr_get(mdata, &metadata->data.misr_request, ctl,
			ctl->is_video_mode);
		break;
	default:
		pr_warn("Unsupported request to MDP META IOCTL.\n");
+18 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
 * Copyright (c) 2014-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
@@ -295,6 +295,23 @@ TRACE_EVENT(mdp_perf_update_bus,
			__entry->ib_quota)
);

TRACE_EVENT(mdp_misr_crc,
	TP_PROTO(u32 block_id, u32 vsync_cnt, u32 crc),
	TP_ARGS(block_id, vsync_cnt, crc),
	TP_STRUCT__entry(
			__field(u32, block_id)
			__field(u32, vsync_cnt)
			__field(u32, crc)
	),
	TP_fast_assign(
			__entry->block_id = block_id;
			__entry->vsync_cnt = vsync_cnt;
			__entry->crc = crc;
	),
	TP_printk("block_id:%d vsync_cnt:%d crc:0x%08x",
			__entry->block_id, __entry->vsync_cnt, __entry->crc)
);

TRACE_EVENT(mdp_cmd_pingpong_done,
	TP_PROTO(struct mdss_mdp_ctl *ctl, u32 pp_num, int koff_cnt),
	TP_ARGS(ctl, pp_num, koff_cnt),
+13 −9
Original line number Diff line number Diff line
@@ -188,11 +188,15 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr)
	if (isr & MDSS_MDP_INTR_INTF_3_UNDERRUN)
		mdss_mdp_intr_done(MDP_INTR_UNDERRUN_INTF_3);

	if (isr & MDSS_MDP_INTR_PING_PONG_0_DONE)
	if (isr & MDSS_MDP_INTR_PING_PONG_0_DONE) {
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_0);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI0, false);
	}

	if (isr & MDSS_MDP_INTR_PING_PONG_1_DONE)
	if (isr & MDSS_MDP_INTR_PING_PONG_1_DONE) {
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_1);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI1, false);
	}

	if (isr & MDSS_MDP_INTR_PING_PONG_2_DONE)
		mdss_mdp_intr_done(MDP_INTR_PING_PONG_2);
@@ -214,38 +218,38 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr)

	if (isr & MDSS_MDP_INTR_INTF_0_VSYNC) {
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_EDP);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_EDP, true);
	}

	if (isr & MDSS_MDP_INTR_INTF_1_VSYNC) {
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_1);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI0);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI0, true);
	}

	if (isr & MDSS_MDP_INTR_INTF_2_VSYNC) {
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_2);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI1);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_DSI1, true);
	}

	if (isr & MDSS_MDP_INTR_INTF_3_VSYNC) {
		mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_3);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_HDMI);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_HDMI, true);
	}

	if (isr & MDSS_MDP_INTR_WB_0_DONE) {
		mdss_mdp_intr_done(MDP_INTR_WB_0);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_MDP);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_MDP, true);
	}

	if (isr & MDSS_MDP_INTR_WB_1_DONE) {
		mdss_mdp_intr_done(MDP_INTR_WB_1);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_MDP);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_MDP, true);
	}

	if (isr & ((mdata->mdp_rev == MDSS_MDP_HW_REV_108) ?
		MDSS_MDP_INTR_WB_2_DONE >> 2 : MDSS_MDP_INTR_WB_2_DONE)) {
		mdss_mdp_intr_done(MDP_INTR_WB_2);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_MDP);
		mdss_misr_crc_collect(mdata, DISPLAY_MISR_MDP, true);
	}

	if (isr & MDSS_MDP_INTR_PING_PONG_0_AUTOREFRESH_DONE)