Files
android_kernel_samsung_sm86…/driver/vidc/src/msm_vidc_state.c
Govindaraj Rajagopal 7c365bdb94 video: driver: restructure video state machine
Re-structure video driver statemachine to use
event handler. Added event handler support for
event states.

Added change to handle event depending on the
underlying state implementation.

Change-Id: Ib55c12c6cadc4d780797a5aee75d5ea61e95c94f
Signed-off-by: Govindaraj Rajagopal <quic_grajagop@quicinc.com>
2023-01-27 16:31:23 +05:30

1193 rader
30 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "msm_vidc_control.h"
#include "msm_vidc_driver.h"
#include "msm_vidc_state.h"
#include "msm_vidc_debug.h"
#include "msm_vidc_core.h"
#include "msm_vidc_vb2.h"
#include "msm_vidc.h"
#include "msm_vidc_events.h"
bool core_in_valid_state(struct msm_vidc_core *core)
{
return (core->state == MSM_VIDC_CORE_INIT ||
core->state == MSM_VIDC_CORE_INIT_WAIT);
}
bool is_core_state(struct msm_vidc_core *core, enum msm_vidc_core_state state)
{
return core->state == state;
}
static const char * const core_state_name_arr[] =
FOREACH_CORE_STATE(GENERATE_STRING);
const char *core_state_name(enum msm_vidc_core_state state)
{
const char *name = "UNKNOWN STATE";
if (state >= ARRAY_SIZE(core_state_name_arr))
goto exit;
name = core_state_name_arr[state];
exit:
return name;
}
static const char * const event_name_arr[] =
FOREACH_EVENT(GENERATE_STRING);
static const char *event_name(enum msm_vidc_event event)
{
const char *name = "UNKNOWN EVENT";
if (event >= ARRAY_SIZE(event_name_arr))
goto exit;
name = event_name_arr[event];
exit:
return name;
}
static int __strict_check(struct msm_vidc_core *core, const char *function)
{
bool fatal = !mutex_is_locked(&core->lock);
WARN_ON(fatal);
if (fatal)
d_vpr_e("%s: strict check failed\n", function);
return fatal ? -EINVAL : 0;
}
static int __strict_inst_check(struct msm_vidc_inst *inst, const char *function)
{
bool fatal = !mutex_is_locked(&inst->lock);
WARN_ON(fatal);
if (fatal)
d_vpr_e("%s: strict check failed\n", function);
return fatal ? -EINVAL : 0;
}
static int msm_vidc_core_deinit_state(struct msm_vidc_core *core,
enum msm_vidc_core_event_type type,
struct msm_vidc_event_data *data)
{
if (!core || !data) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
d_vpr_e("%s: unexpected core event type %u\n", __func__, type);
return -EINVAL;
}
static int msm_vidc_core_init_wait_state(struct msm_vidc_core *core,
enum msm_vidc_core_event_type type,
struct msm_vidc_event_data *data)
{
int rc = 0;
if (!core || !data) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
switch (type) {
case CORE_EVENT_UPDATE_SUB_STATE:
{
u32 req_sub_state;
u32 allow_mask = -1;
req_sub_state = data->edata.uval;
/* none of the requested substate supported */
if (!(req_sub_state & allow_mask)) {
d_vpr_e("%s: invalid substate update request %#x\n",
__func__, req_sub_state);
return -EINVAL;
}
/* update core substate */
core->sub_state |= req_sub_state & allow_mask;
return rc;
}
default: {
d_vpr_e("%s: unexpected core event type %u\n",
__func__, type);
return -EINVAL;
}
}
return rc;
}
static int msm_vidc_core_init_state(struct msm_vidc_core *core,
enum msm_vidc_core_event_type type,
struct msm_vidc_event_data *data)
{
int rc = 0;
if (!core || !data) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
switch (type) {
case CORE_EVENT_UPDATE_SUB_STATE:
{
u32 req_sub_state;
u32 allow_mask = -1;
req_sub_state = data->edata.uval;
/* none of the requested substate supported */
if (!(req_sub_state & allow_mask)) {
d_vpr_e("%s: invalid substate update request %#x\n",
__func__, req_sub_state);
return -EINVAL;
}
/* update core substate */
core->sub_state |= req_sub_state & allow_mask;
return rc;
}
default: {
d_vpr_e("%s: unexpected core event type %u\n",
__func__, type);
return -EINVAL;
}
}
return rc;
}
static int msm_vidc_core_error_state(struct msm_vidc_core *core,
enum msm_vidc_core_event_type type,
struct msm_vidc_event_data *data)
{
int rc = 0;
if (!core || !data) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
switch (type) {
case CORE_EVENT_UPDATE_SUB_STATE:
{
u32 req_sub_state;
u32 allow_mask = -1;
req_sub_state = data->edata.uval;
/* none of the requested substate supported */
if (!(req_sub_state & allow_mask)) {
d_vpr_e("%s: invalid substate update request %#x\n",
__func__, req_sub_state);
return -EINVAL;
}
/* update core substate */
core->sub_state |= req_sub_state & allow_mask;
return rc;
}
default: {
d_vpr_e("%s: unexpected core event type %u\n",
__func__, type);
return -EINVAL;
}
}
return rc;
}
struct msm_vidc_core_state_handle {
enum msm_vidc_core_state state;
int (*handle)(struct msm_vidc_core *core,
enum msm_vidc_core_event_type type,
struct msm_vidc_event_data *data);
};
static struct msm_vidc_core_state_handle *msm_vidc_get_core_state_handle(
enum msm_vidc_core_state req_state)
{
int cnt;
struct msm_vidc_core_state_handle *core_state_handle = NULL;
static struct msm_vidc_core_state_handle state_handle[] = {
{MSM_VIDC_CORE_DEINIT, msm_vidc_core_deinit_state },
{MSM_VIDC_CORE_INIT_WAIT, msm_vidc_core_init_wait_state },
{MSM_VIDC_CORE_INIT, msm_vidc_core_init_state },
{MSM_VIDC_CORE_ERROR, msm_vidc_core_error_state },
};
for (cnt = 0; cnt < ARRAY_SIZE(state_handle); cnt++) {
if (state_handle[cnt].state == req_state) {
core_state_handle = &state_handle[cnt];
break;
}
}
/* if req_state does not exist in the table */
if (cnt == ARRAY_SIZE(state_handle)) {
d_vpr_e("%s: invalid core state \"%s\" requested\n",
__func__, core_state_name(req_state));
return core_state_handle;
}
return core_state_handle;
}
int msm_vidc_update_core_state(struct msm_vidc_core *core,
enum msm_vidc_core_state request_state, const char *func)
{
struct msm_vidc_core_state_handle *state_handle = NULL;
int rc = 0;
/* get core state handler for requested state */
state_handle = msm_vidc_get_core_state_handle(request_state);
if (!state_handle)
return -EINVAL;
d_vpr_h("%s: core state changed to %s from %s\n", func,
core_state_name(state_handle->state), core_state_name(core->state));
/* finally update core state and handler */
core->state = state_handle->state;
core->state_handle = state_handle->handle;
return rc;
}
struct msm_vidc_core_state_allow {
enum msm_vidc_core_state from;
enum msm_vidc_core_state to;
enum msm_vidc_allow allow;
};
enum msm_vidc_allow msm_vidc_allow_core_state_change(
struct msm_vidc_core *core,
enum msm_vidc_core_state req_state)
{
int cnt;
enum msm_vidc_allow allow = MSM_VIDC_DISALLOW;
static struct msm_vidc_core_state_allow state[] = {
/* from, to, allow */
{MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_DEINIT, MSM_VIDC_IGNORE },
{MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_ALLOW },
{MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_INIT, MSM_VIDC_DISALLOW },
{MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_ERROR, MSM_VIDC_IGNORE },
{MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_DEINIT, MSM_VIDC_DISALLOW },
{MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_IGNORE },
{MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_INIT, MSM_VIDC_ALLOW },
{MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_ERROR, MSM_VIDC_ALLOW },
{MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_DEINIT, MSM_VIDC_ALLOW },
{MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_DISALLOW },
{MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_INIT, MSM_VIDC_IGNORE },
{MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_ERROR, MSM_VIDC_ALLOW },
{MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_DEINIT, MSM_VIDC_ALLOW },
{MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_IGNORE },
{MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_INIT, MSM_VIDC_IGNORE },
{MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_ERROR, MSM_VIDC_IGNORE },
};
for (cnt = 0; cnt < ARRAY_SIZE(state); cnt++) {
if (state[cnt].from == core->state && state[cnt].to == req_state) {
allow = state[cnt].allow;
break;
}
}
return allow;
}
int msm_vidc_change_core_state(struct msm_vidc_core *core,
enum msm_vidc_core_state request_state, const char *func)
{
enum msm_vidc_allow allow;
int rc = 0;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* core must be locked */
rc = __strict_check(core, func);
if (rc) {
d_vpr_e("%s(): core was not locked\n", func);
return rc;
}
/* current and requested state is same */
if (core->state == request_state)
return 0;
/* check if requested state movement is allowed */
allow = msm_vidc_allow_core_state_change(core, request_state);
if (allow == MSM_VIDC_IGNORE) {
d_vpr_h("%s: %s core state change %s -> %s\n", func,
allow_name(allow), core_state_name(core->state),
core_state_name(request_state));
return 0;
} else if (allow == MSM_VIDC_DISALLOW) {
d_vpr_e("%s: %s core state change %s -> %s\n", func,
allow_name(allow), core_state_name(core->state),
core_state_name(request_state));
return -EINVAL;
}
/* go ahead and update core state */
rc = msm_vidc_update_core_state(core, request_state, func);
if (rc)
return rc;
return rc;
}
bool is_core_sub_state(struct msm_vidc_core *core,
enum msm_vidc_core_sub_state sub_state)
{
return !!(core->sub_state & sub_state);
}
const char *core_sub_state_name(enum msm_vidc_core_sub_state sub_state)
{
switch (sub_state) {
case CORE_SUBSTATE_NONE: return "NONE ";
case CORE_SUBSTATE_GDSC_HANDOFF: return "GDSC_HANDOFF ";
case CORE_SUBSTATE_PM_SUSPEND: return "PM_SUSPEND ";
case CORE_SUBSTATE_FW_PWR_CTRL: return "FW_PWR_CTRL ";
case CORE_SUBSTATE_POWER_ENABLE: return "POWER_ENABLE ";
case CORE_SUBSTATE_PAGE_FAULT: return "PAGE_FAULT ";
case CORE_SUBSTATE_CPU_WATCHDOG: return "CPU_WATCHDOG ";
case CORE_SUBSTATE_VIDEO_UNRESPONSIVE: return "VIDEO_UNRESPONSIVE ";
case CORE_SUBSTATE_MAX: return "MAX ";
}
return "UNKNOWN ";
}
static int prepare_core_sub_state_name(enum msm_vidc_core_sub_state sub_state,
char *buf, u32 size)
{
int i = 0;
if (!buf || !size)
return -EINVAL;
strscpy(buf, "\0", size);
if (sub_state == CORE_SUBSTATE_NONE) {
strscpy(buf, "CORE_SUBSTATE_NONE", size);
return 0;
}
for (i = 0; BIT(i) < CORE_SUBSTATE_MAX; i++) {
if (sub_state & BIT(i))
strlcat(buf, core_sub_state_name(BIT(i)), size);
}
return 0;
}
static int msm_vidc_update_core_sub_state(struct msm_vidc_core *core,
enum msm_vidc_core_sub_state sub_state, const char *func)
{
struct msm_vidc_event_data data;
char sub_state_name[MAX_NAME_LENGTH];
int ret = 0, rc = 0;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* no substate update */
if (!sub_state)
return 0;
/* invoke update core substate event */
memset(&data, 0, sizeof(struct msm_vidc_event_data));
data.edata.uval = sub_state;
rc = core->state_handle(core, CORE_EVENT_UPDATE_SUB_STATE, &data);
if (rc) {
ret = prepare_core_sub_state_name(sub_state,
sub_state_name, sizeof(sub_state_name) - 1);
if (!ret)
d_vpr_e("%s: state %s, requested invalid core substate %s\n",
func, core_state_name(core->state), sub_state_name);
return rc;
}
return rc;
}
int msm_vidc_change_core_sub_state(struct msm_vidc_core *core,
enum msm_vidc_core_sub_state clear_sub_state,
enum msm_vidc_core_sub_state set_sub_state, const char *func)
{
int rc = 0;
enum msm_vidc_core_sub_state prev_sub_state;
if (!core) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* core must be locked */
rc = __strict_check(core, func);
if (rc) {
d_vpr_e("%s(): core was not locked\n", func);
return rc;
}
/* sanitize core state handler */
if (!core->state_handle) {
d_vpr_e("%s: invalid core state handle\n", __func__);
return -EINVAL;
}
/* final value will not change */
if (clear_sub_state == set_sub_state)
return 0;
/* sanitize clear & set value */
if (set_sub_state > CORE_SUBSTATE_MAX ||
clear_sub_state > CORE_SUBSTATE_MAX) {
d_vpr_e("%s: invalid sub states. clear %#x or set %#x\n",
func, clear_sub_state, set_sub_state);
return -EINVAL;
}
prev_sub_state = core->sub_state;
/* set sub state */
rc = msm_vidc_update_core_sub_state(core, set_sub_state, func);
if (rc)
return rc;
/* check if all core substates updated */
if ((core->sub_state & set_sub_state) != set_sub_state)
d_vpr_e("%s: all substates not updated %#x, expected %#x\n",
func, core->sub_state & set_sub_state, set_sub_state);
/* clear sub state */
core->sub_state &= ~clear_sub_state;
/* print substates only when there is a change */
if (core->sub_state != prev_sub_state) {
rc = prepare_core_sub_state_name(core->sub_state, core->sub_state_name,
sizeof(core->sub_state_name) - 1);
if (!rc)
d_vpr_h("%s: core sub state changed to %s\n", func, core->sub_state_name);
}
return 0;
}
static int msm_vidc_open_state(struct msm_vidc_inst *inst,
enum msm_vidc_event event, void *data)
{
int rc = 0;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* inst must be locked */
rc = __strict_inst_check(inst, __func__);
if (rc) {
i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
return -EINVAL;
}
switch (event) {
case MSM_VIDC_S_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)data;
rc = msm_vidc_s_fmt(inst, f);
if (rc)
return rc;
break;
}
case MSM_VIDC_S_CTRL:
{
struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
/* allow set_control request in open state */
rc = msm_vidc_s_ctrl(inst, ctrl);
if (rc)
return rc;
break;
}
case MSM_VIDC_REQBUFS:
{
struct v4l2_requestbuffers *b = (struct v4l2_requestbuffers *)data;
rc = msm_vidc_reqbufs(inst, b);
if (rc)
return rc;
break;
}
case MSM_VIDC_STREAMON:
{
struct vb2_queue *q = (struct vb2_queue *)data;
rc = msm_vidc_start_streaming(inst, q);
if (rc)
return rc;
break;
}
case MSM_VIDC_STREAMOFF:
{
struct vb2_queue *q = (struct vb2_queue *)data;
/* ignore streamoff request in open state */
i_vpr_e(inst, "%s: type %d is %s in state %s\n",
__func__, q->type, allow_name(MSM_VIDC_IGNORE),
state_name(inst->state));
break;
}
case MSM_VIDC_CMD_START:
{
rc = msm_vidc_start_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_CMD_STOP:
{
rc = msm_vidc_stop_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_BUF_QUEUE:
{
struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
/* defer qbuf request in open state */
print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
break;
}
default:
{
i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
return -EINVAL;
}
}
return rc;
}
static int msm_vidc_input_streaming_state(struct msm_vidc_inst *inst,
enum msm_vidc_event event, void *data)
{
int rc = 0;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* inst must be locked */
rc = __strict_inst_check(inst, __func__);
if (rc) {
i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
return -EINVAL;
}
switch (event) {
case MSM_VIDC_S_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)data;
rc = msm_vidc_s_fmt(inst, f);
if (rc)
return rc;
break;
}
case MSM_VIDC_S_CTRL:
{
struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
u32 cap_id = msm_vidc_get_cap_id(inst, ctrl->id);
if (cap_id == INST_CAP_NONE) {
i_vpr_e(inst, "%s: invalid cap_id %u\n", __func__, cap_id);
return -EINVAL;
}
/* disallow */
if (is_decode_session(inst)) {
/* check dynamic allowed if master port is streaming */
if (!(inst->capabilities->cap[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)) {
i_vpr_e(inst, "%s: cap_id %#x not allowed in state %s\n",
__func__, cap_id, state_name(inst->state));
return -EINVAL;
}
}
rc = msm_vidc_s_ctrl(inst, ctrl);
if (rc)
return rc;
break;
}
case MSM_VIDC_REQBUFS:
{
struct v4l2_requestbuffers *b = (struct v4l2_requestbuffers *)data;
rc = msm_vidc_reqbufs(inst, b);
if (rc)
return rc;
break;
}
case MSM_VIDC_STREAMON:
{
struct vb2_queue *q = (struct vb2_queue *)data;
rc = msm_vidc_start_streaming(inst, q);
if (rc)
return rc;
break;
}
case MSM_VIDC_STREAMOFF:
{
struct vb2_queue *q = (struct vb2_queue *)data;
enum msm_vidc_allow allow = MSM_VIDC_ALLOW;
/* ignore */
if (q->type == OUTPUT_MPLANE || q->type == OUTPUT_META_PLANE)
allow = MSM_VIDC_IGNORE;
/* disallow */
else if (q->type == INPUT_META_PLANE)
allow = MSM_VIDC_DISALLOW;
if (allow != MSM_VIDC_ALLOW) {
i_vpr_e(inst, "%s: type %d is %s in state %s\n",
__func__, q->type, allow_name(allow),
state_name(inst->state));
return (allow == MSM_VIDC_DISALLOW ? -EINVAL : 0);
}
/* sanitize type field */
if (q->type != INPUT_MPLANE) {
i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
return -EINVAL;
}
rc = msm_vidc_stop_streaming(inst, q);
if (rc)
return rc;
break;
}
case MSM_VIDC_CMD_START:
{
rc = msm_vidc_start_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_CMD_STOP:
{
rc = msm_vidc_stop_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_BUF_QUEUE:
{
struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
/* defer meta port */
if (buf->type == MSM_VIDC_BUF_INPUT_META || buf->type == MSM_VIDC_BUF_OUTPUT_META) {
print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
return 0;
}
/* disallow */
if (buf->type != MSM_VIDC_BUF_INPUT && buf->type != MSM_VIDC_BUF_OUTPUT) {
i_vpr_e(inst, "%s: invalid buf type %u\n", __func__, buf->type);
return -EINVAL;
}
/* defer output port */
if (buf->type == MSM_VIDC_BUF_OUTPUT) {
print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
return 0;
}
rc = msm_vidc_buf_queue(inst, buf);
if (rc)
return rc;
break;
}
default:
{
i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
return -EINVAL;
}
}
return rc;
}
static int msm_vidc_output_streaming_state(struct msm_vidc_inst *inst,
enum msm_vidc_event event, void *data)
{
int rc = 0;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* inst must be locked */
rc = __strict_inst_check(inst, __func__);
if (rc) {
i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
return -EINVAL;
}
switch (event) {
case MSM_VIDC_S_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)data;
rc = msm_vidc_s_fmt(inst, f);
if (rc)
return rc;
break;
}
case MSM_VIDC_S_CTRL:
{
struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
u32 cap_id = msm_vidc_get_cap_id(inst, ctrl->id);
if (cap_id == INST_CAP_NONE) {
i_vpr_e(inst, "%s: invalid cap_id %u\n", __func__, cap_id);
return -EINVAL;
}
/* disallow */
if (is_encode_session(inst)) {
/* check dynamic allowed if master port is streaming */
if (!(inst->capabilities->cap[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)) {
i_vpr_e(inst, "%s: cap_id %#x not allowed in state %s\n",
__func__, cap_id, state_name(inst->state));
return -EINVAL;
}
}
rc = msm_vidc_s_ctrl(inst, ctrl);
if (rc)
return rc;
break;
}
case MSM_VIDC_REQBUFS:
{
struct v4l2_requestbuffers *b = (struct v4l2_requestbuffers *)data;
rc = msm_vidc_reqbufs(inst, b);
if (rc)
return rc;
break;
}
case MSM_VIDC_STREAMON:
{
struct vb2_queue *q = (struct vb2_queue *)data;
rc = msm_vidc_start_streaming(inst, q);
if (rc)
return rc;
break;
}
case MSM_VIDC_STREAMOFF:
{
struct vb2_queue *q = (struct vb2_queue *)data;
enum msm_vidc_allow allow = MSM_VIDC_ALLOW;
/* ignore */
if (q->type == INPUT_MPLANE || q->type == INPUT_META_PLANE)
allow = MSM_VIDC_IGNORE;
/* disallow */
else if (q->type == OUTPUT_META_PLANE)
allow = MSM_VIDC_DISALLOW;
if (allow != MSM_VIDC_ALLOW) {
i_vpr_e(inst, "%s: type %d is %s in state %s\n",
__func__, q->type, allow_name(allow),
state_name(inst->state));
return (allow == MSM_VIDC_DISALLOW ? -EINVAL : 0);
}
/* sanitize type field */
if (q->type != OUTPUT_MPLANE) {
i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
return -EINVAL;
}
rc = msm_vidc_stop_streaming(inst, q);
if (rc)
return rc;
break;
}
case MSM_VIDC_CMD_START:
{
rc = msm_vidc_start_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_CMD_STOP:
{
rc = msm_vidc_stop_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_BUF_QUEUE:
{
struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
/* defer meta port */
if (buf->type == MSM_VIDC_BUF_INPUT_META || buf->type == MSM_VIDC_BUF_OUTPUT_META) {
print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
return 0;
}
/* disallow */
if (buf->type != MSM_VIDC_BUF_INPUT && buf->type != MSM_VIDC_BUF_OUTPUT) {
i_vpr_e(inst, "%s: invalid buf type %u\n", __func__, buf->type);
return -EINVAL;
}
/* defer input port */
if (buf->type == MSM_VIDC_BUF_INPUT) {
print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
return 0;
}
rc = msm_vidc_buf_queue(inst, buf);
if (rc)
return rc;
break;
}
default: {
i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
return -EINVAL;
}
}
return rc;
}
static int msm_vidc_streaming_state(struct msm_vidc_inst *inst,
enum msm_vidc_event event, void *data)
{
int rc = 0;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* inst must be locked */
rc = __strict_inst_check(inst, __func__);
if (rc) {
i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
return -EINVAL;
}
switch (event) {
case MSM_VIDC_S_CTRL:
{
struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
u32 cap_id = msm_vidc_get_cap_id(inst, ctrl->id);
if (cap_id == INST_CAP_NONE) {
i_vpr_e(inst, "%s: invalid cap_id %u\n", __func__, cap_id);
return -EINVAL;
}
/* disallow */
if (!(inst->capabilities->cap[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)) {
i_vpr_e(inst, "%s: cap_id %#x not allowed in state %s\n",
__func__, cap_id, state_name(inst->state));
return -EINVAL;
}
rc = msm_vidc_s_ctrl(inst, ctrl);
if (rc)
return rc;
break;
}
case MSM_VIDC_STREAMOFF:
{
struct vb2_queue *q = (struct vb2_queue *)data;
/* disallow */
if (q->type == INPUT_META_PLANE || q->type == OUTPUT_META_PLANE) {
i_vpr_e(inst, "%s: type %d is %s in state %s\n",
__func__, q->type, allow_name(MSM_VIDC_DISALLOW),
state_name(inst->state));
return -EINVAL;
}
/* sanitize type field */
if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
return -EINVAL;
}
rc = msm_vidc_stop_streaming(inst, q);
if (rc)
return rc;
break;
}
case MSM_VIDC_CMD_START:
{
rc = msm_vidc_start_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_CMD_STOP:
{
rc = msm_vidc_stop_cmd(inst);
if (rc)
return rc;
break;
}
case MSM_VIDC_BUF_QUEUE:
{
struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
/* defer meta port */
if (buf->type == MSM_VIDC_BUF_INPUT_META || buf->type == MSM_VIDC_BUF_OUTPUT_META) {
print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
return 0;
}
/* disallow */
if (buf->type != MSM_VIDC_BUF_INPUT && buf->type != MSM_VIDC_BUF_OUTPUT) {
i_vpr_e(inst, "%s: invalid buf type %u\n", __func__, buf->type);
return -EINVAL;
}
rc = msm_vidc_buf_queue(inst, buf);
if (rc)
return rc;
break;
}
default: {
i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
return -EINVAL;
}
}
return rc;
}
static int msm_vidc_close_state(struct msm_vidc_inst *inst,
enum msm_vidc_event event, void *data)
{
int rc = 0;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* inst must be locked */
rc = __strict_inst_check(inst, __func__);
if (rc) {
i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
return -EINVAL;
}
i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
return -EINVAL;
}
static int msm_vidc_error_state(struct msm_vidc_inst *inst,
enum msm_vidc_event event, void *data)
{
int rc = 0;
if (!inst) {
d_vpr_e("%s: invalid params\n", __func__);
return -EINVAL;
}
/* inst must be locked */
rc = __strict_inst_check(inst, __func__);
if (rc) {
i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
return -EINVAL;
}
switch (event) {
case MSM_VIDC_STREAMOFF:
{
struct vb2_queue *q = (struct vb2_queue *)data;
rc = msm_vidc_stop_streaming(inst, q);
if (rc)
return rc;
break;
}
default: {
i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
return -EINVAL;
}
}
return rc;
}
struct msm_vidc_state_handle {
enum msm_vidc_state state;
int (*handle)(struct msm_vidc_inst *inst,
enum msm_vidc_event event, void *data);
};
static struct msm_vidc_state_handle *msm_vidc_get_state_handle(
struct msm_vidc_inst *inst,
enum msm_vidc_state req_state)
{
int cnt;
struct msm_vidc_state_handle *inst_state_handle = NULL;
static struct msm_vidc_state_handle state_handle[] = {
{MSM_VIDC_OPEN, msm_vidc_open_state },
{MSM_VIDC_INPUT_STREAMING, msm_vidc_input_streaming_state },
{MSM_VIDC_OUTPUT_STREAMING, msm_vidc_output_streaming_state },
{MSM_VIDC_STREAMING, msm_vidc_streaming_state },
{MSM_VIDC_CLOSE, msm_vidc_close_state },
{MSM_VIDC_ERROR, msm_vidc_error_state },
};
for (cnt = 0; cnt < ARRAY_SIZE(state_handle); cnt++) {
if (state_handle[cnt].state == req_state) {
inst_state_handle = &state_handle[cnt];
break;
}
}
/* check if req_state does not exist in the table */
if (cnt == ARRAY_SIZE(state_handle)) {
i_vpr_e(inst, "%s: invalid state %s\n", __func__, state_name(req_state));
return inst_state_handle;
}
return inst_state_handle;
}
int msm_vidc_update_state(struct msm_vidc_inst *inst,
enum msm_vidc_state request_state, const char *func)
{
struct msm_vidc_state_handle *state_handle = NULL;
int rc = 0;
/* get inst state handler for requested state */
state_handle = msm_vidc_get_state_handle(inst, request_state);
if (!state_handle)
return -EINVAL;
if (request_state == MSM_VIDC_ERROR)
i_vpr_e(inst, FMT_STRING_STATE_CHANGE,
func, state_name(request_state), state_name(inst->state));
else
i_vpr_h(inst, FMT_STRING_STATE_CHANGE,
func, state_name(request_state), state_name(inst->state));
trace_msm_vidc_common_state_change(inst, func, state_name(inst->state),
state_name(request_state));
/* finally update inst state and handler */
inst->state = state_handle->state;
inst->event_handle = state_handle->handle;
return rc;
}
struct msm_vidc_state_allow {
enum msm_vidc_state from;
enum msm_vidc_state to;
enum msm_vidc_allow allow;
};
enum msm_vidc_allow msm_vidc_allow_state_change(
struct msm_vidc_inst *inst,
enum msm_vidc_state req_state)
{
int cnt;
enum msm_vidc_allow allow = MSM_VIDC_DISALLOW;
static struct msm_vidc_state_allow state[] = {
/* from, to, allow */
{MSM_VIDC_OPEN, MSM_VIDC_OPEN, MSM_VIDC_IGNORE },
{MSM_VIDC_OPEN, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_ALLOW },
{MSM_VIDC_OPEN, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_ALLOW },
{MSM_VIDC_OPEN, MSM_VIDC_STREAMING, MSM_VIDC_DISALLOW },
{MSM_VIDC_OPEN, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
{MSM_VIDC_OPEN, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
{MSM_VIDC_INPUT_STREAMING, MSM_VIDC_OPEN, MSM_VIDC_ALLOW },
{MSM_VIDC_INPUT_STREAMING, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_IGNORE },
{MSM_VIDC_INPUT_STREAMING, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_DISALLOW },
{MSM_VIDC_INPUT_STREAMING, MSM_VIDC_STREAMING, MSM_VIDC_ALLOW },
{MSM_VIDC_INPUT_STREAMING, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
{MSM_VIDC_INPUT_STREAMING, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
{MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_OPEN, MSM_VIDC_ALLOW },
{MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_DISALLOW },
{MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_IGNORE },
{MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_STREAMING, MSM_VIDC_ALLOW },
{MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
{MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
{MSM_VIDC_STREAMING, MSM_VIDC_OPEN, MSM_VIDC_DISALLOW },
{MSM_VIDC_STREAMING, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_ALLOW },
{MSM_VIDC_STREAMING, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_ALLOW },
{MSM_VIDC_STREAMING, MSM_VIDC_STREAMING, MSM_VIDC_IGNORE },
{MSM_VIDC_STREAMING, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
{MSM_VIDC_STREAMING, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
{MSM_VIDC_CLOSE, MSM_VIDC_OPEN, MSM_VIDC_DISALLOW },
{MSM_VIDC_CLOSE, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_DISALLOW },
{MSM_VIDC_CLOSE, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_DISALLOW },
{MSM_VIDC_CLOSE, MSM_VIDC_STREAMING, MSM_VIDC_DISALLOW },
{MSM_VIDC_CLOSE, MSM_VIDC_CLOSE, MSM_VIDC_IGNORE },
{MSM_VIDC_CLOSE, MSM_VIDC_ERROR, MSM_VIDC_IGNORE },
{MSM_VIDC_ERROR, MSM_VIDC_OPEN, MSM_VIDC_IGNORE },
{MSM_VIDC_ERROR, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_IGNORE },
{MSM_VIDC_ERROR, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_IGNORE },
{MSM_VIDC_ERROR, MSM_VIDC_STREAMING, MSM_VIDC_IGNORE },
{MSM_VIDC_ERROR, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
{MSM_VIDC_ERROR, MSM_VIDC_ERROR, MSM_VIDC_IGNORE },
};
for (cnt = 0; cnt < ARRAY_SIZE(state); cnt++) {
if (state[cnt].from == inst->state && state[cnt].to == req_state) {
allow = state[cnt].allow;
break;
}
}
return allow;
}