// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2017 Linaro Ltd.
*/
#include <linux/init.h>
#include <linux/interconnect.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/devcoredump.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-ioctl.h>
#include "core.h"
#include "firmware.h"
#include "pm_helpers.h"
#include "hfi_venus_io.h"
static void venus_coredump(struct venus_core *core)
{
struct device *dev;
phys_addr_t mem_phys;
size_t mem_size;
void *mem_va;
void *data;
dev = core->dev;
mem_phys = core->fw.mem_phys;
mem_size = core->fw.mem_size;
mem_va = memremap(mem_phys, mem_size, MEMREMAP_WC);
if (!mem_va)
return;
data = vmalloc(mem_size);
if (!data) {
memunmap(mem_va);
return;
}
memcpy(data, mem_va, mem_size);
memunmap(mem_va);
dev_coredumpv(dev, data, mem_size, GFP_KERNEL);
}
static void venus_event_notify(struct venus_core *core, u32 event)
{
struct venus_inst *inst;
switch (event) {
case EVT_SYS_WATCHDOG_TIMEOUT:
case EVT_SYS_ERROR:
break;
default:
return;
}
mutex_lock(&core->lock);
set_bit(0, &core->sys_error);
set_bit(0, &core->dump_core);
list_for_each_entry(inst, &core->instances, list)
inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL);
mutex_unlock(&core->lock);
disable_irq_nosync(core->irq);
schedule_delayed_work(&core->work, msecs_to_jiffies(10));
}
static const struct hfi_core_ops venus_core_ops = {
.event_notify = venus_event_notify,
};
#define RPM_WAIT_FOR_IDLE_MAX_ATTEMPTS 10
static void venus_sys_error_handler(struct work_struct *work)
{
struct venus_core *core =
container_of(work, struct venus_core, work.work);
int ret, i, max_attempts = RPM_WAIT_FOR_IDLE_MAX_ATTEMPTS;
const char *err_msg = "";
bool failed = false;
ret = pm_runtime_get_sync(core->dev);
if (ret < 0) {
err_msg = "resume runtime PM";
max_attempts = 0;
failed = true;
}
core->ops->core_deinit(core);
core->state = CORE_UNINIT;
for (i = 0; i < max_attempts; i++) {
if (!pm_runtime_active(core->dev_dec) && !pm_runtime_active(core->dev_enc))
break;
msleep(10);
}
mutex_lock(&core->lock);
venus_shutdown(core);
if (test_bit(0, &core->dump_core)) {
venus_coredump(core);
clear_bit(0, &core->dump_core);
}
pm_runtime_put_sync(core->dev);
for (i = 0; i < max_attempts; i++) {
if (!core->pmdomains ||
!pm_runtime_active(core->pmdomains->pd_devs[0]))
break;
usleep_range(1000, 1500);
}
hfi_reinit(core);
ret = pm_runtime_get_sync(core->dev);
if (ret < 0) {
err_msg = "resume runtime PM";
failed = true;
}
ret = venus_boot(core);
if (ret && !failed) {
err_msg = "boot Venus";
failed = true;
}
ret = hfi_core_resume(core, true);
if (ret && !failed) {
err_msg = "resume HFI";
failed = true;
}
enable_irq(core->irq);
mutex_unlock(&core->lock);
ret = hfi_core_init(core);
if (ret && !failed) {
err_msg = "init HFI";
failed = true;
}
pm_runtime_put_sync(core->dev);
if (failed) {
disable_irq_nosync(core->irq);
dev_warn_ratelimited(core->dev,
"System error has occurred, recovery failed to %s\n",
err_msg);
schedule_delayed_work(&core->work, msecs_to_jiffies(10));
return;
}
dev_warn(core->dev, "system error has occurred (recovered)\n");
mutex_lock(&core->lock);
clear_bit(0, &core->sys_error);
wake_up_all(&core->sys_err_done);
mutex_unlock(&core->lock);
}
static u32 to_v4l2_codec_type(u32 codec)
{
switch (codec) {
case HFI_VIDEO_CODEC_H264:
return V4L2_PIX_FMT_H264;
case HFI_VIDEO_CODEC_H263:
return V4L2_PIX_FMT_H263;
case HFI_VIDEO_CODEC_MPEG1:
return V4L2_PIX_FMT_MPEG1;
case HFI_VIDEO_CODEC_MPEG2:
return V4L2_PIX_FMT_MPEG2;
case HFI_VIDEO_CODEC_MPEG4:
return V4L2_PIX_FMT_MPEG4;
case HFI_VIDEO_CODEC_VC1:
return V4L2_PIX_FMT_VC1_ANNEX_G;
case HFI_VIDEO_CODEC_VP8:
return V4L2_PIX_FMT_VP8;
case HFI_VIDEO_CODEC_VP9:
return V4L2_PIX_FMT_VP9;
case HFI_VIDEO_CODEC_DIVX:
case HFI_VIDEO_CODEC_DIVX_311:
return V4L2_PIX_FMT_XVID;
default:
return 0;
}
}
static int venus_enumerate_codecs(struct venus_core *core, u32 type)
{
const struct hfi_inst_ops dummy_ops = {};
struct venus_inst *inst;
u32 codec, codecs;
unsigned int i;
int ret;
if (core->res->