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

Commit 9e199935 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drm/msm/sde: add crtc per-commit rotator resource management" into msm-4.9

parents 63ebee05 cdb2f282
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