M7350/kernel/drivers/video/msm/vidc/common/vcd/vcd_scheduler.c

297 lines
8.2 KiB
C
Raw Normal View History

2024-09-09 08:52:07 +00:00
/* Copyright (c) 2010-2013, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <media/msm/vidc_type.h>
#include "vcd.h"
#define NORMALIZATION_FACTOR 3600
#define ADJUST_CLIENT_ROUNDS(client, round_adjustment) \
do {\
if ((client)->rounds < round_adjustment) {\
(client)->rounds = 0;\
VCD_MSG_HIGH("%s(): WARNING: Scheduler list unsorted",\
__func__);\
} else\
(client)->rounds -= round_adjustment;\
} while (0)
u32 vcd_sched_create(struct list_head *sched_list)
{
u32 rc = VCD_S_SUCCESS;
if (!sched_list) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else
INIT_LIST_HEAD(sched_list);
return rc;
}
void vcd_sched_destroy(struct list_head *sched_clnt_list)
{
struct vcd_sched_clnt_ctx *sched_clnt, *sched_clnt_next;
if (sched_clnt_list)
list_for_each_entry_safe(sched_clnt,
sched_clnt_next, sched_clnt_list, list) {
list_del_init(&sched_clnt->list);
sched_clnt->clnt_active = false;
}
}
void insert_client_in_list(struct list_head *sched_clnt_list,
struct vcd_sched_clnt_ctx *sched_new_clnt, bool tail)
{
struct vcd_sched_clnt_ctx *sched_clnt;
if (!list_empty(sched_clnt_list)) {
if (tail)
sched_clnt = list_entry(sched_clnt_list->prev,
struct vcd_sched_clnt_ctx, list);
else
sched_clnt = list_first_entry(sched_clnt_list,
struct vcd_sched_clnt_ctx, list);
sched_new_clnt->rounds = sched_clnt->rounds;
} else
sched_new_clnt->rounds = 0;
if (tail)
list_add_tail(&sched_new_clnt->list, sched_clnt_list);
else
list_add(&sched_new_clnt->list, sched_clnt_list);
}
u32 vcd_sched_add_client(struct vcd_clnt_ctxt *cctxt)
{
struct vcd_property_hdr prop_hdr;
struct vcd_sched_clnt_ctx *sched_cctxt;
u32 rc = VCD_S_SUCCESS;
if (!cctxt) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else if (cctxt->sched_clnt_hdl)
VCD_MSG_HIGH(
"%s(): Scheduler client already exists!", __func__);
else {
sched_cctxt = (struct vcd_sched_clnt_ctx *)
kmalloc(sizeof(struct vcd_sched_clnt_ctx),
GFP_KERNEL);
if (sched_cctxt) {
prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS;
prop_hdr.sz = sizeof(cctxt->frm_p_units);
rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr,
&cctxt->frm_p_units);
if (VCD_FAILED(rc)) {
kfree(sched_cctxt);
VCD_MSG_ERROR(
"Failed: Get DDL_I_FRAME_PROC_UNITS");
return rc;
}
if (cctxt->decoding) {
cctxt->frm_rate.fps_numerator =
VCD_DEC_INITIAL_FRAME_RATE;
cctxt->frm_rate.fps_denominator = 1;
} else {
prop_hdr.prop_id = VCD_I_FRAME_RATE;
prop_hdr.sz = sizeof(cctxt->frm_rate);
rc = ddl_get_property(cctxt->ddl_handle,
&prop_hdr, &cctxt->frm_rate);
if (VCD_FAILED(rc)) {
kfree(sched_cctxt);
VCD_MSG_ERROR(
"Failed: Get VCD_I_FRAME_RATE");
return rc;
}
}
if (!cctxt->perf_set_by_client)
cctxt->reqd_perf_lvl = cctxt->frm_p_units *
cctxt->frm_rate.fps_numerator /
cctxt->frm_rate.fps_denominator;
cctxt->sched_clnt_hdl = sched_cctxt;
memset(sched_cctxt, 0,
sizeof(struct vcd_sched_clnt_ctx));
sched_cctxt->tkns = 0;
sched_cctxt->round_perfrm = NORMALIZATION_FACTOR *
cctxt->frm_rate.fps_denominator /
cctxt->frm_rate.fps_numerator;
sched_cctxt->clnt_active = true;
sched_cctxt->clnt_data = cctxt;
INIT_LIST_HEAD(&sched_cctxt->ip_frm_list);
insert_client_in_list(
&cctxt->dev_ctxt->sched_clnt_list,
sched_cctxt, false);
}
}
return rc;
}
u32 vcd_sched_remove_client(struct vcd_sched_clnt_ctx *sched_cctxt)
{
u32 rc = VCD_S_SUCCESS;
struct vcd_clnt_ctxt *cctxt;
if (!sched_cctxt) {
VCD_MSG_ERROR("%s(): Invalid handle ptr", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else if (!list_empty(&sched_cctxt->ip_frm_list)) {
VCD_MSG_ERROR(
"%s(): Cannot remove client, queue no empty", __func__);
rc = VCD_ERR_ILLEGAL_OP;
} else {
cctxt = sched_cctxt->clnt_data;
list_del(&sched_cctxt->list);
memset(sched_cctxt, 0,
sizeof(struct vcd_sched_clnt_ctx));
kfree(sched_cctxt);
}
return rc;
}
u32 vcd_sched_update_config(struct vcd_clnt_ctxt *cctxt)
{
u32 rc = VCD_S_SUCCESS;
if (!cctxt || !cctxt->sched_clnt_hdl) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else {
cctxt->sched_clnt_hdl->rounds /=
cctxt->sched_clnt_hdl->round_perfrm;
cctxt->sched_clnt_hdl->round_perfrm =
NORMALIZATION_FACTOR *
cctxt->frm_rate.fps_denominator /
cctxt->frm_rate.fps_numerator;
cctxt->sched_clnt_hdl->rounds *=
cctxt->sched_clnt_hdl->round_perfrm;
}
return rc;
}
u32 vcd_sched_queue_buffer(
struct vcd_sched_clnt_ctx *sched_cctxt,
struct vcd_buffer_entry *buffer, u32 tail)
{
u32 rc = VCD_S_SUCCESS;
if (!sched_cctxt || !buffer) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else if (tail)
list_add_tail(&buffer->sched_list,
&sched_cctxt->ip_frm_list);
else
list_add(&buffer->sched_list, &sched_cctxt->ip_frm_list);
return rc;
}
u32 vcd_sched_dequeue_buffer(
struct vcd_sched_clnt_ctx *sched_cctxt,
struct vcd_buffer_entry **buffer)
{
u32 rc = VCD_ERR_QEMPTY;
if (!sched_cctxt || !buffer) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else {
*buffer = NULL;
if (!list_empty(&sched_cctxt->ip_frm_list)) {
*buffer = list_first_entry(
&sched_cctxt->ip_frm_list,
struct vcd_buffer_entry,
sched_list);
list_del(&(*buffer)->sched_list);
rc = VCD_S_SUCCESS;
}
}
return rc;
}
u32 vcd_sched_mark_client_eof(struct vcd_sched_clnt_ctx *sched_cctxt)
{
u32 rc = VCD_S_SUCCESS;
struct vcd_buffer_entry *buffer = NULL;
if (!sched_cctxt) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else if (!list_empty(&sched_cctxt->ip_frm_list)) {
buffer = list_entry(sched_cctxt->ip_frm_list.prev,
struct vcd_buffer_entry, sched_list);
buffer->frame.flags |= VCD_FRAME_FLAG_EOS;
} else
rc = VCD_ERR_QEMPTY;
return rc;
}
u32 vcd_sched_suspend_resume_clnt(
struct vcd_clnt_ctxt *cctxt, u32 state)
{
u32 rc = VCD_S_SUCCESS;
struct vcd_sched_clnt_ctx *sched_cctxt;
if (!cctxt || !cctxt->sched_clnt_hdl) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else {
sched_cctxt = cctxt->sched_clnt_hdl;
if (state != sched_cctxt->clnt_active) {
sched_cctxt->clnt_active = state;
if (state)
insert_client_in_list(&cctxt->dev_ctxt->\
sched_clnt_list, sched_cctxt, false);
else
list_del_init(&sched_cctxt->list);
}
}
return rc;
}
u32 vcd_sched_get_client_frame(struct list_head *sched_clnt_list,
struct vcd_clnt_ctxt **cctxt,
struct vcd_buffer_entry **buffer)
{
u32 rc = VCD_ERR_QEMPTY, round_adjustment = 0;
struct vcd_sched_clnt_ctx *sched_clnt, *clnt_nxt;
if (!sched_clnt_list || !cctxt || !buffer) {
VCD_MSG_ERROR("%s(): Invalid parameter", __func__);
rc = VCD_ERR_ILLEGAL_PARM;
} else if (!list_empty(sched_clnt_list)) {
*cctxt = NULL;
*buffer = NULL;
list_for_each_entry_safe(sched_clnt,
clnt_nxt, sched_clnt_list, list) {
if (&sched_clnt->list == sched_clnt_list->next)
round_adjustment = sched_clnt->rounds;
if (*cctxt) {
if ((*cctxt)->sched_clnt_hdl->rounds >=
sched_clnt->rounds)
list_move(&(*cctxt)->sched_clnt_hdl\
->list, &sched_clnt->list);
ADJUST_CLIENT_ROUNDS(sched_clnt,
round_adjustment);
} else if (sched_clnt->tkns &&
!list_empty(&sched_clnt->ip_frm_list)) {
*cctxt = sched_clnt->clnt_data;
sched_clnt->rounds += sched_clnt->round_perfrm;
} else
ADJUST_CLIENT_ROUNDS(sched_clnt,
round_adjustment);
}
if (*cctxt) {
rc = vcd_sched_dequeue_buffer(
(*cctxt)->sched_clnt_hdl, buffer);
if (rc == VCD_S_SUCCESS) {
(*cctxt)->sched_clnt_hdl->tkns--;
ADJUST_CLIENT_ROUNDS((*cctxt)->\
sched_clnt_hdl, round_adjustment);
}
}
}
return rc;
}