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

Commit cdb2f282 authored by Alan Kwong's avatar Alan Kwong
Browse files

drm/msm/sde: add crtc per-commit rotator resource management



Move rotator resource management from plane state to crtc
state so that rotator can be migrated between planes in
back-to-back commits.

A per-commit resource pool is maintained in crtc state. Local
resources, such as framebuffer or framebffer object, as well
as global hardware block resource can be added to crtc resource
pool on per commit basis. Resources are automatically migrated
during state duplication. Any unused resources will be reclaimed
at the end of atomic check and returned to originated pool.

Resource spanning across multiple crtc commits are still managed
by global hardware block resource pool.

CRs-Fixed: 2009714
Change-Id: Ifdbbbf707ebe7bbb5cb1a84d93e22f0eb5eadbf1
Signed-off-by: default avatarAlan Kwong <akwong@codeaurora.org>
parent 7c1b4489
Loading
Loading
Loading
Loading
+390 −3
Original line number Diff line number Diff line
@@ -117,6 +117,374 @@ static inline int _sde_crtc_power_enable(struct sde_crtc *sde_crtc, bool enable)
									enable);
}

/**
 * _sde_crtc_rp_to_crtc - get crtc from resource pool object
 * @rp: Pointer to resource pool
 * return: Pointer to drm crtc if success; null otherwise
 */
static struct drm_crtc *_sde_crtc_rp_to_crtc(struct sde_crtc_respool *rp)
{
	if (!rp)
		return NULL;

	return container_of(rp, struct sde_crtc_state, rp)->base.crtc;
}

/**
 * _sde_crtc_rp_reclaim - reclaim unused, or all if forced, resources in pool
 * @rp: Pointer to resource pool
 * @force: True to reclaim all resources; otherwise, reclaim only unused ones
 * return: None
 */
static void _sde_crtc_rp_reclaim(struct sde_crtc_respool *rp, bool force)
{
	struct sde_crtc_res *res, *next;
	struct drm_crtc *crtc;

	crtc = _sde_crtc_rp_to_crtc(rp);
	if (!crtc) {
		SDE_ERROR("invalid crtc\n");
		return;
	}

	SDE_DEBUG("crtc%d.%u %s\n", crtc->base.id, rp->sequence_id,
			force ? "destroy" : "free_unused");

	list_for_each_entry_safe(res, next, &rp->res_list, list) {
		if (!force && !(res->flags & SDE_CRTC_RES_FLAG_FREE))
			continue;
		SDE_DEBUG("crtc%d.%u reclaim res:0x%x/0x%llx/%pK/%d\n",
				crtc->base.id, rp->sequence_id,
				res->type, res->tag, res->val,
				atomic_read(&res->refcount));
		list_del(&res->list);
		if (res->ops.put)
			res->ops.put(res->val);
		kfree(res);
	}
}

/**
 * _sde_crtc_rp_free_unused - free unused resource in pool
 * @rp: Pointer to resource pool
 * return: none
 */
static void _sde_crtc_rp_free_unused(struct sde_crtc_respool *rp)
{
	_sde_crtc_rp_reclaim(rp, false);
}

/**
 * _sde_crtc_rp_destroy - destroy resource pool
 * @rp: Pointer to resource pool
 * return: None
 */
static void _sde_crtc_rp_destroy(struct sde_crtc_respool *rp)
{
	_sde_crtc_rp_reclaim(rp, true);
}

/**
 * _sde_crtc_hw_blk_get - get callback for hardware block
 * @val: Resource handle
 * @type: Resource type
 * @tag: Search tag for given resource
 * return: Resource handle
 */
static void *_sde_crtc_hw_blk_get(void *val, u32 type, u64 tag)
{
	SDE_DEBUG("res:%d/0x%llx/%pK\n", type, tag, val);
	return sde_hw_blk_get(val, type, tag);
}

/**
 * _sde_crtc_hw_blk_put - put callback for hardware block
 * @val: Resource handle
 * return: None
 */
static void _sde_crtc_hw_blk_put(void *val)
{
	SDE_DEBUG("res://%pK\n", val);
	sde_hw_blk_put(val);
}

/**
 * _sde_crtc_rp_duplicate - duplicate resource pool and reset reference count
 * @rp: Pointer to original resource pool
 * @dup_rp: Pointer to duplicated resource pool
 * return: None
 */
static void _sde_crtc_rp_duplicate(struct sde_crtc_respool *rp,
		struct sde_crtc_respool *dup_rp)
{
	struct sde_crtc_res *res, *dup_res;
	struct drm_crtc *crtc;

	if (!rp || !dup_rp) {
		SDE_ERROR("invalid resource pool\n");
		return;
	}

	crtc = _sde_crtc_rp_to_crtc(rp);
	if (!crtc) {
		SDE_ERROR("invalid crtc\n");
		return;
	}

	SDE_DEBUG("crtc%d.%u duplicate\n", crtc->base.id, rp->sequence_id);

	dup_rp->sequence_id = rp->sequence_id + 1;
	INIT_LIST_HEAD(&dup_rp->res_list);
	dup_rp->ops = rp->ops;
	list_for_each_entry(res, &rp->res_list, list) {
		dup_res = kzalloc(sizeof(struct sde_crtc_res), GFP_KERNEL);
		if (!dup_res)
			return;
		INIT_LIST_HEAD(&dup_res->list);
		atomic_set(&dup_res->refcount, 0);
		dup_res->type = res->type;
		dup_res->tag = res->tag;
		dup_res->val = res->val;
		dup_res->ops = res->ops;
		dup_res->flags = SDE_CRTC_RES_FLAG_FREE;
		SDE_DEBUG("crtc%d.%u dup res:0x%x/0x%llx/%pK/%d\n",
				crtc->base.id, dup_rp->sequence_id,
				dup_res->type, dup_res->tag, dup_res->val,
				atomic_read(&dup_res->refcount));
		list_add_tail(&dup_res->list, &dup_rp->res_list);
		if (dup_res->ops.get)
			dup_res->ops.get(dup_res->val, 0, -1);
	}
}

/**
 * _sde_crtc_rp_reset - reset resource pool after allocation
 * @rp: Pointer to original resource pool
 * return: None
 */
static void _sde_crtc_rp_reset(struct sde_crtc_respool *rp)
{
	if (!rp) {
		SDE_ERROR("invalid resource pool\n");
		return;
	}

	rp->sequence_id = 0;
	INIT_LIST_HEAD(&rp->res_list);
	rp->ops.get = _sde_crtc_hw_blk_get;
	rp->ops.put = _sde_crtc_hw_blk_put;
}

/**
 * _sde_crtc_rp_add - add given resource to resource pool
 * @rp: Pointer to original resource pool
 * @type: Resource type
 * @tag: Search tag for given resource
 * @val: Resource handle
 * @ops: Resource callback operations
 * return: 0 if success; error code otherwise
 */
static int _sde_crtc_rp_add(struct sde_crtc_respool *rp, u32 type, u64 tag,
		void *val, struct sde_crtc_res_ops *ops)
{
	struct sde_crtc_res *res;
	struct drm_crtc *crtc;

	if (!rp || !ops) {
		SDE_ERROR("invalid resource pool/ops\n");
		return -EINVAL;
	}

	crtc = _sde_crtc_rp_to_crtc(rp);
	if (!crtc) {
		SDE_ERROR("invalid crtc\n");
		return -EINVAL;
	}

	list_for_each_entry(res, &rp->res_list, list) {
		if (res->type != type || res->tag != tag)
			continue;
		SDE_ERROR("crtc%d.%u already exist res:0x%x/0x%llx/%pK/%d\n",
				crtc->base.id, rp->sequence_id,
				res->type, res->tag, res->val,
				atomic_read(&res->refcount));
		return -EEXIST;
	}
	res = kzalloc(sizeof(struct sde_crtc_res), GFP_KERNEL);
	if (!res)
		return -ENOMEM;
	INIT_LIST_HEAD(&res->list);
	atomic_set(&res->refcount, 1);
	res->type = type;
	res->tag = tag;
	res->val = val;
	res->ops = *ops;
	list_add_tail(&res->list, &rp->res_list);
	SDE_DEBUG("crtc%d.%u added res:0x%x/0x%llx\n",
			crtc->base.id, rp->sequence_id, type, tag);
	return 0;
}

/**
 * _sde_crtc_rp_get - lookup the resource from given resource pool and obtain
 *	if available; otherwise, obtain resource from global pool
 * @rp: Pointer to original resource pool
 * @type: Resource type
 * @tag:  Search tag for given resource
 * return: Resource handle if success; pointer error or null otherwise
 */
static void *_sde_crtc_rp_get(struct sde_crtc_respool *rp, u32 type, u64 tag)
{
	struct sde_crtc_res *res;
	void *val = NULL;
	int rc;
	struct drm_crtc *crtc;

	if (!rp) {
		SDE_ERROR("invalid resource pool\n");
		return NULL;
	}

	crtc = _sde_crtc_rp_to_crtc(rp);
	if (!crtc) {
		SDE_ERROR("invalid crtc\n");
		return NULL;
	}

	list_for_each_entry(res, &rp->res_list, list) {
		if (res->type != type || res->tag != tag)
			continue;
		SDE_DEBUG("crtc%d.%u found res:0x%x/0x%llx/%pK/%d\n",
				crtc->base.id, rp->sequence_id,
				res->type, res->tag, res->val,
				atomic_read(&res->refcount));
		atomic_inc(&res->refcount);
		res->flags &= ~SDE_CRTC_RES_FLAG_FREE;
		return res->val;
	}
	list_for_each_entry(res, &rp->res_list, list) {
		if (res->type != type || !(res->flags & SDE_CRTC_RES_FLAG_FREE))
			continue;
		SDE_DEBUG("crtc%d.%u retag res:0x%x/0x%llx/%pK/%d\n",
				crtc->base.id, rp->sequence_id,
				res->type, res->tag, res->val,
				atomic_read(&res->refcount));
		atomic_inc(&res->refcount);
		res->tag = tag;
		res->flags &= ~SDE_CRTC_RES_FLAG_FREE;
		return res->val;
	}
	if (rp->ops.get)
		val = rp->ops.get(NULL, type, -1);
	if (IS_ERR_OR_NULL(val)) {
		SDE_ERROR("crtc%d.%u failed to get res:0x%x//\n",
				crtc->base.id, rp->sequence_id, type);
		return NULL;
	}
	rc = _sde_crtc_rp_add(rp, type, tag, val, &rp->ops);
	if (rc) {
		SDE_ERROR("crtc%d.%u failed to add res:0x%x/0x%llx\n",
				crtc->base.id, rp->sequence_id, type, tag);
		if (rp->ops.put)
			rp->ops.put(val);
		val = NULL;
	}
	return val;
}

/**
 * _sde_crtc_rp_put - return given resource to resource pool
 * @rp: Pointer to original resource pool
 * @type: Resource type
 * @tag: Search tag for given resource
 * return: None
 */
static void _sde_crtc_rp_put(struct sde_crtc_respool *rp, u32 type, u64 tag)
{
	struct sde_crtc_res *res, *next;
	struct drm_crtc *crtc;

	if (!rp) {
		SDE_ERROR("invalid resource pool\n");
		return;
	}

	crtc = _sde_crtc_rp_to_crtc(rp);
	if (!crtc) {
		SDE_ERROR("invalid crtc\n");
		return;
	}

	list_for_each_entry_safe(res, next, &rp->res_list, list) {
		if (res->type != type || res->tag != tag)
			continue;
		SDE_DEBUG("crtc%d.%u found res:0x%x/0x%llx/%pK/%d\n",
				crtc->base.id, rp->sequence_id,
				res->type, res->tag, res->val,
				atomic_read(&res->refcount));
		if (res->flags & SDE_CRTC_RES_FLAG_FREE)
			SDE_ERROR(
				"crtc%d.%u already free res:0x%x/0x%llx/%pK/%d\n",
					crtc->base.id, rp->sequence_id,
					res->type, res->tag, res->val,
					atomic_read(&res->refcount));
		else if (atomic_dec_return(&res->refcount) == 0)
			res->flags |= SDE_CRTC_RES_FLAG_FREE;

		return;
	}
	SDE_ERROR("crtc%d.%u not found res:0x%x/0x%llx\n",
			crtc->base.id, rp->sequence_id, type, tag);
}

int sde_crtc_res_add(struct drm_crtc_state *state, u32 type, u64 tag,
		void *val, struct sde_crtc_res_ops *ops)
{
	struct sde_crtc_respool *rp;

	if (!state) {
		SDE_ERROR("invalid parameters\n");
		return -EINVAL;
	}

	rp = &to_sde_crtc_state(state)->rp;
	return _sde_crtc_rp_add(rp, type, tag, val, ops);
}

void *sde_crtc_res_get(struct drm_crtc_state *state, u32 type, u64 tag)
{
	struct sde_crtc_respool *rp;
	void *val;

	if (!state) {
		SDE_ERROR("invalid parameters\n");
		return NULL;
	}

	rp = &to_sde_crtc_state(state)->rp;
	val = _sde_crtc_rp_get(rp, type, tag);
	if (IS_ERR(val)) {
		SDE_ERROR("failed to get res type:0x%x:0x%llx\n",
				type, tag);
		return NULL;
	}

	return val;
}

void sde_crtc_res_put(struct drm_crtc_state *state, u32 type, u64 tag)
{
	struct sde_crtc_respool *rp;

	if (!state) {
		SDE_ERROR("invalid parameters\n");
		return;
	}

	rp = &to_sde_crtc_state(state)->rp;
	_sde_crtc_rp_put(rp, type, tag);
}

static void _sde_crtc_deinit_events(struct sde_crtc *sde_crtc)
{
	if (!sde_crtc)
@@ -1071,6 +1439,8 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc,

	SDE_DEBUG("crtc%d\n", crtc->base.id);

	_sde_crtc_rp_destroy(&cstate->rp);

	__drm_atomic_helper_crtc_destroy_state(state);

	/* destroy value helper */
@@ -1262,6 +1632,8 @@ static struct drm_crtc_state *sde_crtc_duplicate_state(struct drm_crtc *crtc)
	/* duplicate base helper */
	__drm_atomic_helper_crtc_duplicate_state(crtc, &cstate->base);

	_sde_crtc_rp_duplicate(&old_cstate->rp, &cstate->rp);

	return &cstate->base;
}

@@ -1304,6 +1676,8 @@ static void sde_crtc_reset(struct drm_crtc *crtc)

	_sde_crtc_set_input_fence_timeout(cstate);

	_sde_crtc_rp_reset(&cstate->rp);

	cstate->base.crtc = crtc;
	crtc->state = &cstate->base;
}
@@ -1487,14 +1861,15 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
		return -EINVAL;
	}

	sde_crtc = to_sde_crtc(crtc);
	cstate = to_sde_crtc_state(state);

	if (!state->enable || !state->active) {
		SDE_DEBUG("crtc%d -> enable %d, active %d, skip atomic_check\n",
				crtc->base.id, state->enable, state->active);
		return 0;
		goto end;
	}

	sde_crtc = to_sde_crtc(crtc);
	cstate = to_sde_crtc_state(state);
	mode = &state->adjusted_mode;
	SDE_DEBUG("%s: check", sde_crtc->name);

@@ -1702,6 +2077,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,


end:
	_sde_crtc_rp_free_unused(&cstate->rp);
	return rc;
}

@@ -1907,6 +2283,9 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc,
		}
		if (ret)
			DRM_ERROR("failed to set the property\n");

		SDE_DEBUG("crtc%d %s[%d] <= 0x%llx ret=%d\n", crtc->base.id,
				property->name, property->base.id, val, ret);
	}

	return ret;
@@ -2209,6 +2588,7 @@ static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v)
{
	struct drm_crtc *crtc = (struct drm_crtc *) s->private;
	struct sde_crtc_state *cstate = to_sde_crtc_state(crtc->state);
	struct sde_crtc_res *res;

	seq_printf(s, "num_connectors: %d\n", cstate->num_connectors);
	seq_printf(s, "client type: %d\n", sde_crtc_get_client_type(crtc));
@@ -2218,6 +2598,13 @@ static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v)
	seq_printf(s, "max_per_pipe_ib: %llu\n",
			cstate->cur_perf.max_per_pipe_ib);

	seq_printf(s, "rp.%d: ", cstate->rp.sequence_id);
	list_for_each_entry(res, &cstate->rp.res_list, list)
		seq_printf(s, "0x%x/0x%llx/%pK/%d ",
				res->type, res->tag, res->val,
				atomic_read(&res->refcount));
	seq_puts(s, "\n");

	return 0;
}
DEFINE_SDE_DEBUGFS_SEQ_FOPS(sde_crtc_debugfs_state);
+83 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include "sde_fence.h"
#include "sde_kms.h"
#include "sde_core_perf.h"
#include "sde_hw_blk.h"

#define SDE_CRTC_NAME_SIZE	12

@@ -190,6 +191,56 @@ struct sde_crtc {

#define to_sde_crtc(x) container_of(x, struct sde_crtc, base)

/**
 * struct sde_crtc_res_ops - common operations for crtc resources
 * @get: get given resource
 * @put: put given resource
 */
struct sde_crtc_res_ops {
	void *(*get)(void *val, u32 type, u64 tag);
	void (*put)(void *val);
};

/* crtc resource type (0x0-0xffff reserved for hw block type */
#define SDE_CRTC_RES_ROT_OUT_FBO	0x10000
#define SDE_CRTC_RES_ROT_OUT_FB		0x10001
#define SDE_CRTC_RES_ROT_PLANE		0x10002
#define SDE_CRTC_RES_ROT_IN_FB		0x10003

#define SDE_CRTC_RES_FLAG_FREE		BIT(0)

/**
 * struct sde_crtc_res - definition of crtc resources
 * @list: list of crtc resource
 * @type: crtc resource type
 * @tag: unique identifier per type
 * @refcount: reference/usage count
 * @ops: callback operations
 * @val: resource handle associated with type/tag
 * @flags: customization flags
 */
struct sde_crtc_res {
	struct list_head list;
	u32 type;
	u64 tag;
	atomic_t refcount;
	struct sde_crtc_res_ops ops;
	void *val;
	u32 flags;
};

/**
 * sde_crtc_respool - crtc resource pool
 * @sequence_id: sequence identifier, incremented per state duplication
 * @res_list: list of resource managed by this resource pool
 * @ops: resource operations for parent resource pool
 */
struct sde_crtc_respool {
	u32 sequence_id;
	struct list_head res_list;
	struct sde_crtc_res_ops ops;
};

/**
 * struct sde_crtc_state - sde container for atomic crtc state
 * @base: Base drm crtc state structure
@@ -226,6 +277,8 @@ struct sde_crtc_state {
	struct sde_core_perf_params new_perf;
	struct sde_ctl_sbuf_cfg sbuf_cfg;
	u64 sbuf_prefill_line;

	struct sde_crtc_respool rp;
};

#define to_sde_crtc_state(x) \
@@ -370,4 +423,34 @@ static inline bool sde_crtc_is_enabled(struct drm_crtc *crtc)
int sde_crtc_event_queue(struct drm_crtc *crtc,
		void (*func)(struct drm_crtc *crtc, void *usr), void *usr);

/**
 * sde_crtc_res_add - add given resource to resource pool in crtc state
 * @state: Pointer to drm crtc state
 * @type: Resource type
 * @tag: Search tag for given resource
 * @val: Resource handle
 * @ops: Resource callback operations
 * return: 0 if success; error code otherwise
 */
int sde_crtc_res_add(struct drm_crtc_state *state, u32 type, u64 tag,
		void *val, struct sde_crtc_res_ops *ops);

/**
 * sde_crtc_res_get - get given resource from resource pool in crtc state
 * @state: Pointer to drm crtc state
 * @type: Resource type
 * @tag: Search tag for given resource
 * return: Resource handle if success; pointer error or null otherwise
 */
void *sde_crtc_res_get(struct drm_crtc_state *state, u32 type, u64 tag);

/**
 * sde_crtc_res_put - return given resource to resource pool in crtc state
 * @state: Pointer to drm crtc state
 * @type: Resource type
 * @tag: Search tag for given resource
 * return: None
 */
void sde_crtc_res_put(struct drm_crtc_state *state, u32 type, u64 tag);

#endif /* _SDE_CRTC_H_ */
+28 −140
Original line number Diff line number Diff line
@@ -29,9 +29,11 @@ static LIST_HEAD(sde_hw_blk_list);
 * sde_hw_blk_init - initialize hw block object
 * @type: hw block type - enum sde_hw_blk_type
 * @id: instance id of the hw block
 * @ops: Pointer to block operations
 * return: 0 if success; error code otherwise
 */
int sde_hw_blk_init(struct sde_hw_blk *hw_blk, u32 type, int id)
int sde_hw_blk_init(struct sde_hw_blk *hw_blk, u32 type, int id,
		struct sde_hw_blk_ops *ops)
{
	if (!hw_blk) {
		pr_err("invalid parameters\n");
@@ -42,7 +44,9 @@ int sde_hw_blk_init(struct sde_hw_blk *hw_blk, u32 type, int id)
	hw_blk->type = type;
	hw_blk->id = id;
	atomic_set(&hw_blk->refcount, 0);
	INIT_LIST_HEAD(&hw_blk->attach_list);

	if (ops)
		hw_blk->ops = *ops;

	mutex_lock(&sde_hw_blk_lock);
	list_add(&hw_blk->list, &sde_hw_blk_list);
@@ -58,8 +62,6 @@ int sde_hw_blk_init(struct sde_hw_blk *hw_blk, u32 type, int id)
 */
void sde_hw_blk_destroy(struct sde_hw_blk *hw_blk)
{
	struct sde_hw_blk_attachment *curr, *next;

	if (!hw_blk) {
		pr_err("invalid parameters\n");
		return;
@@ -69,14 +71,6 @@ void sde_hw_blk_destroy(struct sde_hw_blk *hw_blk)
		pr_err("hw_blk:%d.%d invalid refcount\n", hw_blk->type,
				hw_blk->id);

	list_for_each_entry_safe(curr, next, &hw_blk->attach_list, list) {
		pr_err("hw_blk:%d.%d tag:0x%x/0x%llx still attached\n",
				hw_blk->type, hw_blk->id,
				curr->tag, (u64) curr->value);
		list_del_init(&curr->list);
		kfree(curr);
	}

	mutex_lock(&sde_hw_blk_lock);
	list_del(&hw_blk->list);
	mutex_unlock(&sde_hw_blk_lock);
@@ -92,6 +86,7 @@ void sde_hw_blk_destroy(struct sde_hw_blk *hw_blk)
struct sde_hw_blk *sde_hw_blk_get(struct sde_hw_blk *hw_blk, u32 type, int id)
{
	struct sde_hw_blk *curr;
	int rc, refcount;

	if (!hw_blk) {
		mutex_lock(&sde_hw_blk_lock);
@@ -108,16 +103,28 @@ struct sde_hw_blk *sde_hw_blk_get(struct sde_hw_blk *hw_blk, u32 type, int id)
		mutex_unlock(&sde_hw_blk_lock);
	}

	if (hw_blk) {
		int refcount = atomic_inc_return(&hw_blk->refcount);
	if (!hw_blk) {
		pr_debug("no hw_blk:%d\n", type);
		return NULL;
	}

	refcount = atomic_inc_return(&hw_blk->refcount);

		pr_debug("hw_blk:%d.%d refcount:%d\n", hw_blk->type,
				hw_blk->id, refcount);
	} else {
		pr_err("no hw_blk:%d\n", type);
	if (refcount == 1 && hw_blk->ops.start) {
		rc = hw_blk->ops.start(hw_blk);
		if (rc) {
			pr_err("failed to start  hw_blk:%d rc:%d\n", type, rc);
			goto error_start;
		}
	}

	pr_debug("hw_blk:%d.%d refcount:%d\n", hw_blk->type,
			hw_blk->id, refcount);
	return hw_blk;

error_start:
	sde_hw_blk_put(hw_blk);
	return ERR_PTR(rc);
}

/**
@@ -125,11 +132,8 @@ struct sde_hw_blk *sde_hw_blk_get(struct sde_hw_blk *hw_blk, u32 type, int id)
 * @hw_blk: hw block to be freed
 * @free_blk: function to be called when reference count goes to zero
 */
void sde_hw_blk_put(struct sde_hw_blk *hw_blk,
		void (*free_blk)(struct sde_hw_blk *))
void sde_hw_blk_put(struct sde_hw_blk *hw_blk)
{
	struct sde_hw_blk_attachment *curr, *next;

	if (!hw_blk) {
		pr_err("invalid parameters\n");
		return;
@@ -146,122 +150,6 @@ void sde_hw_blk_put(struct sde_hw_blk *hw_blk,
	if (atomic_dec_return(&hw_blk->refcount))
		return;

	if (free_blk)
		free_blk(hw_blk);

	/* report any residual attachments */
	list_for_each_entry_safe(curr, next, &hw_blk->attach_list, list) {
		pr_err("hw_blk:%d.%d tag:0x%x/0x%llx still attached\n",
				hw_blk->type, hw_blk->id,
				curr->tag, (u64) curr->value);
		list_del_init(&curr->list);
		kfree(curr);
	}
}

/**
 * sde_hw_blk_lookup_blk - lookup hardware block that matches tag/value/type
 *	tuple and increment reference count
 * @tag: search tag
 * @value: value associated with search tag
 * @type: hardware block type
 * return: Pointer to hardware block
 */
struct sde_hw_blk *sde_hw_blk_lookup_blk(u32 tag, void *value, u32 type)
{
	struct sde_hw_blk *hw_blk = NULL, *curr;
	struct sde_hw_blk_attachment *attach;

	pr_debug("hw_blk:%d tag:0x%x/0x%llx\n", type, tag, (u64) value);

	mutex_lock(&sde_hw_blk_lock);
	list_for_each_entry(curr, &sde_hw_blk_list, list) {
		if ((curr->type != type) || !atomic_read(&curr->refcount))
			continue;

		list_for_each_entry(attach, &curr->attach_list, list) {
			if ((attach->tag != tag) || (attach->value != value))
				continue;

			hw_blk = curr;
			break;
		}

		if (hw_blk)
			break;
	}
	mutex_unlock(&sde_hw_blk_lock);

	if (hw_blk)
		sde_hw_blk_get(hw_blk, 0, -1);

	return hw_blk;
}

/**
 * sde_hw_blk_attach - attach given tag/value pair to hardware block
 *	and increment reference count
 * @hw_blk: Pointer hardware block
 * @tag: search tag
 * @value: value associated with search tag
 * return: 0 if success; error code otherwise
 */
int sde_hw_blk_attach(struct sde_hw_blk *hw_blk, u32 tag, void *value)
{
	struct sde_hw_blk_attachment *attach;

	if (!hw_blk) {
		pr_err("invalid parameters\n");
		return -EINVAL;
	}

	pr_debug("hw_blk:%d.%d tag:0x%x/0x%llx\n", hw_blk->type, hw_blk->id,
			tag, (u64) value);

	attach = kzalloc(sizeof(struct sde_hw_blk_attachment), GFP_KERNEL);
	if (!attach)
		return -ENOMEM;

	INIT_LIST_HEAD(&attach->list);
	attach->tag = tag;
	attach->value = value;
	/* always add to the front so latest shows up first in search */
	list_add(&attach->list, &hw_blk->attach_list);
	sde_hw_blk_get(hw_blk, 0, -1);

	return 0;
}

/**
 * sde_hw_blk_detach - detach given tag/value pair from hardware block
 *	and decrement reference count
 * @hw_blk: Pointer hardware block
 * @tag: search tag
 * @value: value associated with search tag
 * return: none
 */
void sde_hw_blk_detach(struct sde_hw_blk *hw_blk, u32 tag, void *value)
{
	struct sde_hw_blk_attachment *curr, *next;

	if (!hw_blk) {
		pr_err("invalid parameters\n");
		return;
	}

	pr_debug("hw_blk:%d.%d tag:0x%x/0x%llx\n", hw_blk->type, hw_blk->id,
			tag, (u64) value);

	list_for_each_entry_safe(curr, next, &hw_blk->attach_list, list) {
		if ((curr->tag != tag) || (curr->value != value))
			continue;

		list_del_init(&curr->list);
		kfree(curr);
		sde_hw_blk_put(hw_blk, NULL);
		return;
	}

	pr_err("hw_blk:%d.%d tag:0x%x/0x%llx not found\n", hw_blk->type,
			hw_blk->id, tag, (u64) value);
	if (hw_blk->ops.stop)
		hw_blk->ops.stop(hw_blk);
}
+12 −46
Original line number Diff line number Diff line
@@ -17,16 +17,16 @@
#include <linux/list.h>
#include <linux/atomic.h>

struct sde_hw_blk;

/**
 * struct sde_hw_blk_attachment - hardware block attachment
 * @list: list of attachment
 * @tag: search tag
 * @value: value associated with the given tag
 * struct sde_hw_blk_ops - common hardware block operations
 * @start: start operation on first get
 * @stop: stop operation on last put
 */
struct sde_hw_blk_attachment {
	struct list_head list;
	u32 tag;
	void *value;
struct sde_hw_blk_ops {
	int (*start)(struct sde_hw_blk *);
	void (*stop)(struct sde_hw_blk *);
};

/**
@@ -35,53 +35,19 @@ struct sde_hw_blk_attachment {
 * @type: hardware block type
 * @id: instance id
 * @refcount: reference/usage count
 * @attachment_list: list of attachment
 */
struct sde_hw_blk {
	struct list_head list;
	u32 type;
	int id;
	atomic_t refcount;
	struct list_head attach_list;
	struct sde_hw_blk_ops ops;
};

int sde_hw_blk_init(struct sde_hw_blk *hw_blk, u32 type, int id);
int sde_hw_blk_init(struct sde_hw_blk *hw_blk, u32 type, int id,
		struct sde_hw_blk_ops *ops);
void sde_hw_blk_destroy(struct sde_hw_blk *hw_blk);

struct sde_hw_blk *sde_hw_blk_get(struct sde_hw_blk *hw_blk, u32 type, int id);
void sde_hw_blk_put(struct sde_hw_blk *hw_blk,
		void (*blk_free)(struct sde_hw_blk *));

struct sde_hw_blk *sde_hw_blk_lookup_blk(u32 tag, void *value, u32 type);
int sde_hw_blk_attach(struct sde_hw_blk *hw_blk, u32 tag, void *value);
void sde_hw_blk_detach(struct sde_hw_blk *hw_blk, u32 tag, void *value);

/**
 * sde_hw_blk_lookup_value - return value associated with the given tag
 * @hw_blk: Pointer to hardware block
 * @tag: tag to find
 * @idx: index if more than one value found, with 0 being first
 * return: value associated with the given tag
 */
static inline void *sde_hw_blk_lookup_value(struct sde_hw_blk *hw_blk,
		u32 tag, u32 idx)
{
	struct sde_hw_blk_attachment *attach;

	if (!hw_blk)
		return NULL;

	list_for_each_entry(attach, &hw_blk->attach_list, list) {
		if (attach->tag != tag)
			continue;

		if (idx == 0)
			return attach->value;

		idx--;
	}

	return NULL;
}

void sde_hw_blk_put(struct sde_hw_blk *hw_blk);
#endif /*_SDE_HW_BLK_H */
+40 −43
Original line number Diff line number Diff line
@@ -792,6 +792,42 @@ static void _setup_rot_ops(struct sde_hw_rot_ops *ops, unsigned long features)
	ops->get_cache_size = sde_hw_rot_get_cache_size;
}

/**
 * sde_hw_rot_blk_stop - stop rotator block
 * @hw_blk: Pointer to base hardware block
 * return: none
 */
static void sde_hw_rot_blk_stop(struct sde_hw_blk *hw_blk)
{
	struct sde_hw_rot *hw_rot = to_sde_hw_rot(hw_blk);

	SDE_DEBUG("type:%d id:%d\n", hw_blk->type, hw_blk->id);

	sde_hw_rot_stop(hw_rot);
}

/**
 * sde_hw_rot_blk_start - art rotator block
 * @hw_blk: Pointer to base hardware block
 * return: 0 if success; error code otherwise
 */
static int sde_hw_rot_blk_start(struct sde_hw_blk *hw_blk)
{
	struct sde_hw_rot *hw_rot = to_sde_hw_rot(hw_blk);
	int rc = 0;

	SDE_DEBUG("type:%d id:%d\n", hw_blk->type, hw_blk->id);

	rc = sde_hw_rot_start(hw_rot);

	return rc;
}

static struct sde_hw_blk_ops sde_hw_rot_ops = {
	.start = sde_hw_rot_blk_start,
	.stop = sde_hw_rot_blk_stop,
};

/**
 * sde_hw_rot_init - create/initialize given rotator instance
 * @idx: index of given rotator
@@ -823,7 +859,8 @@ struct sde_hw_rot *sde_hw_rot_init(enum sde_rot idx,
	c->caps = cfg;
	_setup_rot_ops(&c->ops, c->caps->features);

	rc = sde_hw_blk_init(&c->base, SDE_HW_BLK_ROT, idx);
	rc = sde_hw_blk_init(&c->base, SDE_HW_BLK_ROT, idx,
			&sde_hw_rot_ops);
	if (rc) {
		SDE_ERROR("failed to init hw blk %d\n", rc);
		goto blk_init_error;
@@ -850,57 +887,17 @@ void sde_hw_rot_destroy(struct sde_hw_rot *hw_rot)
	kfree(hw_rot);
}

/**
 * sde_hw_rot_blk_stop - stop rotator block
 * @hw_blk: Pointer to base hardware block
 * return: none
 */
static void sde_hw_rot_blk_stop(struct sde_hw_blk *hw_blk)
{
	struct sde_hw_rot *hw_rot = to_sde_hw_rot(hw_blk);

	SDE_DEBUG("type:%d id:%d\n", hw_blk->type, hw_blk->id);

	sde_hw_rot_stop(hw_rot);
}

/**
 * sde_hw_rot_blk_start - art rotator block
 * @hw_blk: Pointer to base hardware block
 * return: 0 if success; error code otherwise
 */
static int sde_hw_rot_blk_start(struct sde_hw_blk *hw_blk)
{
	struct sde_hw_rot *hw_rot = to_sde_hw_rot(hw_blk);
	int rc = 0;

	SDE_DEBUG("type:%d id:%d\n", hw_blk->type, hw_blk->id);

	rc = sde_hw_rot_start(hw_rot);

	return rc;
}

struct sde_hw_rot *sde_hw_rot_get(struct sde_hw_rot *hw_rot)
{
	struct sde_hw_blk *hw_blk = sde_hw_blk_get(hw_rot ? &hw_rot->base :
			NULL, SDE_HW_BLK_ROT, -1);
	int rc = 0;

	if (!hw_rot && hw_blk)
		rc = sde_hw_rot_blk_start(hw_blk);

	if (rc) {
		sde_hw_blk_put(hw_blk, NULL);
		return NULL;
	}

	return hw_blk ? to_sde_hw_rot(hw_blk) : NULL;
	return IS_ERR_OR_NULL(hw_blk) ? NULL : to_sde_hw_rot(hw_blk);
}

void sde_hw_rot_put(struct sde_hw_rot *hw_rot)
{
	struct sde_hw_blk *hw_blk = hw_rot ? &hw_rot->base : NULL;

	sde_hw_blk_put(hw_blk, sde_hw_rot_blk_stop);
	sde_hw_blk_put(hw_blk);
}
Loading