unplugged-kernel/drivers/misc/mediatek/apusys/midware/1.1/mdw_dispr.c

400 lines
8.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "mdw_cmn.h"
#include "mdw_rsc.h"
#include "mdw_cmd.h"
#include "mdw_sched.h"
enum {
MDW_DISPR_NORM,
MDW_DISPR_MULTI,
MDW_DISPR_PACK,
};
static struct mdw_cmd_parser *cmd_parser;
struct mdw_disp_item {
int type;
int id; // pack id(pack) or subcmd id(multi)
int target_sc_num;
int cur_sc_num;
struct mdw_apu_cmd *c;
struct mdw_rsc_req req;
struct list_head sc_list; // for mdw_apu_sc
struct list_head c_item; // to mdw_apu_cmd
struct list_head p_item; //to pack mgr
int (*exec)(struct mdw_disp_item *di);
struct mutex mtx;
};
struct mdw_dispr_mgr {
struct list_head ready_list;
struct mutex mtx;
};
static struct mdw_dispr_mgr d_mgr;
static int mdw_dispr_item_put(struct mdw_disp_item *di)
{
struct mdw_apu_cmd *c = di->c;
mdw_flw_debug("di(%p) destroy\n", di);
mutex_lock(&c->mtx);
list_del(&di->c_item);
vfree(di);
mutex_unlock(&c->mtx);
return 0;
}
static struct mdw_disp_item *mdw_dispr_item_get(
struct mdw_apu_sc *sc, int disp_type)
{
struct mdw_disp_item *di = NULL;
struct mdw_apu_cmd *c = sc->parent;
struct list_head *tmp = NULL, *list_ptr = NULL;
/* check packid */
if (sc->hdr->pack_id >= 64 ||
disp_type < MDW_DISPR_MULTI || disp_type > MDW_DISPR_PACK)
return NULL;
/* get disp item from cmd list */
mutex_lock(&c->mtx);
list_for_each_safe(list_ptr, tmp, &c->di_list) {
di = list_entry(list_ptr, struct mdw_disp_item, c_item);
if (di->type == disp_type &&
disp_type == MDW_DISPR_PACK &&
di->id == sc->hdr->pack_id)
break;
else if (di->type == disp_type &&
disp_type == MDW_DISPR_MULTI &&
di->id == sc->idx)
break;
di = NULL;
}
if (di)
goto out;
/* alloc new pack item */
di = vzalloc(sizeof(struct mdw_disp_item));
if (!di)
goto out;
INIT_LIST_HEAD(&di->sc_list);
INIT_LIST_HEAD(&di->req.d_list);
mutex_init(&di->mtx);
di->type = disp_type;
di->c = c;
/* setup id */
if (di->type == MDW_DISPR_MULTI) {
di->id = sc->idx;
di->target_sc_num = 1;
} else if (di->type == MDW_DISPR_PACK) {
di->id = sc->hdr->pack_id;
di->target_sc_num = c->pack_cnt[sc->hdr->pack_id];
}
list_add_tail(&di->c_item, &c->di_list);
mdw_flw_debug("di(%p) create, type(%d) id(%d)\n", di, di->type, di->id);
out:
mutex_unlock(&c->mtx);
return di;
}
static void mdw_dispr_async_cb(struct mdw_rsc_req *r)
{
struct mdw_disp_item *di =
container_of(r, struct mdw_disp_item, req);
mdw_flw_debug("di(%p) done\n", di);
/* add to mgr's done list */
mutex_lock(&d_mgr.mtx);
list_add_tail(&di->p_item, &d_mgr.ready_list);
mutex_unlock(&d_mgr.mtx);
mdw_sched(NULL);
}
static int mdw_dispr_get_dev(struct mdw_disp_item *di)
{
int ret = 0;
di->req.mode = MDW_DEV_INFO_GET_MODE_ASYNC;
di->req.policy = MDW_DEV_INFO_GET_POLICY_SEQ;
di->req.cb_async = mdw_dispr_async_cb;
mdw_flw_debug("di(%p)\n", di);
ret = mdw_rsc_get_dev(&di->req);
/* ret = 0 or -EAGAIN are normal */
if (!ret || ret == -EAGAIN)
return 0;
return ret;
}
static int mdw_dispr_pwron_dev(struct mdw_rsc_req *r, struct mdw_apu_sc *sc)
{
struct mdw_dev_info *d = NULL;
struct list_head *tmp = NULL, *list_ptr = NULL;
int ret = 0;
list_for_each_safe(list_ptr, tmp, &r->d_list) {
d = list_entry(list_ptr, struct mdw_dev_info, r_item);
ret = d->pwr_on(d, sc->boost, MDW_RSC_SET_PWR_TIMEOUT);
if (ret) {
mdw_drv_err("pwn on(%s-#d) fail\n", d->name, d->idx);
break;
}
sc->multi_bmp |= (1ULL << d->idx);
mdw_flw_debug("pwron dev(%s%d)\n", d->name, d->idx);
}
return ret;
}
static struct mdw_dev_info *mdw_dispr_pop_dev(int type,
struct mdw_rsc_req *r)
{
struct mdw_dev_info *d = NULL;
struct list_head *tmp = NULL, *list_ptr = NULL;
list_for_each_safe(list_ptr, tmp, &r->d_list) {
d = list_entry(list_ptr, struct mdw_dev_info, r_item);
if (d->type == type) {
list_del(&d->r_item);
break;
}
d = NULL;
}
return d;
}
static int mdw_dispr_exec_pack(struct mdw_disp_item *di)
{
struct mdw_apu_sc *sc = NULL;
struct mdw_dev_info *d = NULL;
struct list_head *tmp = NULL, *list_ptr = NULL;
int ret = 0;
mdw_flw_debug("di(%p|%d-%d)\n", di, di->type, di->id);
mutex_lock(&di->mtx);
list_for_each_safe(list_ptr, tmp, &di->sc_list) {
sc = list_entry(list_ptr, struct mdw_apu_sc, di_item);
d = mdw_dispr_pop_dev(sc->type, &di->req);
if (d)
ret = d->exec(d, sc);
if (!d || ret)
mdw_drv_err("sc(0x%llx-#%d) fail(%p/%d)\n",
sc->parent->kid, sc->idx, d, ret);
}
mutex_unlock(&di->mtx);
mdw_dispr_item_put(di);
return ret;
}
static int mdw_dispr_exec_multi(struct mdw_disp_item *di)
{
struct mdw_apu_sc *sc = NULL;
struct mdw_dev_info *d = NULL;
struct list_head *tmp = NULL, *list_ptr = NULL;
int ret = 0;
mdw_flw_debug("di(%p|%d-%d)\n", di, di->type, di->id);
mutex_lock(&di->mtx);
sc = list_first_entry_or_null(&di->sc_list,
struct mdw_apu_sc, di_item);
if (!sc)
goto out;
mutex_lock(&sc->mtx);
mdw_dispr_pwron_dev(&di->req, sc);
mutex_unlock(&sc->mtx);
list_for_each_safe(list_ptr, tmp, &di->req.d_list) {
d = list_entry(list_ptr, struct mdw_dev_info, r_item);
if (d->type == sc->type) {
list_del(&d->r_item);
ret = d->exec(d, sc);
}
if (ret)
mdw_drv_err("sc(0x%llx-#%d) fail(%d)\n",
sc->parent->kid, sc->idx, ret);
}
out:
mutex_unlock(&di->mtx);
mdw_dispr_item_put(di);
return ret;
}
//------------------------
void mdw_dispr_check(void)
{
struct mdw_disp_item *di = NULL;
struct list_head *tmp = NULL, *list_ptr = NULL;
mutex_lock(&d_mgr.mtx);
list_for_each_safe(list_ptr, tmp, &d_mgr.ready_list) {
di = list_entry(list_ptr, struct mdw_disp_item, p_item);
list_del(&di->p_item);
mutex_unlock(&d_mgr.mtx);
di->exec(di);
mutex_lock(&d_mgr.mtx);
}
mutex_unlock(&d_mgr.mtx);
mdw_flw_debug("\n");
}
int mdw_dispr_norm(struct mdw_apu_sc *sc)
{
struct mdw_rsc_req r;
struct mdw_dev_info *d = NULL;
struct list_head *tmp = NULL, *list_ptr = NULL;
int ret = 0;
/* allocate device */
memset(&r, 0, sizeof(r));
r.num[sc->type] = sc->multi_total;
r.total_num = sc->multi_total;
r.acq_bmp |= (1ULL << sc->type);
r.mode = MDW_DEV_INFO_GET_MODE_TRY;
if (cmd_parser->is_deadline(sc))
r.policy = MDW_DEV_INFO_GET_POLICY_RR;
else
r.policy = MDW_DEV_INFO_GET_POLICY_SEQ;
ret = mdw_rsc_get_dev(&r);
if (ret)
goto out;
mutex_lock(&sc->mtx);
sc->multi_total = r.get_num[sc->type];
refcount_set(&sc->multi_ref.refcount, r.get_num[sc->type]);
/* power on each device if multicore */
if (r.get_num[sc->type] > 1)
mdw_dispr_pwron_dev(&r, sc);
mutex_unlock(&sc->mtx);
mdw_flw_debug("sc(0x%llx-#%d) #dev(%u/%u) ref(%d)\n",
sc->parent->kid, sc->idx, r.get_num[sc->type],
r.num[sc->type], kref_read(&sc->multi_ref));
/* dispatch cmd */
list_for_each_safe(list_ptr, tmp, &r.d_list) {
d = list_entry(list_ptr, struct mdw_dev_info, r_item);
ret = d->exec(d, sc);
if (ret)
goto fail_exec_sc;
list_del(&d->r_item);
}
goto out;
fail_exec_sc:
list_for_each_safe(list_ptr, tmp, &r.d_list) {
d = list_entry(list_ptr, struct mdw_dev_info, r_item);
list_del(&d->r_item);
mdw_rsc_put_dev(d);
}
out:
return ret;
}
int mdw_dispr_multi(struct mdw_apu_sc *sc)
{
struct mdw_disp_item *di = NULL;
int ret = 0;
di = mdw_dispr_item_get(sc, MDW_DISPR_MULTI);
if (!di)
return -ENODATA;
mutex_lock(&di->mtx);
list_add_tail(&sc->di_item, &di->sc_list);
di->cur_sc_num++;
di->req.num[sc->type] = sc->multi_total;
di->req.acq_bmp |= (1ULL << sc->type);
di->req.total_num = sc->multi_total;
di->exec = mdw_dispr_exec_multi;
refcount_set(&sc->multi_ref.refcount, sc->multi_total);
mdw_flw_debug("sc(0x%llx-#%d) multi(%d)(%d/%d)\n", sc->parent->kid,
sc->idx, sc->hdr->pack_id, di->cur_sc_num, di->target_sc_num);
ret = mdw_dispr_get_dev(di);
mutex_unlock(&di->mtx);
return ret;
}
int mdw_dispr_pack(struct mdw_apu_sc *sc)
{
struct mdw_disp_item *di = NULL;
int ret = 0;
di = mdw_dispr_item_get(sc, MDW_DISPR_PACK);
if (!di)
return -ENODATA;
mutex_lock(&di->mtx);
list_add_tail(&sc->di_item, &di->sc_list);
di->cur_sc_num++;
di->req.num[sc->type]++;
di->req.acq_bmp |= (1ULL << sc->type);
di->req.total_num++;
refcount_set(&sc->multi_ref.refcount, 1);
di->exec = mdw_dispr_exec_pack;
mdw_flw_debug("sc(0x%llx-#%d) pack(%d)(%d/%d)\n", sc->parent->kid,
sc->idx, sc->hdr->pack_id, di->cur_sc_num, di->target_sc_num);
if (di->cur_sc_num >= di->target_sc_num)
ret = mdw_dispr_get_dev(di);
mutex_unlock(&di->mtx);
return ret;
}
int mdw_dispr_init(void)
{
memset(&d_mgr, 0, sizeof(d_mgr));
mutex_init(&d_mgr.mtx);
INIT_LIST_HEAD(&d_mgr.ready_list);
cmd_parser = mdw_cmd_get_parser();
return 0;
}
void mdw_dispr_exit(void)
{
}