When SCP timeout during playing video, kernel crashes with following message. It's caused by accessing NULL pointer in vpu_dec_ipi_handler. This patch doesn't solve the root cause of NULL pointer, but merely prevent kernel crashed when encounter the NULL pointer. After applied this patch, kernel keeps alive, only the video player turns to green screen. [67242.065474] pc : vpu_dec_ipi_handler+0xa0/0xb20 [mtk_vcodec_dec] [67242.065485] [MTK_V4L2] level=0 fops_vcodec_open(),334: 18000000.vcodec_dec decoder [135] [67242.065523] lr : scp_ipi_handler+0x11c/0x244 [mtk_scp] [67242.065540] sp : ffffffbb4207fb10 [67242.065557] x29: ffffffbb4207fb30 x28: ffffffd00a1d5000 [67242.065592] x27: 1ffffffa0143aa24 x26: 0000000000000000 [67242.065625] x25: dfffffd000000000 x24: ffffffd0168bfdb0 [67242.065659] x23: 1ffffff76840ff74 x22: ffffffbb41fa8a88 [67242.065692] x21: ffffffbb4207fb9c x20: ffffffbb4207fba0 [67242.065725] x19: ffffffbb4207fb98 x18: 0000000000000000 [67242.065758] x17: 0000000000000000 x16: ffffffd042022094 [67242.065791] x15: 1ffffff77ed4b71a x14: 1ffffff77ed4b719 [67242.065824] x13: 0000000000000000 x12: 0000000000000000 [67242.065857] x11: 0000000000000000 x10: dfffffd000000001 [67242.065890] x9 : 0000000000000000 x8 : 0000000000000002 [67242.065923] x7 : 0000000000000000 x6 : 000000000000003f [67242.065956] x5 : 0000000000000040 x4 : ffffffffffffffe0 [67242.065989] x3 : ffffffd043b841b8 x2 : 0000000000000000 [67242.066021] x1 : 0000000000000010 x0 : 0000000000000010 [67242.066055] Call trace: [67242.066092] vpu_dec_ipi_handler+0xa0/0xb20 [mtk_vcodec_dec 12220d230d83a7426fc38c56b3e7bc6066955bae] [67242.066119] scp_ipi_handler+0x11c/0x244 [mtk_scp 8fb69c2ef141dd3192518b952b65aba35627b8bf] [67242.066145] mt8192_scp_irq_handler+0x70/0x128 [mtk_scp 8fb69c2ef141dd3192518b952b65aba35627b8bf] [67242.066172] scp_irq_handler+0xa0/0x114 [mtk_scp 8fb69c2ef141dd3192518b952b65aba35627b8bf] [67242.066200] irq_thread_fn+0x84/0xf8 [67242.066220] irq_thread+0x170/0x1ec [67242.066242] kthread+0x2f8/0x3b8 [67242.066264] ret_from_fork+0x10/0x30 [67242.066292] Code: 38f96908 35003628 91004340 d343fc08 (38f96908) Signed-off-by: Tinghan Shen <tinghan.shen@mediatek.com> Signed-off-by: Yunfei Dong <yunfei.dong@mediatek.com> Reviewed-by: Macpaul Lin <macpaul.lin@mediatek.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
298 lines
7.1 KiB
C
298 lines
7.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2016 MediaTek Inc.
|
|
* Author: PC Chen <pc.chen@mediatek.com>
|
|
*/
|
|
|
|
#include "mtk_vcodec_drv.h"
|
|
#include "mtk_vcodec_util.h"
|
|
#include "vdec_drv_if.h"
|
|
#include "vdec_ipi_msg.h"
|
|
#include "vdec_vpu_if.h"
|
|
#include "mtk_vcodec_fw.h"
|
|
|
|
static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg)
|
|
{
|
|
struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *)
|
|
(unsigned long)msg->ap_inst_addr;
|
|
|
|
mtk_vcodec_debug(vpu, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr);
|
|
|
|
/* mapping VPU address to kernel virtual address */
|
|
/* the content in vsi is initialized to 0 in VPU */
|
|
vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler,
|
|
msg->vpu_inst_addr);
|
|
vpu->inst_addr = msg->vpu_inst_addr;
|
|
|
|
mtk_vcodec_debug(vpu, "- vpu_inst_addr = 0x%x", vpu->inst_addr);
|
|
|
|
/* Set default ABI version if dealing with unversioned firmware. */
|
|
vpu->fw_abi_version = 0;
|
|
/*
|
|
* Instance ID is only used if ABI version >= 2. Initialize it with
|
|
* garbage by default.
|
|
*/
|
|
vpu->inst_id = 0xdeadbeef;
|
|
|
|
/* VPU firmware does not contain a version field. */
|
|
if (mtk_vcodec_fw_get_type(vpu->ctx->dev->fw_handler) == VPU)
|
|
return;
|
|
|
|
/* Check firmware version. */
|
|
vpu->fw_abi_version = msg->vdec_abi_version;
|
|
mtk_vcodec_debug(vpu, "firmware version 0x%x\n", vpu->fw_abi_version);
|
|
switch (vpu->fw_abi_version) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
vpu->inst_id = msg->inst_id;
|
|
break;
|
|
default:
|
|
mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n",
|
|
vpu->fw_abi_version);
|
|
vpu->failure = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void handle_get_param_msg_ack(const struct vdec_vpu_ipi_get_param_ack *msg)
|
|
{
|
|
struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *)
|
|
(unsigned long)msg->ap_inst_addr;
|
|
|
|
mtk_vcodec_debug(vpu, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr);
|
|
|
|
/* param_type is enum vdec_get_param_type */
|
|
switch (msg->param_type) {
|
|
case GET_PARAM_PIC_INFO:
|
|
vpu->fb_sz[0] = msg->data[0];
|
|
vpu->fb_sz[1] = msg->data[1];
|
|
break;
|
|
default:
|
|
mtk_vcodec_err(vpu, "invalid get param type=%d", msg->param_type);
|
|
vpu->failure = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* vpu_dec_ipi_handler - Handler for VPU ipi message.
|
|
*
|
|
* @data: ipi message
|
|
* @len : length of ipi message
|
|
* @priv: callback private data which is passed by decoder when register.
|
|
*
|
|
* This function runs in interrupt context and it means there's an IPI MSG
|
|
* from VPU.
|
|
*/
|
|
static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv)
|
|
{
|
|
const struct vdec_vpu_ipi_ack *msg = data;
|
|
struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *)
|
|
(unsigned long)msg->ap_inst_addr;
|
|
|
|
if (!vpu) {
|
|
mtk_v4l2_err("ap_inst_addr is NULL, did the SCP hang or crash?");
|
|
return;
|
|
}
|
|
|
|
mtk_vcodec_debug(vpu, "+ id=%X", msg->msg_id);
|
|
|
|
vpu->failure = msg->status;
|
|
vpu->signaled = 1;
|
|
|
|
if (msg->status == 0) {
|
|
switch (msg->msg_id) {
|
|
case VPU_IPIMSG_DEC_INIT_ACK:
|
|
handle_init_ack_msg(data);
|
|
break;
|
|
|
|
case VPU_IPIMSG_DEC_START_ACK:
|
|
case VPU_IPIMSG_DEC_END_ACK:
|
|
case VPU_IPIMSG_DEC_DEINIT_ACK:
|
|
case VPU_IPIMSG_DEC_RESET_ACK:
|
|
case VPU_IPIMSG_DEC_CORE_ACK:
|
|
case VPU_IPIMSG_DEC_CORE_END_ACK:
|
|
break;
|
|
|
|
case VPU_IPIMSG_DEC_GET_PARAM_ACK:
|
|
handle_get_param_msg_ack(data);
|
|
break;
|
|
default:
|
|
mtk_vcodec_err(vpu, "invalid msg=%X", msg->msg_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
mtk_vcodec_debug(vpu, "- id=%X", msg->msg_id);
|
|
}
|
|
|
|
static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len)
|
|
{
|
|
int err, id, msgid;
|
|
|
|
msgid = *(uint32_t *)msg;
|
|
mtk_vcodec_debug(vpu, "id=%X", msgid);
|
|
|
|
vpu->failure = 0;
|
|
vpu->signaled = 0;
|
|
|
|
if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) {
|
|
if (msgid == AP_IPIMSG_DEC_CORE ||
|
|
msgid == AP_IPIMSG_DEC_CORE_END)
|
|
id = vpu->core_id;
|
|
else
|
|
id = vpu->id;
|
|
} else {
|
|
id = vpu->id;
|
|
}
|
|
|
|
err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, id, msg,
|
|
len, 2000);
|
|
if (err) {
|
|
mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d",
|
|
id, msgid, err);
|
|
return err;
|
|
}
|
|
|
|
return vpu->failure;
|
|
}
|
|
|
|
static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id)
|
|
{
|
|
struct vdec_ap_ipi_cmd msg;
|
|
int err = 0;
|
|
|
|
mtk_vcodec_debug(vpu, "+ id=%X", msg_id);
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_id = msg_id;
|
|
if (vpu->fw_abi_version < 2)
|
|
msg.vpu_inst_addr = vpu->inst_addr;
|
|
else
|
|
msg.inst_id = vpu->inst_id;
|
|
msg.codec_type = vpu->codec_type;
|
|
|
|
err = vcodec_vpu_send_msg(vpu, &msg, sizeof(msg));
|
|
mtk_vcodec_debug(vpu, "- id=%X ret=%d", msg_id, err);
|
|
return err;
|
|
}
|
|
|
|
int vpu_dec_init(struct vdec_vpu_inst *vpu)
|
|
{
|
|
struct vdec_ap_ipi_init msg;
|
|
int err;
|
|
|
|
mtk_vcodec_debug_enter(vpu);
|
|
|
|
init_waitqueue_head(&vpu->wq);
|
|
vpu->handler = vpu_dec_ipi_handler;
|
|
|
|
err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id,
|
|
vpu->handler, "vdec", NULL);
|
|
if (err) {
|
|
mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err);
|
|
return err;
|
|
}
|
|
|
|
if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) {
|
|
err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler,
|
|
vpu->core_id, vpu->handler,
|
|
"vdec", NULL);
|
|
if (err) {
|
|
mtk_vcodec_err(vpu, "vpu_ipi_register core fail status=%d", err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_id = AP_IPIMSG_DEC_INIT;
|
|
msg.ap_inst_addr = (unsigned long)vpu;
|
|
msg.codec_type = vpu->codec_type;
|
|
|
|
mtk_vcodec_debug(vpu, "vdec_inst=%p", vpu);
|
|
|
|
err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg));
|
|
mtk_vcodec_debug(vpu, "- ret=%d", err);
|
|
return err;
|
|
}
|
|
|
|
int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len)
|
|
{
|
|
struct vdec_ap_ipi_dec_start msg;
|
|
int i;
|
|
int err = 0;
|
|
|
|
mtk_vcodec_debug_enter(vpu);
|
|
|
|
if (len > ARRAY_SIZE(msg.data)) {
|
|
mtk_vcodec_err(vpu, "invalid len = %d\n", len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_id = AP_IPIMSG_DEC_START;
|
|
if (vpu->fw_abi_version < 2)
|
|
msg.vpu_inst_addr = vpu->inst_addr;
|
|
else
|
|
msg.inst_id = vpu->inst_id;
|
|
|
|
for (i = 0; i < len; i++)
|
|
msg.data[i] = data[i];
|
|
msg.codec_type = vpu->codec_type;
|
|
|
|
err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg));
|
|
mtk_vcodec_debug(vpu, "- ret=%d", err);
|
|
return err;
|
|
}
|
|
|
|
int vpu_dec_get_param(struct vdec_vpu_inst *vpu, uint32_t *data,
|
|
unsigned int len, unsigned int param_type)
|
|
{
|
|
struct vdec_ap_ipi_get_param msg;
|
|
int err;
|
|
|
|
mtk_vcodec_debug_enter(vpu);
|
|
|
|
if (len > ARRAY_SIZE(msg.data)) {
|
|
mtk_vcodec_err(vpu, "invalid len = %d\n", len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_id = AP_IPIMSG_DEC_GET_PARAM;
|
|
msg.inst_id = vpu->inst_id;
|
|
memcpy(msg.data, data, sizeof(unsigned int) * len);
|
|
msg.param_type = param_type;
|
|
msg.codec_type = vpu->codec_type;
|
|
|
|
err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg));
|
|
mtk_vcodec_debug(vpu, "- ret=%d", err);
|
|
return err;
|
|
}
|
|
|
|
int vpu_dec_core(struct vdec_vpu_inst *vpu)
|
|
{
|
|
return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE);
|
|
}
|
|
|
|
int vpu_dec_end(struct vdec_vpu_inst *vpu)
|
|
{
|
|
return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_END);
|
|
}
|
|
|
|
int vpu_dec_core_end(struct vdec_vpu_inst *vpu)
|
|
{
|
|
return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE_END);
|
|
}
|
|
|
|
int vpu_dec_deinit(struct vdec_vpu_inst *vpu)
|
|
{
|
|
return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_DEINIT);
|
|
}
|
|
|
|
int vpu_dec_reset(struct vdec_vpu_inst *vpu)
|
|
{
|
|
return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_RESET);
|
|
}
|