diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index 29708708..93547e80 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Display Controller Engine code. # @@ -34,6 +34,7 @@ tegra-dce-$(CONFIG_TEGRA_DCE) += \ dce-ipc-signal.o \ dce-client-ipc.o \ dce-module.o \ + dce-pm.o \ dce-util-common.o ifeq ($(CONFIG_DEBUG_FS),y) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 83fb263c..b6173ed4 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -471,6 +471,79 @@ out: return ret; } +/** + * dce_admin_send_prepare_sc7 - Sends DCE_ADMIN_CMD_PREPARE_SC7 cmd. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ +int dce_admin_send_prepare_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PREPARE_SC7; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending prepare sc7 command [%d]", ret); + goto out; + } + +out: + return ret; +} + +/** + * dce_admin_send_enter_sc7 - Sends DCE_ADMIN_CMD_ENTER_SC7 cmd. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ +int dce_admin_send_enter_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_ENTER_SC7; + + ret = dce_ipc_send_message(d, DCE_IPC_CHANNEL_TYPE_ADMIN, msg->tx.data, msg->tx.size); + if (ret) { + dce_err(d, "Error sending enter sc7 command [%d]", ret); + goto out; + } + + /* Wait for SC7 Enter done */ + ret = dce_wait_interruptible(d, DCE_WAIT_SC7_ENTER); + if (ret) { + dce_err(d, "SC7 Enter wait was interrupted with err:%d", ret); + goto out; + } + +out: + return ret; +} + static int dce_admin_setup_clients_ipc(struct tegra_dce *d, struct dce_ipc_message *msg) { diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 31da8840..3a16164e 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -151,7 +151,7 @@ int dce_handle_boot_complete_received_event(struct tegra_dce *d, void *params) * * Return : 0 if successful else error code */ -static int +int dce_start_boot_flow(struct tegra_dce *d) { int ret = 0; @@ -241,8 +241,12 @@ void dce_handle_irq_status(struct tegra_dce *d, u32 status) NULL); } - if (status & DCE_IRQ_SC7_ENTERED) + if (status & DCE_IRQ_SC7_ENTERED) { dce_info(d, "DCE can be safely powered-off now"); + (void)dce_fsm_post_event(d, + EVENT_ID_DCE_SC7_ENTERED_RECEIVED, + NULL); + } if (status & DCE_IRQ_LOG_READY) { dce_info(d, "DCE trace log buffers available"); diff --git a/drivers/platform/tegra/dce/dce-fsm.c b/drivers/platform/tegra/dce/dce-fsm.c index 4430c8a5..c8e35c56 100644 --- a/drivers/platform/tegra/dce/dce-fsm.c +++ b/drivers/platform/tegra/dce/dce-fsm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -53,11 +53,15 @@ static struct dce_event_process_struct event_process_table[] = { }, { .event = EVENT_ID_DCE_SC7_ENTER_REQUESTED, - .fsm_event_handle = dce_handle_event_stub, + .fsm_event_handle = dce_pm_handle_sc7_enter_requested_event, }, { .event = EVENT_ID_DCE_SC7_ENTERED_RECEIVED, - .fsm_event_handle = dce_handle_event_stub, + .fsm_event_handle = dce_pm_handle_sc7_enter_received_event, + }, + { + .event = EVENT_ID_DCE_SC7_EXIT_RECEIVED, + .fsm_event_handle = dce_pm_handle_sc7_exit_received_event, }, { .event = EVENT_ID_DCE_LOG_REQUESTED, @@ -178,6 +182,11 @@ dce_fsm_set_state(struct tegra_dce *d, fsm->requested_ipcs &= ~DCE_BIT(DCE_WAIT_SC7_ENTER); break; + case EVENT_ID_DCE_SC7_EXIT_RECEIVED: + fsm->c_state = STATE_DCE_FSM_IDLE; + fsm->requested_ipcs = 0; + break; + case EVENT_ID_DCE_LOG_REQUESTED: fsm->c_state = STATE_DCE_LOG_READY_WFI; fsm->requested_ipcs |= DCE_BIT(DCE_WAIT_LOG); @@ -355,11 +364,16 @@ dce_fsm_validate_event(struct tegra_dce *d, } break; case STATE_DCE_SC7_ENTERED: - // - // STATE_DCE_SC7_ENTERED is short lived state for now - // FSM can expect only EVENT_ID_DCE_FSM_START event here - // - dce_err(d, "Event received while in STATE_DCE_SC7_ENTERED state"); + switch (event) { + case EVENT_ID_DCE_SC7_EXIT_RECEIVED: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } break; default: dce_err(d, "Invalid state:[%d] event received [%d]\n", curr_state, diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index aaba1de6..1931c3ae 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -59,6 +59,19 @@ static inline struct tegra_dce *dce_get_pdata_dce(struct platform_device *pdev) return (&((struct dce_device *)dev_get_drvdata(&pdev->dev))->d); } +/** + * dce_get_tegra_dce_from_dev - inline function to get the tegra_dce pointer + * from devicve struct. + * + * @pdev : Pointer to the device data structure. + * + * Return : Pointer pointing to tegra_dce data structure. + */ +static inline struct tegra_dce *dce_get_tegra_dce_from_dev(struct device *dev) +{ + return (&((struct dce_device *)dev_get_drvdata(dev))->d); +} + /** * dce_init_dev_data - Function to initialize the dce device data structure. * @@ -273,11 +286,35 @@ static int tegra_dce_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int dce_pm_suspend(struct device *dev) +{ + struct tegra_dce *d = dce_get_tegra_dce_from_dev(dev); + + return dce_pm_enter_sc7(d); +} + +static int dce_pm_resume(struct device *dev) +{ + struct tegra_dce *d = dce_get_tegra_dce_from_dev(dev); + + return dce_pm_exit_sc7(d); +} + +const struct dev_pm_ops dce_pm_ops = { + .suspend = dce_pm_suspend, + .resume = dce_pm_resume, +}; +#endif + static struct platform_driver tegra_dce_driver = { .driver = { .name = "tegra-dce", .of_match_table = of_match_ptr(tegra_dce_of_match), +#ifdef CONFIG_PM + .pm = &dce_pm_ops, +#endif }, .probe = tegra_dce_probe, .remove = tegra_dce_remove, diff --git a/drivers/platform/tegra/dce/dce-pm.c b/drivers/platform/tegra/dce/dce-pm.c new file mode 100644 index 00000000..ab16708b --- /dev/null +++ b/drivers/platform/tegra/dce/dce-pm.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 + +#define CCPLEX_HSP_IE 1U /* TODO : Have an api to read from platform data */ + +static void dce_pm_save_state(struct tegra_dce *d) +{ + d->sc7_state.hsp_ie = dce_hsp_ie_read(d, CCPLEX_HSP_IE); +} + +static void dce_pm_restore_state(struct tegra_dce *d) +{ + uint32_t val = d->sc7_state.hsp_ie; + + dce_hsp_ie_write(d, val, CCPLEX_HSP_IE); +} + +/** + * dce_resume_work_fn : execute resume and bootstrap flow + * + * @d : Pointer to tegra_dce struct. + * + * Return : void + */ +void dce_resume_work_fn(struct tegra_dce *d) +{ + int ret = 0; + + if (d == NULL) { + dce_err(d, "tegra_dce struct is NULL"); + return; + } + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED, NULL); + if (ret) { + dce_err(d, "Error while posting DCE_BOOT_COMPLETE_REQUESTED event"); + return; + } + + ret = dce_start_boot_flow(d); + if (ret) { + dce_err(d, "DCE bootstrapping failed\n"); + return; + } +} + +/** + * dce_handle_sc7_enter_requested_event - callback handler function for event + * EVENT_ID_DCE_SC7_ENTER_REQUESTED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_pm_handle_sc7_enter_requested_event(struct tegra_dce *d, void *params) +{ + int ret = 0; + struct dce_ipc_message *msg = NULL; + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + goto out; + } + + ret = dce_admin_send_enter_sc7(d, msg); + if (ret) { + dce_err(d, "Enter SC7 failed [%d]", ret); + goto out; + } + +out: + dce_admin_free_message(d, msg); + return ret; +} + +/** + * dce_handle_sc7_enter_received_event - callback handler function for event + * EVENT_ID_DCE_SC7_ENTER_RECEIVED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_pm_handle_sc7_enter_received_event(struct tegra_dce *d, void *params) +{ + dce_wakeup_interruptible(d, DCE_WAIT_SC7_ENTER); + return 0; +} + +/** + * dce_handle_sc7_exit_received_event - callback handler function for event + * EVENT_ID_DCE_SC7_EXIT_RECEIVED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_pm_handle_sc7_exit_received_event(struct tegra_dce *d, void *params) +{ + dce_schedule_work(&d->dce_resume_work); + return 0; +} + +int dce_pm_enter_sc7(struct tegra_dce *d) +{ + int ret = 0; + struct dce_ipc_message *msg = NULL; + + /* + * If Bootstrap is not yet done. Nothing to do during SC7 Enter + * Return success immediately. + */ + if (!dce_is_bootstrap_done(d)) { + dce_debug(d, "Bootstrap not done, Succeed SC7 enter\n"); + goto out; + } + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + ret = -1; + goto out; + } + + dce_pm_save_state(d); + + ret = dce_admin_send_prepare_sc7(d, msg); + if (ret) { + dce_err(d, "Prepare SC7 failed [%d]", ret); + ret = -1; + goto out; + } + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_SC7_ENTER_REQUESTED, NULL); + if (ret) { + dce_err(d, "Error while posting SC7_ENTER event [%d]", ret); + ret = -1; + goto out; + } + +out: + dce_admin_free_message(d, msg); + return ret; +} + +int dce_pm_exit_sc7(struct tegra_dce *d) +{ + int ret = 0; + + dce_pm_restore_state(d); + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_SC7_EXIT_RECEIVED, NULL); + if (ret) { + dce_err(d, "Error while posting SC7_EXIT event [%d]", ret); + goto out; + } +out: + return ret; +} diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index ace3198e..7305c0fd 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -85,7 +85,13 @@ int dce_work_cond_sw_resource_init(struct tegra_dce *d) ret = dce_init_work(d, &d->dce_bootstrap_work, dce_bootstrap_work_fn); if (ret) { - dce_err(d, "fsm_start work init failed"); + dce_err(d, "Bootstrap work init failed"); + goto exit; + } + + ret = dce_init_work(d, &d->dce_resume_work, dce_resume_work_fn); + if (ret) { + dce_err(d, "resume work init failed"); goto exit; } diff --git a/drivers/platform/tegra/dce/include/dce-fsm.h b/drivers/platform/tegra/dce/include/dce-fsm.h index be6d213c..8fcf2806 100644 --- a/drivers/platform/tegra/dce/include/dce-fsm.h +++ b/drivers/platform/tegra/dce/include/dce-fsm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -32,6 +32,7 @@ enum dce_fsm_event_id_type { EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED, EVENT_ID_DCE_SC7_ENTER_REQUESTED, EVENT_ID_DCE_SC7_ENTERED_RECEIVED, + EVENT_ID_DCE_SC7_EXIT_RECEIVED, EVENT_ID_DCE_LOG_REQUESTED, EVENT_ID_DCE_LOG_READY_RECEIVED, EVENT_ID_DCE_ABORT_RECEIVED, diff --git a/drivers/platform/tegra/dce/include/dce-pm.h b/drivers/platform/tegra/dce/include/dce-pm.h new file mode 100644 index 00000000..6cb4b3b1 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-pm.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef DCE_PM_H +#define DCE_PM_H + +#include + +struct dce_sc7_state { + uint32_t hsp_ie; +}; + +int dce_pm_enter_sc7(struct tegra_dce *d); +int dce_pm_exit_sc7(struct tegra_dce *d); +void dce_resume_work_fn(struct tegra_dce *d); +int dce_pm_handle_sc7_enter_requested_event(struct tegra_dce *d, void *params); +int dce_pm_handle_sc7_enter_received_event(struct tegra_dce *d, void *params); +int dce_pm_handle_sc7_exit_received_event(struct tegra_dce *d, void *params); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 7197cb73..24f32c82 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -148,6 +149,14 @@ struct tegra_dce { * dce_bootstrap_work : dce work to be executed to start FSM flow */ struct dce_work_struct dce_bootstrap_work; + /** + * dce_resume_work : dce work to executed dce resume flow + */ + struct dce_work_struct dce_resume_work; + /** + * dce_sc7_state : structure to save/restore state during sc7 enter/exit + */ + struct dce_sc7_state sc7_state; /** * dce_wait_info - Data structure to manage wait for different event types */ @@ -377,6 +386,7 @@ const char *dce_get_fw_name(struct tegra_dce *d); int dce_driver_init(struct tegra_dce *d); void dce_driver_deinit(struct tegra_dce *d); +int dce_start_boot_flow(struct tegra_dce *d); void dce_bootstrap_work_fn(struct tegra_dce *d); int dce_start_bootstrap_flow(struct tegra_dce *d); int dce_boot_interface_init(struct tegra_dce *d); @@ -402,6 +412,10 @@ int dce_admin_send_cmd_echo(struct tegra_dce *d, struct dce_ipc_message *msg); int dce_admin_send_cmd_ext_test(struct tegra_dce *d, struct dce_ipc_message *msg); +int dce_admin_send_prepare_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg); +int dce_admin_send_enter_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg); int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params); int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params); int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type);