diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3c8d882ff14b..d940aa2365ff 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -544,6 +544,15 @@ config TILE_SROM source "drivers/char/xillybus/Kconfig" +config MSM_FASTCVPD + bool "QTI FASTCVP driver" + depends on QCOM_GLINK + help + This driver exposes APIs to video driver to share + HFI command queue address to CDSP and handle errors + to exit gracefully in case video and cdsp subsystems crash. + Say M if you want to enable this module. + config MSM_ADSPRPC tristate "QTI FastRPC driver" depends on RPMSG && QCOM_SECURE_BUFFER && MSM_SERVICE_LOCATOR && MSM_SERVICE_NOTIFIER && PM_SLEEP && HAS_DMA && CMA && MMU diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 219b2f6a3f54..babeeee732a0 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -61,3 +61,4 @@ endif obj-$(CONFIG_MSM_ADSPRPC) += frpc-adsprpc.o obj-$(CONFIG_ADI) += adi.o obj-$(CONFIG_MSM_RDBG) += rdbg.o +obj-$(CONFIG_MSM_FASTCVPD) += fastcvpd.o diff --git a/drivers/char/fastcvpd.c b/drivers/char/fastcvpd.c new file mode 100644 index 000000000000..2357cd6689ce --- /dev/null +++ b/drivers/char/fastcvpd.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include "linux/fastcvpd.h" + +#define VMID_CDSP_Q6 (30) +#define SRC_VM_NUM 1 +#define DEST_VM_NUM 2 +#define FASTCVPD_VIDEO_SEND_HFI_CMD_QUEUE 0 +#define FASTCVPD_VIDEO_SUSPEND 1 +#define FASTCVPD_VIDEO_RESUME 2 +#define FASTCVPD_VIDEO_SHUTDOWN 3 +#define STATUS_INIT 0 +#define STATUS_DEINIT 1 +#define STATUS_OK 2 +#define STATUS_SSR 3 + +struct fastcvpd_cmd_msg { + uint32_t cmd_msg_type; + int ret_val; + uint64_t msg_ptr; + uint32_t msg_ptr_len; +}; + +struct fastcvpd_cmd_msg_rsp { + int ret_val; +}; + +struct fastcvpd_apps { + struct rpmsg_device *chan; + struct mutex smd_mutex; + int rpmsg_register; + uint32_t cdsp_state; + uint32_t video_shutdown; +}; + +static struct completion work; + +static struct fastcvpd_apps gfa_cv; + +static struct fastcvpd_cmd_msg cmd_msg; + +static struct fastcvpd_cmd_msg_rsp cmd_msg_rsp; + +static int fastcvpd_send_cmd(void *msg, uint32_t len) +{ + struct fastcvpd_apps *me = &gfa_cv; + int err; + + if (IS_ERR_OR_NULL(me->chan)) { + err = -EINVAL; + goto bail; + } + err = rpmsg_send(me->chan->ept, msg, len); + +bail: + return err; +} + +static int fastcvpd_rpmsg_probe(struct rpmsg_device *rpdev) +{ + int err = 0; + struct fastcvpd_apps *me = &gfa_cv; + uint32_t cdsp_state, video_shutdown; + uint64_t msg_ptr; + uint32_t msg_ptr_len; + int srcVM[DEST_VM_NUM] = {VMID_HLOS, VMID_CDSP_Q6}; + int destVM[SRC_VM_NUM] = {VMID_HLOS}; + int destVMperm[SRC_VM_NUM] = { PERM_READ | PERM_WRITE | PERM_EXEC }; + + if (strcmp(rpdev->dev.parent->of_node->name, "cdsp")) { + pr_err("%s: Failed to probe rpmsg device.Node name:%s\n", + __func__, rpdev->dev.parent->of_node->name); + err = -EINVAL; + goto bail; + } + mutex_lock(&me->smd_mutex); + me->chan = rpdev; + cdsp_state = me->cdsp_state; + video_shutdown = me->video_shutdown; + msg_ptr = cmd_msg.msg_ptr; + msg_ptr_len = cmd_msg.msg_ptr_len; + mutex_unlock(&me->smd_mutex); + + if (cdsp_state == STATUS_SSR && video_shutdown == STATUS_OK) { + err = hyp_assign_phys((uint64_t)msg_ptr, + msg_ptr_len, srcVM, DEST_VM_NUM, destVM, + destVMperm, SRC_VM_NUM); + if (err) { + pr_err("%s: Failed to hyp_assign. err=%d\n", + __func__, err); + return err; + } + err = fastcvpd_video_send_cmd_hfi_queue( + (phys_addr_t *)msg_ptr, msg_ptr_len); + if (err) { + pr_err("%s: Failed to send HFI Queue address. err=%d\n", + __func__, err); + goto bail; + } + mutex_lock(&me->smd_mutex); + cdsp_state = me->cdsp_state; + mutex_unlock(&me->smd_mutex); + } + + pr_info("%s: Successfully probed. cdsp_state=%d video_shutdown=%d\n", + __func__, cdsp_state, video_shutdown); +bail: + return err; +} + +static void fastcvpd_rpmsg_remove(struct rpmsg_device *rpdev) +{ + struct fastcvpd_apps *me = &gfa_cv; + + mutex_lock(&me->smd_mutex); + me->chan = NULL; + me->cdsp_state = STATUS_SSR; + mutex_unlock(&me->smd_mutex); + pr_info("%s: CDSP SSR triggered\n", __func__); +} + +static int fastcvpd_rpmsg_callback(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 addr) +{ + int *rpmsg_resp = (int *)data; + + cmd_msg_rsp.ret_val = *rpmsg_resp; + complete(&work); + + return 0; +} + +int fastcvpd_video_send_cmd_hfi_queue(phys_addr_t *phys_addr, + uint32_t size_in_bytes) +{ + int err; + struct fastcvpd_cmd_msg local_cmd_msg; + struct fastcvpd_apps *me = &gfa_cv; + int srcVM[SRC_VM_NUM] = {VMID_HLOS}; + int destVM[DEST_VM_NUM] = {VMID_HLOS, VMID_CDSP_Q6}; + int destVMperm[DEST_VM_NUM] = { PERM_READ | PERM_WRITE | PERM_EXEC, + PERM_READ | PERM_WRITE | PERM_EXEC }; + + local_cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_SEND_HFI_CMD_QUEUE; + local_cmd_msg.msg_ptr = (uint64_t)phys_addr; + local_cmd_msg.msg_ptr_len = size_in_bytes; + mutex_lock(&me->smd_mutex); + cmd_msg.msg_ptr = (uint64_t)phys_addr; + cmd_msg.msg_ptr_len = (size_in_bytes); + mutex_unlock(&me->smd_mutex); + + pr_debug("%s :: address of buffer, PA=0x%pK size_buff=%d\n", + __func__, phys_addr, size_in_bytes); + + err = hyp_assign_phys((uint64_t)local_cmd_msg.msg_ptr, + local_cmd_msg.msg_ptr_len, srcVM, SRC_VM_NUM, destVM, + destVMperm, DEST_VM_NUM); + if (err) { + pr_err("%s: Failed in hyp_assign. err=%d\n", + __func__, err); + return err; + } + + err = fastcvpd_send_cmd + (&local_cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + else { + mutex_lock(&me->smd_mutex); + me->video_shutdown = STATUS_OK; + me->cdsp_state = STATUS_OK; + mutex_unlock(&me->smd_mutex); + } + + return err; +} +EXPORT_SYMBOL(fastcvpd_video_send_cmd_hfi_queue); + +int fastcvpd_video_suspend(uint32_t session_flag) +{ + int err = 0; + struct fastcvpd_cmd_msg local_cmd_msg; + struct fastcvpd_apps *me = &gfa_cv; + uint32_t cdsp_state; + + mutex_lock(&me->smd_mutex); + cdsp_state = me->cdsp_state; + mutex_unlock(&me->smd_mutex); + + if (cdsp_state == STATUS_SSR) + return 0; + + local_cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_SUSPEND; + err = fastcvpd_send_cmd + (&local_cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + + return err; +} +EXPORT_SYMBOL(fastcvpd_video_suspend); + +int fastcvpd_video_resume(uint32_t session_flag) +{ + int err; + struct fastcvpd_cmd_msg local_cmd_msg; + struct fastcvpd_apps *me = &gfa_cv; + uint32_t cdsp_state; + + mutex_lock(&me->smd_mutex); + cdsp_state = me->cdsp_state; + mutex_unlock(&me->smd_mutex); + + if (cdsp_state == STATUS_SSR) + return 0; + + local_cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_RESUME; + err = fastcvpd_send_cmd + (&local_cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + + return err; +} +EXPORT_SYMBOL(fastcvpd_video_resume); + +int fastcvpd_video_shutdown(uint32_t session_flag) +{ + struct fastcvpd_apps *me = &gfa_cv; + int err, local_cmd_msg_rsp; + struct fastcvpd_cmd_msg local_cmd_msg; + int srcVM[DEST_VM_NUM] = {VMID_HLOS, VMID_CDSP_Q6}; + int destVM[SRC_VM_NUM] = {VMID_HLOS}; + int destVMperm[SRC_VM_NUM] = { PERM_READ | PERM_WRITE | PERM_EXEC }; + + local_cmd_msg.cmd_msg_type = FASTCVPD_VIDEO_SHUTDOWN; + err = fastcvpd_send_cmd + (&local_cmd_msg, sizeof(struct fastcvpd_cmd_msg)); + if (err != 0) + pr_err("%s: fastcvpd_send_cmd failed with err=%d\n", + __func__, err); + + wait_for_completion(&work); + + mutex_lock(&me->smd_mutex); + me->video_shutdown = STATUS_SSR; + local_cmd_msg.msg_ptr = cmd_msg.msg_ptr; + local_cmd_msg.msg_ptr_len = cmd_msg.msg_ptr_len; + mutex_unlock(&me->smd_mutex); + local_cmd_msg_rsp = cmd_msg_rsp.ret_val; + if (local_cmd_msg_rsp == 0) { + err = hyp_assign_phys((uint64_t)local_cmd_msg.msg_ptr, + local_cmd_msg.msg_ptr_len, srcVM, DEST_VM_NUM, destVM, + destVMperm, SRC_VM_NUM); + if (err) { + pr_err("%s: Failed to hyp_assign. err=%d\n", + __func__, err); + return err; + } + } else { + pr_err("%s: Skipping hyp_assign as CDSP sent invalid response=%d\n", + __func__, local_cmd_msg_rsp); + } + + return err; +} +EXPORT_SYMBOL(fastcvpd_video_shutdown); + +static const struct rpmsg_device_id fastcvpd_rpmsg_match[] = { + { FASTCVPD_GLINK_GUID }, + { }, +}; + +static struct rpmsg_driver fastcvpd_rpmsg_client = { + .id_table = fastcvpd_rpmsg_match, + .probe = fastcvpd_rpmsg_probe, + .remove = fastcvpd_rpmsg_remove, + .callback = fastcvpd_rpmsg_callback, + .drv = { + .name = "qcom,msm_fastcvpd_rpmsg", + }, +}; + +static int __init fastcvpd_device_init(void) +{ + struct fastcvpd_apps *me = &gfa_cv; + int err; + + init_completion(&work); + mutex_init(&me->smd_mutex); + me->video_shutdown = STATUS_INIT; + me->cdsp_state = STATUS_INIT; + err = register_rpmsg_driver(&fastcvpd_rpmsg_client); + if (err) { + pr_err("%s : register_rpmsg_driver failed with err %d\n", + __func__, err); + goto register_bail; + } + me->rpmsg_register = 1; + return 0; + +register_bail: + me->video_shutdown = STATUS_DEINIT; + me->cdsp_state = STATUS_DEINIT; + return err; +} + +static void __exit fastcvpd_device_exit(void) +{ + struct fastcvpd_apps *me = &gfa_cv; + + me->video_shutdown = STATUS_DEINIT; + me->cdsp_state = STATUS_DEINIT; + mutex_destroy(&me->smd_mutex); + if (me->rpmsg_register == 1) + unregister_rpmsg_driver(&fastcvpd_rpmsg_client); +} + +late_initcall(fastcvpd_device_init); +module_exit(fastcvpd_device_exit); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/fastcvpd.h b/include/linux/fastcvpd.h new file mode 100644 index 000000000000..50914550365f --- /dev/null +++ b/include/linux/fastcvpd.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef FASTCVPD_H +#define FASTCVPD_H + +#include + +#define FASTCVPD_GLINK_GUID "fastcvpd-glink-apps-dsp" +#define FASTCVPD_SMD_GUID "fastcvpd-smd-apps-dsp" +#define FASTCVPD_DEVICE_NAME "fastcvpd-smd" + +#ifdef CONFIG_MSM_FASTCVPD +/* + * API for Video driver to send physical address to FastCVP driver + * @param phys_addr + * Physical address of command message queue + * that needs to be mapped to CDSP. + * It should be allocated from CMA adsp_mem region. + * + * @param size_in_bytes + * Size in bytes of command message queue + */ +int fastcvpd_video_send_cmd_hfi_queue(phys_addr_t *phys_addr, + uint32_t size_in_bytes); + +/* + * API for Video driver to suspend CVP session during + * power collapse + * + * @param session_flag + * Flag to share details of session. + */ +int fastcvpd_video_suspend(uint32_t session_flag); + +/* + * API for Video driver to resume CVP session during + * power collapse + * + * @param session_flag + * Flag to share details of session. + */ +int fastcvpd_video_resume(uint32_t session_flag); + +/* + * API for Video driver to shutdown CVP session during + * video subsystem error. + * + * @param session_flag + * Flag to share details of session. + */ +int fastcvpd_video_shutdown(uint32_t session_flag); + +#else + +static inline int fastcvpd_video_send_cmd_hfi_queue( + phys_addr_t *phys_addr, + uint32_t size_in_bytes) +{ + return -ENODEV; +} + +static inline int fastcvpd_video_shutdown(uint32_t session_flag) +{ + return -ENODEV; +} + +static inline int fastcvpd_video_suspend(uint32_t session_flag) +{ + return -ENODEV; +} + +static inline int fastcvpd_video_resume(uint32_t session_flag) +{ + return -ENODEV; +} + +#endif // CONFIG_FASTCVPD +#endif // FASTCVPD_H