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

Commit 8644342a authored by Krishna Manikandan's avatar Krishna Manikandan Committed by Gerrit - the friendly Code Review server
Browse files

msm: mdss: Add support for sg_table cloning



Add support for cloning of the sg_table and
padding it with extra buffer to avoid faults
which can occur as a result of a hardware bug
that over-fetches.

Change-Id: I6f33c0568bab4f97aa6ed063bdd8b898723571d6
Signed-off-by: default avatarFranklin S Cooper Jr <fcooper@ti.com>
Signed-off-by: default avatarKrishna Manikandan <mkrishn@codeaurora.org>
parent 5b68799e
Loading
Loading
Loading
Loading
+44 −6
Original line number Diff line number Diff line
@@ -1834,9 +1834,13 @@ int mdp3_put_img(struct mdp3_img_data *data, int client)
			return -ENOMEM;
		}
		if (data->mapped) {
			if (client == MDP3_CLIENT_PPP ||
						client == MDP3_CLIENT_DMA_P)
				mdss_smmu_unmap_dma_buf(data->tab_clone,
					dom, dir, data->srcp_dma_buf);
			else
				mdss_smmu_unmap_dma_buf(data->srcp_table,
						dom, dir,
					data->srcp_dma_buf);
					dom, dir, data->srcp_dma_buf);
			data->mapped = false;
		}
		if (!data->skip_detach) {
@@ -1851,6 +1855,10 @@ int mdp3_put_img(struct mdp3_img_data *data, int client)
	} else {
		return -EINVAL;
	}
	if (client == MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) {
		kfree(data->tab_clone->sgl);
		kfree(data->tab_clone);
	}
	return 0;
}

@@ -1913,9 +1921,27 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client)
				goto err_detach;
			}

			if (client == MDP3_CLIENT_PPP ||
						client == MDP3_CLIENT_DMA_P) {
				data->tab_clone =
				mdss_smmu_sg_table_clone(data->srcp_table,
							GFP_KERNEL, true);
				if (IS_ERR_OR_NULL(data->tab_clone)) {
					if (!(data->tab_clone))
						ret = -EINVAL;
					else
						ret = PTR_ERR(data->tab_clone);
					goto clone_err;
				}
				ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf,
					data->tab_clone, dom,
					&data->addr, &data->len,
					DMA_BIDIRECTIONAL);
			} else {
				ret = mdss_smmu_map_dma_buf(data->srcp_dma_buf,
					data->srcp_table, dom,
				&data->addr, &data->len, DMA_BIDIRECTIONAL);
					data->srcp_table, dom, &data->addr,
					&data->len, DMA_BIDIRECTIONAL);
			}

			if (IS_ERR_VALUE(ret)) {
				pr_err("smmu map dma buf failed: (%d)\n", ret);
@@ -1926,6 +1952,10 @@ int mdp3_get_img(struct msmfb_data *img, struct mdp3_img_data *data, int client)
		data->skip_detach = false;
	}
done:
	if (client ==  MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) {
		data->addr  += data->tab_clone->sgl->length;
		data->len   -= data->tab_clone->sgl->length;
	}
	if (!ret && (img->offset < data->len)) {
		data->addr += img->offset;
		data->len -= img->offset;
@@ -1940,6 +1970,9 @@ done:
	}
	return ret;

clone_err:
	dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table,
		mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL));
err_detach:
	dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
err_put:
@@ -1950,6 +1983,11 @@ err_unmap:
			mdss_smmu_dma_data_direction(DMA_BIDIRECTIONAL));
	dma_buf_detach(data->srcp_dma_buf, data->srcp_attachment);
	dma_buf_put(data->srcp_dma_buf);

	if (client ==  MDP3_CLIENT_PPP || client == MDP3_CLIENT_DMA_P) {
		kfree(data->tab_clone->sgl);
		kfree(data->tab_clone);
	}
	return ret;

}
+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved.
 * Copyright (C) 2007 Google Incorporated
 *
 * This program is free software; you can redistribute it and/or modify
@@ -233,6 +233,7 @@ struct mdp3_img_data {
	struct dma_buf *srcp_dma_buf;
	struct dma_buf_attachment *srcp_attachment;
	struct sg_table *srcp_table;
	struct sg_table *tab_clone;
};

extern struct mdp3_hw_resource *mdp3_res;
+2 −0
Original line number Diff line number Diff line
@@ -265,6 +265,8 @@ struct mdss_smmu_ops {
	void (*smmu_dsi_unmap_buffer)(dma_addr_t dma_addr, int domain,
			unsigned long size, int dir);
	void (*smmu_deinit)(struct mdss_data_type *mdata);
	struct sg_table * (*smmu_sg_table_clone)(struct sg_table *orig_table,
			gfp_t gfp_mask, bool padding);
};

struct mdss_data_type {
+130 −1
Original line number Diff line number Diff line
/* Copyright (c) 2007-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2007-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
@@ -480,6 +480,134 @@ static void mdss_smmu_deinit_v2(struct mdss_data_type *mdata)
	}
}

/*
 * sg_clone -	Duplicate an existing chained sgl
 * @orig_sgl:	Original sg list to be duplicated
 * @len:	Total length of sg while taking chaining into account
 * @gfp_mask:	GFP allocation mask
 * @padding:	specifies if padding is required
 *
 * Description:
 *   Clone a chained sgl. This cloned copy may be modified in some ways while
 *   keeping the original sgl in tact. Also allow the cloned copy to have
 *   a smaller length than the original which may reduce the sgl total
 *   sg entries and also allows cloned copy to have one extra sg  entry on
 *   either sides of sgl.
 *
 * Returns:
 *   Pointer to new kmalloced sg list, ERR_PTR() on error
 *
 */
static struct scatterlist *sg_clone(struct scatterlist *orig_sgl, u64 len,
				gfp_t gfp_mask, bool padding)
{
	int nents;
	bool last_entry;
	struct scatterlist *sgl, *head;

	nents = sg_nents(orig_sgl);
	if (nents < 0)
		return ERR_PTR(-EINVAL);
	if (padding)
		nents += 2;

	head = kmalloc_array(nents, sizeof(struct scatterlist), gfp_mask);
	if (!head)
		return ERR_PTR(-ENOMEM);

	sgl = head;

	sg_init_table(sgl, nents);

	if (padding) {
		*sgl = *orig_sgl;
		if (sg_is_chain(orig_sgl)) {
			orig_sgl = sg_next(orig_sgl);
			*sgl = *orig_sgl;
		}
		sgl->page_link &= (unsigned long)(~0x03);
		sgl = sg_next(sgl);
	}

	for (; sgl; orig_sgl = sg_next(orig_sgl), sgl = sg_next(sgl)) {

		last_entry = sg_is_last(sgl);

		/*
		 * * If page_link is pointing to a chained sgl then set
		 * the sg entry in the cloned list to the next sg entry
		 * in the original sg list as chaining is already taken
		 * care.
		 */

		if (sg_is_chain(orig_sgl))
			orig_sgl = sg_next(orig_sgl);

		if (padding)
			last_entry = sg_is_last(orig_sgl);

		*sgl = *orig_sgl;
		sgl->page_link &= (unsigned long)(~0x03);

		if (last_entry) {
			if (padding) {
				len -= sg_dma_len(sgl);
				sgl = sg_next(sgl);
				*sgl = *orig_sgl;
			}
			sg_dma_len(sgl) = len ? len : SZ_4K;
			/* Set bit 1 to indicate end of sgl */
			sgl->page_link |= 0x02;
		} else {
			len -= sg_dma_len(sgl);
		}
	}

	return head;
}

/*
 * sg_table_clone - Duplicate an existing sg_table including chained sgl
 * @orig_table:     Original sg_table to be duplicated
 * @len:            Total length of sg while taking chaining into account
 * @gfp_mask:       GFP allocation mask
 * @padding:	    specifies if padding is required
 *
 * Description:
 *   Clone a sg_table along with chained sgl. This cloned copy may be
 *   modified in some ways while keeping the original table and sgl in tact.
 *   Also allow the cloned sgl copy to have a smaller length than the original
 *   which may reduce the sgl total sg entries.
 *
 * Returns:
 *   Pointer to new kmalloced sg_table, ERR_PTR() on error
 *
 */
static struct sg_table *sg_table_clone(struct sg_table *orig_table,
				gfp_t gfp_mask, bool padding)
{
	struct sg_table *table;
	struct scatterlist *sg = orig_table->sgl;
	u64 len = 0;

	for (len = 0; sg; sg = sg_next(sg))
		len += sg->length;

	table = kmalloc(sizeof(struct sg_table), gfp_mask);
	if (!table)
		return ERR_PTR(-ENOMEM);

	table->sgl = sg_clone(orig_table->sgl, len, gfp_mask, padding);
	if (IS_ERR(table->sgl)) {
		kfree(table);
		return ERR_PTR(-ENOMEM);
	}

	table->nents = table->orig_nents = sg_nents(table->sgl);

	return table;
}

static void mdss_smmu_ops_init(struct mdss_data_type *mdata)
{
	mdata->smmu_ops.smmu_attach = mdss_smmu_attach_v2;
@@ -501,6 +629,7 @@ static void mdss_smmu_ops_init(struct mdss_data_type *mdata)
	mdata->smmu_ops.smmu_dsi_unmap_buffer =
				mdss_smmu_dsi_unmap_buffer_v2;
	mdata->smmu_ops.smmu_deinit = mdss_smmu_deinit_v2;
	mdata->smmu_ops.smmu_sg_table_clone = sg_table_clone;
}

/*
+13 −1
Original line number Diff line number Diff line
/* Copyright (c) 2007-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2007-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
@@ -292,4 +292,16 @@ static inline void mdss_smmu_deinit(struct mdss_data_type *mdata)
		mdata->smmu_ops.smmu_deinit(mdata);
}

static inline struct sg_table *mdss_smmu_sg_table_clone(struct sg_table
			*orig_table, gfp_t gfp_mask, bool padding)
{
	struct mdss_data_type *mdata = mdss_mdp_get_mdata();

	if (!mdata || !mdata->smmu_ops.smmu_sg_table_clone)
		return NULL;

	return mdata->smmu_ops.smmu_sg_table_clone(orig_table,
				gfp_mask, padding);
}

#endif /* MDSS_SMMU_H */