Browse Source

msm: camera: req_mgr: Enhance camera v4l2 subdev shutdown sequence

when provider crash occurred, there was a CSID lane overflow
observed due to shutdown sequence from CRM(sensor, csid, csiphy).
To fix the issue need to change the shutdown sequence
(csid, sensor, csiphy).

This change will update the devices sequence in order while registering
and close the sequence accordingly.

CRs-Fixed: 2852076
Change-Id: Ia6d8022e995823bf031400c33528eb8544dc2e29
Signed-off-by: Alok Chauhan <[email protected]>
Alok Chauhan 4 năm trước cách đây
mục cha
commit
2ebae4fabe
38 tập tin đã thay đổi với 848 bổ sung159 xóa
  1. 25 0
      drivers/cam_core/cam_context.c
  2. 17 0
      drivers/cam_core/cam_context.h
  3. 21 0
      drivers/cam_core/cam_node.c
  4. 27 1
      drivers/cam_core/cam_node.h
  5. 16 1
      drivers/cam_core/cam_subdev.c
  6. 23 1
      drivers/cam_cpas/cam_cpas_intf.c
  7. 18 0
      drivers/cam_cust/cam_custom_context.c
  8. 12 0
      drivers/cam_cust/cam_custom_context.h
  9. 16 2
      drivers/cam_cust/cam_custom_dev.c
  10. 25 3
      drivers/cam_fd/cam_fd_context.c
  11. 12 1
      drivers/cam_fd/cam_fd_context.h
  12. 21 2
      drivers/cam_fd/cam_fd_dev.c
  13. 18 3
      drivers/cam_icp/cam_icp_context.c
  14. 9 1
      drivers/cam_icp/cam_icp_context.h
  15. 19 6
      drivers/cam_icp/cam_icp_subdev.c
  16. 11 0
      drivers/cam_isp/cam_isp_context.c
  17. 11 0
      drivers/cam_isp/cam_isp_context.h
  18. 15 2
      drivers/cam_isp/cam_isp_dev.c
  19. 27 1
      drivers/cam_jpeg/cam_jpeg_context.c
  20. 13 1
      drivers/cam_jpeg/cam_jpeg_context.h
  21. 15 3
      drivers/cam_jpeg/cam_jpeg_dev.c
  22. 18 3
      drivers/cam_lrme/cam_lrme_context.c
  23. 13 1
      drivers/cam_lrme/cam_lrme_context.h
  24. 15 2
      drivers/cam_lrme/cam_lrme_dev.c
  25. 19 2
      drivers/cam_ope/cam_ope_context.c
  26. 9 1
      drivers/cam_ope/cam_ope_context.h
  27. 16 2
      drivers/cam_ope/cam_ope_subdev.c
  28. 63 3
      drivers/cam_req_mgr/cam_req_mgr_dev.c
  29. 7 1
      drivers/cam_req_mgr/cam_req_mgr_dev.h
  30. 32 1
      drivers/cam_req_mgr/cam_req_mgr_util.c
  31. 7 0
      drivers/cam_req_mgr/cam_req_mgr_util.h
  32. 28 1
      drivers/cam_req_mgr/cam_subdev.h
  33. 45 20
      drivers/cam_sensor_module/cam_actuator/cam_actuator_dev.c
  34. 41 18
      drivers/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
  35. 41 19
      drivers/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
  36. 41 19
      drivers/cam_sensor_module/cam_flash/cam_flash_dev.c
  37. 40 19
      drivers/cam_sensor_module/cam_ois/cam_ois_dev.c
  38. 42 19
      drivers/cam_sensor_module/cam_sensor/cam_sensor_dev.c

+ 25 - 0
drivers/cam_core/cam_context.c

@@ -556,6 +556,31 @@ int cam_context_handle_stop_dev(struct cam_context *ctx,
 	return rc;
 }
 
+int cam_context_handle_shutdown_dev(struct cam_context *ctx,
+	struct cam_control *cmd, struct v4l2_subdev_fh *fh)
+{
+	int rc = 0;
+
+	if (!ctx || !ctx->state_machine) {
+		CAM_ERR(CAM_CORE, "Context is not ready");
+		return -EINVAL;
+	}
+
+	if (!cmd) {
+		CAM_ERR(CAM_CORE, "Invalid stop device command payload");
+		return -EINVAL;
+	}
+
+	if (ctx->state_machine[ctx->state].ioctl_ops.shutdown_dev)
+		rc = ctx->state_machine[ctx->state].ioctl_ops.shutdown_dev(
+			(struct v4l2_subdev *)cmd->handle, fh);
+	else
+		CAM_WARN(CAM_CORE, "No shutdown device in dev %d, state %d",
+			ctx->dev_hdl, ctx->state);
+
+	return rc;
+}
+
 int cam_context_handle_info_dump(void *context,
 	enum cam_context_dump_id id)
 {

+ 17 - 0
drivers/cam_core/cam_context.h

@@ -9,6 +9,7 @@
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include <linux/kref.h>
+#include <media/v4l2-subdev.h>
 #include "cam_req_mgr_interface.h"
 #include "cam_hw_mgr_intf.h"
 #include "cam_smmu_api.h"
@@ -97,6 +98,7 @@ struct cam_ctx_request {
  * @acquire_hw:            Function pointer for acquire hw
  * @release_hw:            Function pointer for release hw
  * @dump_dev:              Function pointer for dump dev
+ * @shutdown_dev:          Function pointer for shutdown dev
  *
  */
 struct cam_ctx_ioctl_ops {
@@ -116,6 +118,8 @@ struct cam_ctx_ioctl_ops {
 	int (*release_hw)(struct cam_context *ctx, void *args);
 	int (*dump_dev)(struct cam_context *ctx,
 			struct cam_dump_req_cmd *cmd);
+	int (*shutdown_dev)(struct v4l2_subdev *sd,
+			struct v4l2_subdev_fh *fh);
 };
 
 /**
@@ -483,6 +487,19 @@ int cam_context_handle_start_dev(struct cam_context *ctx,
 int cam_context_handle_stop_dev(struct cam_context *ctx,
 		struct cam_start_stop_dev_cmd *cmd);
 
+/**
+ * cam_context_handle_shutdown_dev()
+ *
+ * @brief:        Handle shutdown device command
+ *
+ * @ctx:          Object pointer for cam_context
+ * @cmd:          Shutdown device command payload
+ * @fh:           Pointer to struct v4l2_subdev_fh
+ *
+ */
+int cam_context_handle_shutdown_dev(struct cam_context *ctx,
+	struct cam_control *cmd, struct v4l2_subdev_fh *fh);
+
 /**
  * cam_context_handle_dump_dev()
  *

+ 21 - 0
drivers/cam_core/cam_node.c

@@ -297,6 +297,27 @@ static int __cam_node_handle_stop_dev(struct cam_node *node,
 	return rc;
 }
 
+int cam_node_handle_shutdown_dev(struct cam_node *node,
+	struct cam_control *cmd, struct v4l2_subdev_fh *fh)
+{
+	struct cam_context *ctx = NULL;
+	int32_t dev_index = -1;
+	int rc = 0, ret = 0;
+
+	while ((dev_index = cam_get_dev_handle_info(cmd->handle,
+		&ctx, dev_index)) < CAM_REQ_MGR_MAX_HANDLES_V2) {
+		ret = cam_context_handle_shutdown_dev(ctx, cmd, fh);
+		if (ret) {
+			rc = ret;
+			CAM_ERR(CAM_CORE, "Shutdown failure for node %s",
+					node->name);
+			continue;
+		}
+	}
+
+	return rc;
+}
+
 static int __cam_node_handle_config_dev(struct cam_node *node,
 	struct cam_config_dev_cmd *config)
 {

+ 27 - 1
drivers/cam_core/cam_node.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2019, 2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_NODE_H_
@@ -100,4 +100,30 @@ int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf,
  */
 void cam_node_put_ctxt_to_free_list(struct kref *ref);
 
+/**
+ * cam_get_dev_handle_info()
+ *
+ * @brief:       provides the active dev index.
+ *
+ * @handle:      pointer to struct v4l2_dev
+ * @ctx:         pointer to struct cam_context
+ * @dev_index:   dev index
+ *
+ */
+int32_t cam_get_dev_handle_info(uint64_t handle,
+	struct cam_context **ctx, int32_t dev_index);
+
+/**
+ * cam_node_handle_shutdown_dev()
+ *
+ * @brief:       Shutdowns all the active devices.
+ *
+ * @node:        pointer to struct node
+ * @cmd:         pointer to struct cmd
+ * @fh:          pointer to struct v4l2_subdev_fh
+ *
+ */
+int cam_node_handle_shutdown_dev(struct cam_node *node,
+	struct cam_control *cmd, struct v4l2_subdev_fh *fh);
+
 #endif /* _CAM_NODE_H_ */

+ 16 - 1
drivers/cam_core/cam_subdev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 2021 The Linux Foundation. All rights reserved.
  */
 
 #include "cam_subdev.h"
@@ -45,6 +45,8 @@ static long cam_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
 	long rc;
 	struct cam_node *node =
 		(struct cam_node *) v4l2_get_subdevdata(sd);
+	struct v4l2_subdev_fh *fh = (struct v4l2_subdev_fh *)arg;
+	struct cam_control cntrl_cmd;
 
 	if (!node || node->state == CAM_NODE_STATE_UNINIT) {
 		rc = -EINVAL;
@@ -56,6 +58,19 @@ static long cam_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
 		rc = cam_node_handle_ioctl(node,
 			(struct cam_control *) arg);
 		break;
+	case CAM_SD_SHUTDOWN:
+		if (!cam_req_mgr_is_shutdown()) {
+			CAM_WARN(CAM_CORE, "SD shouldn't come from user space");
+			return 0;
+		}
+
+		cntrl_cmd.op_code = CAM_SD_SHUTDOWN;
+		cntrl_cmd.handle = (uint64_t)sd;
+		rc = cam_node_handle_shutdown_dev(node, &cntrl_cmd, fh);
+		if (rc)
+			CAM_ERR(CAM_CORE, "shutdown device failed(rc = %d)",
+				rc);
+		break;
 	default:
 		CAM_ERR(CAM_CORE, "Invalid command %d for %s", cmd,
 			node->name);

+ 23 - 1
drivers/cam_cpas/cam_cpas_intf.c

@@ -678,7 +678,7 @@ static int cam_cpas_subdev_open(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int cam_cpas_subdev_close(struct v4l2_subdev *sd,
+static int __cam_cpas_subdev_close(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	struct cam_cpas_intf *cpas_intf = v4l2_get_subdevdata(sd);
@@ -689,6 +689,11 @@ static int cam_cpas_subdev_close(struct v4l2_subdev *sd,
 	}
 
 	mutex_lock(&cpas_intf->intf_lock);
+	if (cpas_intf->open_cnt <= 0) {
+		CAM_WARN(CAM_CPAS, "device already closed, open_cnt: %d", cpas_intf->open_cnt);
+		mutex_unlock(&cpas_intf->intf_lock);
+		return 0;
+	}
 	cpas_intf->open_cnt--;
 	CAM_DBG(CAM_CPAS, "CPAS Subdev close count %d", cpas_intf->open_cnt);
 	mutex_unlock(&cpas_intf->intf_lock);
@@ -696,6 +701,19 @@ static int cam_cpas_subdev_close(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int cam_cpas_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_CPAS);
+
+	if (crm_active) {
+		CAM_DBG(CAM_CPAS, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return __cam_cpas_subdev_close(sd, fh);
+}
+
 static long cam_cpas_subdev_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, void *arg)
 {
@@ -711,6 +729,9 @@ static long cam_cpas_subdev_ioctl(struct v4l2_subdev *sd,
 	case VIDIOC_CAM_CONTROL:
 		rc = cam_cpas_subdev_cmd(cpas_intf, (struct cam_control *) arg);
 		break;
+	case CAM_SD_SHUTDOWN:
+		rc = __cam_cpas_subdev_close(sd, NULL);
+		break;
 	default:
 		CAM_ERR(CAM_CPAS, "Invalid command %d for CPAS!", cmd);
 		rc = -EINVAL;
@@ -798,6 +819,7 @@ static int cam_cpas_subdev_register(struct platform_device *pdev)
 	subdev->sd_flags =
 		V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
 	subdev->ent_function = CAM_CPAS_DEVICE_TYPE;
+	subdev->close_seq_prior = CAM_SD_CLOSE_LOW_PRIORITY;
 
 	rc = cam_register_subdev(subdev);
 	if (rc) {

+ 18 - 0
drivers/cam_cust/cam_custom_context.c

@@ -1604,6 +1604,19 @@ static int __cam_custom_ctx_apply_default_req(
 	return rc;
 }
 
+static int __cam_custom_ctx_shutdown_dev(
+	struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	int rc = -EINVAL;
+
+	if (!sd || !fh) {
+		CAM_ERR(CAM_CUSTOM, "Invalid input pointer");
+		return rc;
+	}
+
+	return cam_custom_subdev_close_internal(sd, fh);
+}
+
 /* top state machine */
 static struct cam_ctx_ops
 	cam_custom_dev_ctx_top_state_machine[CAM_CTX_STATE_MAX] = {
@@ -1618,6 +1631,7 @@ static struct cam_ctx_ops
 		.ioctl_ops = {
 			.acquire_dev =
 				__cam_custom_ctx_acquire_dev_in_available,
+			.shutdown_dev = __cam_custom_ctx_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
@@ -1629,6 +1643,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_custom_release_dev_in_acquired,
 			.config_dev = __cam_custom_ctx_config_dev_in_acquired,
 			.release_hw = __cam_custom_ctx_release_hw_in_top_state,
+			.shutdown_dev = __cam_custom_ctx_shutdown_dev,
 		},
 		.crm_ops = {
 			.link = __cam_custom_ctx_link_in_acquired,
@@ -1647,6 +1662,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_custom_release_dev_in_acquired,
 			.config_dev = __cam_custom_ctx_config_dev,
 			.release_hw = __cam_custom_ctx_release_hw_in_top_state,
+			.shutdown_dev = __cam_custom_ctx_shutdown_dev,
 		},
 		.crm_ops = {
 			.unlink = __cam_custom_ctx_unlink_in_ready,
@@ -1664,6 +1680,7 @@ static struct cam_ctx_ops
 			.config_dev = __cam_custom_ctx_config_dev_in_flushed,
 			.release_hw =
 				__cam_custom_ctx_release_hw_in_activated_state,
+			.shutdown_dev = __cam_custom_ctx_shutdown_dev,
 		},
 		.crm_ops = {
 			.unlink = __cam_custom_ctx_unlink_in_ready,
@@ -1680,6 +1697,7 @@ static struct cam_ctx_ops
 			.config_dev = __cam_custom_ctx_config_dev,
 			.release_hw =
 				__cam_custom_ctx_release_hw_in_activated_state,
+			.shutdown_dev = __cam_custom_ctx_shutdown_dev,
 		},
 		.crm_ops = {
 			.unlink = __cam_custom_ctx_unlink_in_activated,

+ 12 - 0
drivers/cam_cust/cam_custom_context.h

@@ -150,4 +150,16 @@ int cam_custom_dev_context_init(struct cam_custom_context *ctx,
  */
 int cam_custom_dev_context_deinit(struct cam_custom_context *ctx);
 
+/**
+ * cam_custom_subdev_close_internal()
+ *
+ * @brief:               Close function for the Custom context
+ *
+ * @sd:                  Pointer to struct v4l2_subdev
+ * @fh:                  Pointer to struct v4l2_subdev_fh
+ *
+ */
+int cam_custom_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh);
+
 #endif  /* _CAM_CUSTOM_CONTEXT_H_ */

+ 16 - 2
drivers/cam_cust/cam_custom_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -58,7 +58,7 @@ static int cam_custom_subdev_open(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int cam_custom_subdev_close(struct v4l2_subdev *sd,
+int cam_custom_subdev_close_internal(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	int rc = 0;
@@ -86,6 +86,19 @@ end:
 	return rc;
 }
 
+static int cam_custom_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_CUSTOM);
+
+	if (crm_active) {
+		CAM_DBG(CAM_CUSTOM, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_custom_subdev_close_internal(sd, fh);
+}
+
 static const struct v4l2_subdev_internal_ops cam_custom_subdev_internal_ops = {
 	.close = cam_custom_subdev_close,
 	.open = cam_custom_subdev_open,
@@ -102,6 +115,7 @@ static int cam_custom_component_bind(struct device *dev,
 	struct platform_device *pdev = to_platform_device(dev);
 
 	g_custom_dev.sd.internal_ops = &cam_custom_subdev_internal_ops;
+	g_custom_dev.sd.close_seq_prior = CAM_SD_CLOSE_HIGH_PRIORITY;
 
 	rc = cam_subdev_probe(&g_custom_dev.sd, pdev, CAM_CUSTOM_DEV_NAME,
 		CAM_CUSTOM_DEVICE_TYPE);

+ 25 - 3
drivers/cam_fd/cam_fd_context.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/module.h>
@@ -169,6 +169,19 @@ static int __cam_fd_ctx_handle_irq_in_activated(void *context,
 	return rc;
 }
 
+static int __cam_fd_shutdown_dev(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	int rc = -EINVAL;
+
+	if (!sd || !fh) {
+		CAM_ERR(CAM_FD, "Invalid input pointer");
+		return rc;
+	}
+
+	return cam_fd_dev_close_internal(sd, fh);
+}
+
 /* top state machine */
 static struct cam_ctx_ops
 	cam_fd_ctx_state_machine[CAM_CTX_STATE_MAX] = {
@@ -182,6 +195,7 @@ static struct cam_ctx_ops
 	{
 		.ioctl_ops = {
 			.acquire_dev = __cam_fd_ctx_acquire_dev_in_available,
+			.shutdown_dev = __cam_fd_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
@@ -192,18 +206,25 @@ static struct cam_ctx_ops
 			.release_dev = __cam_fd_ctx_release_dev_in_acquired,
 			.config_dev = __cam_fd_ctx_config_dev_in_acquired,
 			.start_dev = __cam_fd_ctx_start_dev_in_acquired,
+			.shutdown_dev = __cam_fd_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
 	},
 	/* Ready */
 	{
-		.ioctl_ops = { },
+		.ioctl_ops = {
+			.shutdown_dev = __cam_fd_shutdown_dev,
+		},
 		.crm_ops = {},
 		.irq_ops = NULL,
 	},
 	/* Flushed */
-	{},
+	{
+		.ioctl_ops = {
+			.shutdown_dev = __cam_fd_shutdown_dev,
+		},
+	},
 	/* Activated */
 	{
 		.ioctl_ops = {
@@ -212,6 +233,7 @@ static struct cam_ctx_ops
 			.config_dev = __cam_fd_ctx_config_dev_in_activated,
 			.flush_dev = __cam_fd_ctx_flush_dev_in_activated,
 			.dump_dev = __cam_fd_ctx_dump_dev_in_activated,
+			.shutdown_dev = __cam_fd_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_fd_ctx_handle_irq_in_activated,

+ 12 - 1
drivers/cam_fd/cam_fd_context.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_FD_CONTEXT_H_
@@ -27,4 +27,15 @@ int cam_fd_context_init(struct cam_fd_context *fd_ctx,
 	uint32_t ctx_id);
 int cam_fd_context_deinit(struct cam_fd_context *ctx);
 
+/**
+ * cam_fd_dev_close_internal()
+ *
+ * @brief:     Close function for the fd dev
+ *
+ * @sd:        Pointer to struct v4l2_subdev
+ * @fh:        Pointer to struct v4l2_subdev_fh
+ */
+int cam_fd_dev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh);
+
 #endif /* _CAM_FD_CONTEXT_H_ */

+ 21 - 2
drivers/cam_fd/cam_fd_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/device.h>
@@ -57,7 +57,7 @@ static int cam_fd_dev_open(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int cam_fd_dev_close(struct v4l2_subdev *sd,
+static int cam_fd_dev_close_internal(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	struct cam_fd_dev *fd_dev = &g_fd_dev;
@@ -69,6 +69,11 @@ static int cam_fd_dev_close(struct v4l2_subdev *sd,
 	}
 
 	mutex_lock(&fd_dev->lock);
+	if (fd_dev->open_cnt == 0) {
+		CAM_WARN(CAM_FD, "device already closed");
+		mutex_unlock(&fd_dev->lock);
+		return 0;
+	}
 	fd_dev->open_cnt--;
 	CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt);
 	mutex_unlock(&fd_dev->lock);
@@ -83,6 +88,19 @@ static int cam_fd_dev_close(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int cam_fd_dev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_FD);
+
+	if (crm_active) {
+		CAM_DBG(CAM_FD, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_fd_dev_close_internal(sd, fh);
+}
+
 static const struct v4l2_subdev_internal_ops cam_fd_subdev_internal_ops = {
 	.open = cam_fd_dev_open,
 	.close = cam_fd_dev_close,
@@ -98,6 +116,7 @@ static int cam_fd_dev_component_bind(struct device *dev,
 	struct platform_device *pdev = to_platform_device(dev);
 
 	g_fd_dev.sd.internal_ops = &cam_fd_subdev_internal_ops;
+	g_fd_dev.sd.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	/* Initialize the v4l2 subdevice first. (create cam_node) */
 	rc = cam_subdev_probe(&g_fd_dev.sd, pdev, CAM_FD_DEV_NAME,

+ 18 - 3
drivers/cam_icp/cam_icp_context.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/debugfs.h>
@@ -221,6 +221,12 @@ static int __cam_icp_handle_buf_done_in_ready(void *ctx,
 	return cam_context_buf_done_from_hw(ctx, done, evt_id);
 }
 
+static int __cam_icp_shutdown_dev(
+	struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return cam_icp_subdev_close_internal(sd, fh);
+}
+
 static struct cam_ctx_ops
 	cam_icp_ctx_state_machine[CAM_CTX_STATE_MAX] = {
 	/* Uninit */
@@ -233,6 +239,7 @@ static struct cam_ctx_ops
 	{
 		.ioctl_ops = {
 			.acquire_dev = __cam_icp_acquire_dev_in_available,
+			.shutdown_dev = __cam_icp_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
@@ -245,6 +252,7 @@ static struct cam_ctx_ops
 			.config_dev = __cam_icp_config_dev_in_ready,
 			.flush_dev = __cam_icp_flush_dev_in_ready,
 			.dump_dev = __cam_icp_dump_dev_in_ready,
+			.shutdown_dev = __cam_icp_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_icp_handle_buf_done_in_ready,
@@ -258,16 +266,23 @@ static struct cam_ctx_ops
 			.config_dev = __cam_icp_config_dev_in_ready,
 			.flush_dev = __cam_icp_flush_dev_in_ready,
 			.dump_dev = __cam_icp_dump_dev_in_ready,
+			.shutdown_dev = __cam_icp_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_icp_handle_buf_done_in_ready,
 		.pagefault_ops = cam_icp_context_dump_active_request,
 	},
 	/* Flushed */
-	{},
+	{
+		.ioctl_ops = {
+			.shutdown_dev = __cam_icp_shutdown_dev,
+		},
+	},
 	/* Activated */
 	{
-		.ioctl_ops = {},
+		.ioctl_ops = {
+			.shutdown_dev = __cam_icp_shutdown_dev,
+		},
 		.crm_ops = {},
 		.irq_ops = NULL,
 		.pagefault_ops = cam_icp_context_dump_active_request,

+ 9 - 1
drivers/cam_icp/cam_icp_context.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_ICP_CONTEXT_H_
@@ -39,4 +39,12 @@ int cam_icp_context_init(struct cam_icp_context *ctx,
  */
 int cam_icp_context_deinit(struct cam_icp_context *ctx);
 
+/**
+ * cam_icp_subdev_close_internal() - Close function for the icp dev
+ * @sd: Pointer to struct v4l2_subdev
+ * @fh: Pointer to struct v4l2_subdev_fh
+ */
+int cam_icp_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh);
+
 #endif /* _CAM_ICP_CONTEXT_H_ */

+ 19 - 6
drivers/cam_icp/cam_icp_subdev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -98,7 +98,7 @@ end:
 	return rc;
 }
 
-static int cam_icp_subdev_close(struct v4l2_subdev *sd,
+int cam_icp_subdev_close_internal(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	int rc = 0;
@@ -107,9 +107,8 @@ static int cam_icp_subdev_close(struct v4l2_subdev *sd,
 
 	mutex_lock(&g_icp_dev.icp_lock);
 	if (g_icp_dev.open_cnt <= 0) {
-		CAM_DBG(CAM_ICP, "ICP subdev is already closed");
-		rc = -EINVAL;
-		goto end;
+		CAM_WARN(CAM_ICP, "ICP subdev is already closed");
+		return 0;
 	}
 	g_icp_dev.open_cnt--;
 	if (!node) {
@@ -133,7 +132,20 @@ static int cam_icp_subdev_close(struct v4l2_subdev *sd,
 
 end:
 	mutex_unlock(&g_icp_dev.icp_lock);
-	return 0;
+	return rc;
+}
+
+static int cam_icp_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_ICP);
+
+	if (crm_active) {
+		CAM_DBG(CAM_ICP, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_icp_subdev_close_internal(sd, fh);
 }
 
 const struct v4l2_subdev_internal_ops cam_icp_subdev_internal_ops = {
@@ -157,6 +169,7 @@ static int cam_icp_component_bind(struct device *dev,
 
 	g_icp_dev.sd.pdev = pdev;
 	g_icp_dev.sd.internal_ops = &cam_icp_subdev_internal_ops;
+	g_icp_dev.sd.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 	rc = cam_subdev_probe(&g_icp_dev.sd, pdev, CAM_ICP_DEV_NAME,
 		CAM_ICP_DEVICE_TYPE);
 	if (rc) {

+ 11 - 0
drivers/cam_isp/cam_isp_context.c

@@ -5943,6 +5943,12 @@ static int __cam_isp_ctx_handle_irq_in_activated(void *context,
 	return rc;
 }
 
+static int __cam_isp_shutdown_dev(
+	struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return cam_isp_subdev_close_internal(sd, fh);
+}
+
 /* top state machine */
 static struct cam_ctx_ops
 	cam_isp_ctx_top_state_machine[CAM_CTX_STATE_MAX] = {
@@ -5956,6 +5962,7 @@ static struct cam_ctx_ops
 	{
 		.ioctl_ops = {
 			.acquire_dev = __cam_isp_ctx_acquire_dev_in_available,
+			.shutdown_dev = __cam_isp_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
@@ -5967,6 +5974,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_isp_ctx_release_dev_in_top_state,
 			.config_dev = __cam_isp_ctx_config_dev_in_acquired,
 			.release_hw = __cam_isp_ctx_release_hw_in_top_state,
+			.shutdown_dev = __cam_isp_shutdown_dev,
 		},
 		.crm_ops = {
 			.link = __cam_isp_ctx_link_in_acquired,
@@ -5986,6 +5994,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_isp_ctx_release_dev_in_top_state,
 			.config_dev = __cam_isp_ctx_config_dev_in_top_state,
 			.release_hw = __cam_isp_ctx_release_hw_in_top_state,
+			.shutdown_dev = __cam_isp_shutdown_dev,
 		},
 		.crm_ops = {
 			.unlink = __cam_isp_ctx_unlink_in_ready,
@@ -6003,6 +6012,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_isp_ctx_release_dev_in_activated,
 			.config_dev = __cam_isp_ctx_config_dev_in_flushed,
 			.release_hw = __cam_isp_ctx_release_hw_in_activated,
+			.shutdown_dev = __cam_isp_shutdown_dev,
 		},
 		.crm_ops = {
 			.unlink = __cam_isp_ctx_unlink_in_ready,
@@ -6019,6 +6029,7 @@ static struct cam_ctx_ops
 			.release_dev = __cam_isp_ctx_release_dev_in_activated,
 			.config_dev = __cam_isp_ctx_config_dev_in_top_state,
 			.release_hw = __cam_isp_ctx_release_hw_in_activated,
+			.shutdown_dev = __cam_isp_shutdown_dev,
 		},
 		.crm_ops = {
 			.unlink = __cam_isp_ctx_unlink_in_activated,

+ 11 - 0
drivers/cam_isp/cam_isp_context.h

@@ -365,5 +365,16 @@ int cam_isp_context_init(struct cam_isp_context *ctx,
  */
 int cam_isp_context_deinit(struct cam_isp_context *ctx);
 
+/**
+ * cam_isp_subdev_close_internal()
+ *
+ * @brief:              Close function for the isp dev
+ *
+ * @sd:                 Pointer to struct v4l2_subdev
+ * @fh:                 Pointer to struct v4l2_subdev_fh
+ *
+ */
+int cam_isp_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh);
 
 #endif  /* __CAM_ISP_CONTEXT_H__ */

+ 15 - 2
drivers/cam_isp/cam_isp_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -55,7 +55,7 @@ static int cam_isp_subdev_open(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int cam_isp_subdev_close(struct v4l2_subdev *sd,
+int cam_isp_subdev_close_internal(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	int rc = 0;
@@ -83,6 +83,18 @@ end:
 	return rc;
 }
 
+static int cam_isp_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_ISP);
+
+	if (crm_active) {
+		CAM_DBG(CAM_ISP, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+	return cam_isp_subdev_close_internal(sd, fh);
+}
+
 static const struct v4l2_subdev_internal_ops cam_isp_subdev_internal_ops = {
 	.close = cam_isp_subdev_close,
 	.open = cam_isp_subdev_open,
@@ -104,6 +116,7 @@ static int cam_isp_dev_component_bind(struct device *dev,
 		(const char **)&compat_str);
 
 	g_isp_dev.sd.internal_ops = &cam_isp_subdev_internal_ops;
+	g_isp_dev.sd.close_seq_prior = CAM_SD_CLOSE_HIGH_PRIORITY;
 	/* Initialize the v4l2 subdevice first. (create cam_node) */
 	if (strnstr(compat_str, "ife", strlen(compat_str))) {
 		rc = cam_subdev_probe(&g_isp_dev.sd, pdev, CAM_ISP_DEV_NAME,

+ 27 - 1
drivers/cam_jpeg/cam_jpeg_context.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/debugfs.h>
@@ -135,6 +135,12 @@ static int __cam_jpeg_ctx_stop_dev_in_acquired(struct cam_context *ctx,
 	return rc;
 }
 
+static int __cam_jpeg_shutdown_dev(
+	struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return cam_jpeg_subdev_close_internal(sd, fh);
+}
+
 /* top state machine */
 static struct cam_ctx_ops
 	cam_jpeg_ctx_state_machine[CAM_CTX_STATE_MAX] = {
@@ -148,6 +154,7 @@ static struct cam_ctx_ops
 	{
 		.ioctl_ops = {
 			.acquire_dev = __cam_jpeg_ctx_acquire_dev_in_available,
+			.shutdown_dev = __cam_jpeg_shutdown_dev,
 		},
 		.crm_ops = { },
 		.irq_ops = NULL,
@@ -160,11 +167,30 @@ static struct cam_ctx_ops
 			.stop_dev = __cam_jpeg_ctx_stop_dev_in_acquired,
 			.flush_dev = __cam_jpeg_ctx_flush_dev_in_acquired,
 			.dump_dev = __cam_jpeg_ctx_dump_dev_in_acquired,
+			.shutdown_dev = __cam_jpeg_shutdown_dev,
 		},
 		.crm_ops = { },
 		.irq_ops = __cam_jpeg_ctx_handle_buf_done_in_acquired,
 		.pagefault_ops = cam_jpeg_context_dump_active_request,
 	},
+	/* Ready */
+	{
+		.ioctl_ops = {
+			.shutdown_dev = __cam_jpeg_shutdown_dev,
+		},
+	},
+	/* Flushed */
+	{
+		.ioctl_ops = {
+			.shutdown_dev = __cam_jpeg_shutdown_dev,
+		},
+	},
+	/* Activated */
+	{
+		.ioctl_ops = {
+			.shutdown_dev = __cam_jpeg_shutdown_dev,
+		},
+	},
 };
 
 int cam_jpeg_context_init(struct cam_jpeg_context *ctx,

+ 13 - 1
drivers/cam_jpeg/cam_jpeg_context.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_JPEG_CONTEXT_H_
@@ -64,4 +64,16 @@ int cam_jpeg_context_init(struct cam_jpeg_context *ctx,
  */
 int cam_jpeg_context_deinit(struct cam_jpeg_context *ctx);
 
+/**
+ * cam_jpeg_subdev_close_internal()
+ *
+ * @brief: Close function for the jpeg dev
+ *
+ * @sd: Pointer to struct v4l2_subdev
+ * @fh: Pointer to struct v4l2_subdev_fh
+ *
+ */
+int cam_jpeg_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh);
+
 #endif  /* __CAM_JPEG_CONTEXT_H__ */

+ 15 - 3
drivers/cam_jpeg/cam_jpeg_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -56,13 +56,12 @@ static int cam_jpeg_subdev_open(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int cam_jpeg_subdev_close(struct v4l2_subdev *sd,
+int cam_jpeg_subdev_close_internal(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	int rc = 0;
 	struct cam_node *node = v4l2_get_subdevdata(sd);
 
-
 	mutex_lock(&g_jpeg_dev.jpeg_mutex);
 	if (g_jpeg_dev.open_cnt <= 0) {
 		CAM_DBG(CAM_JPEG, "JPEG subdev is already closed");
@@ -86,6 +85,18 @@ end:
 	return rc;
 }
 
+static int cam_jpeg_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_JPEG);
+
+	if (crm_active) {
+		CAM_DBG(CAM_JPEG, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+	return cam_jpeg_subdev_close_internal(sd, fh);
+}
+
 static const struct v4l2_subdev_internal_ops cam_jpeg_subdev_internal_ops = {
 	.close = cam_jpeg_subdev_close,
 	.open = cam_jpeg_subdev_open,
@@ -102,6 +113,7 @@ static int cam_jpeg_dev_component_bind(struct device *dev,
 	struct platform_device *pdev = to_platform_device(dev);
 
 	g_jpeg_dev.sd.internal_ops = &cam_jpeg_subdev_internal_ops;
+	g_jpeg_dev.sd.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 	rc = cam_subdev_probe(&g_jpeg_dev.sd, pdev, CAM_JPEG_DEV_NAME,
 		CAM_JPEG_DEVICE_TYPE);
 	if (rc) {

+ 18 - 3
drivers/cam_lrme/cam_lrme_context.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/module.h>
@@ -172,6 +172,12 @@ static int __cam_lrme_ctx_handle_irq_in_activated(void *context,
 	return rc;
 }
 
+static int __cam_lrme_shutdown_dev(
+	struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return cam_lrme_dev_close_internal(sd, fh);
+}
+
 /* top state machine */
 static struct cam_ctx_ops
 	cam_lrme_ctx_state_machine[CAM_CTX_STATE_MAX] = {
@@ -185,6 +191,7 @@ static struct cam_ctx_ops
 	{
 		.ioctl_ops = {
 			.acquire_dev = __cam_lrme_ctx_acquire_dev_in_available,
+			.shutdown_dev = __cam_lrme_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
@@ -195,18 +202,25 @@ static struct cam_ctx_ops
 			.config_dev = __cam_lrme_ctx_config_dev_in_activated,
 			.release_dev = __cam_lrme_ctx_release_dev_in_acquired,
 			.start_dev = __cam_lrme_ctx_start_dev_in_acquired,
+			.shutdown_dev = __cam_lrme_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
 	},
 	/* Ready */
 	{
-		.ioctl_ops = {},
+		.ioctl_ops = {
+			.shutdown_dev = __cam_lrme_shutdown_dev,
+		},
 		.crm_ops = {},
 		.irq_ops = NULL,
 	},
 	/* Flushed */
-	{},
+	{
+		.ioctl_ops = {
+			.shutdown_dev = __cam_lrme_shutdown_dev,
+	},
+	},
 	/* Activate */
 	{
 		.ioctl_ops = {
@@ -215,6 +229,7 @@ static struct cam_ctx_ops
 			.stop_dev = __cam_lrme_ctx_stop_dev_in_activated,
 			.flush_dev = __cam_lrme_ctx_flush_dev_in_activated,
 			.dump_dev = __cam_lrme_ctx_dump_dev_in_activated,
+			.shutdown_dev = __cam_lrme_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_lrme_ctx_handle_irq_in_activated,

+ 13 - 1
drivers/cam_lrme/cam_lrme_context.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2019, 2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_LRME_CONTEXT_H_
@@ -30,4 +30,16 @@ int cam_lrme_context_init(struct cam_lrme_context *lrme_ctx,
 	uint32_t index);
 int cam_lrme_context_deinit(struct cam_lrme_context *lrme_ctx);
 
+/**
+ * cam_lrme_dev_close_internal()
+ *
+ * @brief: Close function for the jpeg dev
+ *
+ * @sd: Pointer to struct v4l2_subdev
+ * @fh: Pointer to struct v4l2_subdev_fh
+ *
+ */
+int cam_lrme_dev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh);
+
 #endif /* _CAM_LRME_CONTEXT_H_ */

+ 15 - 2
drivers/cam_lrme/cam_lrme_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/device.h>
@@ -72,7 +72,7 @@ static int cam_lrme_dev_open(struct v4l2_subdev *sd,
 	return 0;
 }
 
-static int cam_lrme_dev_close(struct v4l2_subdev *sd,
+int cam_lrme_dev_close_internal(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	int rc = 0;
@@ -106,6 +106,18 @@ end:
 	return rc;
 }
 
+static int cam_lrme_dev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_LRME);
+
+	if (crm_active) {
+		CAM_DBG(CAM_LRME, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+	return cam_lrme_dev_close_internal(sd, fh);
+}
+
 static const struct v4l2_subdev_internal_ops cam_lrme_subdev_internal_ops = {
 	.open = cam_lrme_dev_open,
 	.close = cam_lrme_dev_close,
@@ -126,6 +138,7 @@ static int cam_lrme_component_bind(struct device *dev,
 		return -ENOMEM;
 	}
 	g_lrme_dev->sd.internal_ops = &cam_lrme_subdev_internal_ops;
+	g_lrme_dev->sd.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	mutex_init(&g_lrme_dev->lock);
 

+ 19 - 2
drivers/cam_ope/cam_ope_context.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/debugfs.h>
@@ -195,6 +195,12 @@ static int __cam_ope_handle_buf_done_in_ready(void *ctx,
 	return cam_context_buf_done_from_hw(ctx, done, evt_id);
 }
 
+static int __cam_ope_shutdown_dev(
+	struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return cam_ope_subdev_close_internal(sd, fh);
+}
+
 static struct cam_ctx_ops
 	cam_ope_ctx_state_machine[CAM_CTX_STATE_MAX] = {
 	/* Uninit */
@@ -207,6 +213,7 @@ static struct cam_ctx_ops
 	{
 		.ioctl_ops = {
 			.acquire_dev = __cam_ope_acquire_dev_in_available,
+			.shutdown_dev = __cam_ope_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = NULL,
@@ -219,6 +226,7 @@ static struct cam_ctx_ops
 			.config_dev = __cam_ope_config_dev_in_ready,
 			.flush_dev = __cam_ope_flush_dev_in_ready,
 			.dump_dev = __cam_ope_dump_dev_in_ready,
+			.shutdown_dev = __cam_ope_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_ope_handle_buf_done_in_ready,
@@ -232,14 +240,23 @@ static struct cam_ctx_ops
 			.config_dev = __cam_ope_config_dev_in_ready,
 			.flush_dev = __cam_ope_flush_dev_in_ready,
 			.dump_dev = __cam_ope_dump_dev_in_ready,
+			.shutdown_dev = __cam_ope_shutdown_dev,
 		},
 		.crm_ops = {},
 		.irq_ops = __cam_ope_handle_buf_done_in_ready,
 		.pagefault_ops = cam_ope_context_dump_active_request,
 	},
+	/* Flushed */
+	{
+		.ioctl_ops = {
+			.shutdown_dev = __cam_ope_shutdown_dev,
+		},
+	},
 	/* Activated */
 	{
-		.ioctl_ops = {},
+		.ioctl_ops = {
+			.shutdown_dev = __cam_ope_shutdown_dev,
+		},
 		.crm_ops = {},
 		.irq_ops = NULL,
 		.pagefault_ops = cam_ope_context_dump_active_request,

+ 9 - 1
drivers/cam_ope/cam_ope_context.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_OPE_CONTEXT_H_
@@ -41,4 +41,12 @@ int cam_ope_context_init(struct cam_ope_context *ctx,
  */
 int cam_ope_context_deinit(struct cam_ope_context *ctx);
 
+/**
+ * cam_ope_subdev_close_internal() - Close function for the icp dev
+ * @sd: Pointer to struct v4l2_subdev
+ * @fh: Pointer to struct v4l2_subdev_fh
+ */
+int cam_ope_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh);
+
 #endif /* _CAM_OPE_CONTEXT_H_ */

+ 16 - 2
drivers/cam_ope/cam_ope_subdev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/delay.h>
@@ -95,7 +95,7 @@ end:
 	return rc;
 }
 
-static int cam_ope_subdev_close(struct v4l2_subdev *sd,
+int cam_ope_subdev_close_internal(struct v4l2_subdev *sd,
 	struct v4l2_subdev_fh *fh)
 {
 	int rc = 0;
@@ -134,6 +134,19 @@ end:
 	return rc;
 }
 
+static int cam_ope_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_OPE);
+
+	if (crm_active) {
+		CAM_DBG(CAM_OPE, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_ope_subdev_close_internal(sd, fh);
+}
+
 const struct v4l2_subdev_internal_ops cam_ope_subdev_internal_ops = {
 	.open = cam_ope_subdev_open,
 	.close = cam_ope_subdev_close,
@@ -156,6 +169,7 @@ static int cam_ope_subdev_component_bind(struct device *dev,
 
 	g_ope_dev.sd.pdev = pdev;
 	g_ope_dev.sd.internal_ops = &cam_ope_subdev_internal_ops;
+	g_ope_dev.sd.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 	rc = cam_subdev_probe(&g_ope_dev.sd, pdev, OPE_DEV_NAME,
 		CAM_OPE_DEVICE_TYPE);
 	if (rc) {

+ 63 - 3
drivers/cam_req_mgr/cam_req_mgr_dev.c

@@ -17,6 +17,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/cam_req_mgr.h>
 #include <media/cam_defs.h>
+#include <linux/list_sort.h>
 
 #include "cam_req_mgr_dev.h"
 #include "cam_req_mgr_util.h"
@@ -32,6 +33,7 @@
 
 static struct cam_req_mgr_device g_dev;
 struct kmem_cache *g_cam_req_mgr_timer_cachep;
+static struct list_head cam_req_mgr_ordered_sd_list;
 
 static struct device_attribute camera_debug_sysfs_attr =
 	__ATTR(debug_node, 0600, NULL, cam_debug_sysfs_node_store);
@@ -122,6 +124,7 @@ static int cam_req_mgr_open(struct file *filep)
 	spin_unlock_bh(&g_dev.cam_eventq_lock);
 
 	g_dev.open_cnt++;
+	g_dev.read_active_dev_id_hdls = 0;
 	rc = cam_mem_mgr_init();
 	if (rc) {
 		g_dev.open_cnt--;
@@ -158,6 +161,7 @@ static unsigned int cam_req_mgr_poll(struct file *f,
 static int cam_req_mgr_close(struct file *filep)
 {
 	struct v4l2_subdev *sd;
+	struct cam_subdev *csd;
 	struct v4l2_fh *vfh = filep->private_data;
 	struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
 
@@ -172,18 +176,23 @@ static int cam_req_mgr_close(struct file *filep)
 	}
 
 	cam_req_mgr_handle_core_shutdown();
+	g_dev.shutdown_state = true;
 
-	list_for_each_entry(sd, &g_dev.v4l2_dev->subdevs, list) {
+	list_for_each_entry(csd, &cam_req_mgr_ordered_sd_list, list) {
+		sd = &csd->sd;
 		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
 			continue;
-		if (sd->internal_ops && sd->internal_ops->close) {
+		if (sd->internal_ops) {
 			CAM_DBG(CAM_CRM, "Invoke subdev close for device %s",
 				sd->name);
-			sd->internal_ops->close(sd, subdev_fh);
+			v4l2_subdev_call(sd, core, ioctl,
+				CAM_SD_SHUTDOWN, subdev_fh);
 		}
 	}
 
 	g_dev.open_cnt--;
+	g_dev.shutdown_state = false;
+	g_dev.read_active_dev_id_hdls = 0;
 	v4l2_fh_release(filep);
 
 	spin_lock_bh(&g_dev.cam_eventq_lock);
@@ -659,6 +668,51 @@ void cam_subdev_notify_message(u32 subdev_type,
 }
 EXPORT_SYMBOL(cam_subdev_notify_message);
 
+
+static int cam_req_mgr_ordered_list_cmp(void *priv,
+	struct list_head *head_1, struct list_head *head_2)
+{
+	struct cam_subdev *entry_1 =
+		list_entry(head_1, struct cam_subdev, list);
+	struct cam_subdev *entry_2 =
+		list_entry(head_2, struct cam_subdev, list);
+	int ret = -1;
+
+	if (entry_1->close_seq_prior > entry_2->close_seq_prior)
+		return 1;
+	else if (entry_1->close_seq_prior < entry_2->close_seq_prior)
+		return ret;
+	else
+		return 0;
+}
+
+bool cam_req_mgr_is_open(uint64_t dev_id)
+{
+	bool crm_status;
+	bool dev_id_status;
+
+	mutex_lock(&g_dev.cam_lock);
+	crm_status = g_dev.open_cnt ? true : false;
+
+	if (!g_dev.read_active_dev_id_hdls) {
+		g_dev.active_dev_id_hdls = cam_get_dev_handle_status();
+		g_dev.read_active_dev_id_hdls++;
+	}
+
+	dev_id_status = (g_dev.active_dev_id_hdls & dev_id) ? true : false;
+	crm_status &=  dev_id_status;
+	mutex_unlock(&g_dev.cam_lock);
+
+	return crm_status;
+}
+EXPORT_SYMBOL(cam_req_mgr_is_open);
+
+bool cam_req_mgr_is_shutdown(void)
+{
+	return g_dev.shutdown_state;
+}
+EXPORT_SYMBOL(cam_req_mgr_is_shutdown);
+
 int cam_register_subdev(struct cam_subdev *csd)
 {
 	struct v4l2_subdev *sd;
@@ -687,6 +741,10 @@ int cam_register_subdev(struct cam_subdev *csd)
 	sd->entity.pads = NULL;
 	sd->entity.function = csd->ent_function;
 
+	list_add(&csd->list, &cam_req_mgr_ordered_sd_list);
+	list_sort(NULL, &cam_req_mgr_ordered_sd_list,
+		cam_req_mgr_ordered_list_cmp);
+
 	rc = v4l2_device_register_subdev(g_dev.v4l2_dev, sd);
 	if (rc) {
 		CAM_ERR(CAM_CRM, "register subdev failed");
@@ -747,6 +805,7 @@ static int cam_req_mgr_component_master_bind(struct device *dev)
 		goto video_setup_fail;
 
 	g_dev.open_cnt = 0;
+	g_dev.shutdown_state = false;
 	mutex_init(&g_dev.cam_lock);
 	spin_lock_init(&g_dev.cam_eventq_lock);
 	mutex_init(&g_dev.dev_lock);
@@ -764,6 +823,7 @@ static int cam_req_mgr_component_master_bind(struct device *dev)
 	}
 
 	g_dev.state = true;
+	INIT_LIST_HEAD(&cam_req_mgr_ordered_sd_list);
 
 	if (g_cam_req_mgr_timer_cachep == NULL) {
 		g_cam_req_mgr_timer_cachep = kmem_cache_create("crm_timer",

+ 7 - 1
drivers/cam_req_mgr/cam_req_mgr_dev.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_REQ_MGR_DEV_H_
@@ -19,6 +19,9 @@
  * @cam_lock: per file handle lock
  * @cam_eventq: event queue
  * @cam_eventq_lock: lock for event queue
+ * @shutdown_state: shutdown state
+ * @active_dev_id_hdls: active dev id handles
+ * @read_active_dev_id_hdls: read active_dev_id_hdls status
  */
 struct cam_req_mgr_device {
 	struct video_device *video;
@@ -30,6 +33,9 @@ struct cam_req_mgr_device {
 	struct mutex cam_lock;
 	struct v4l2_fh *cam_eventq;
 	spinlock_t cam_eventq_lock;
+	bool shutdown_state;
+	uint64_t active_dev_id_hdls;
+	int read_active_dev_id_hdls;
 };
 
 #define CAM_REQ_MGR_GET_PAYLOAD_PTR(ev, type)        \

+ 32 - 1
drivers/cam_req_mgr/cam_req_mgr_util.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #define pr_fmt(fmt) "CAM-REQ-MGR_UTIL %s:%d " fmt, __func__, __LINE__
@@ -14,6 +14,7 @@
 #include <media/cam_req_mgr.h>
 #include "cam_req_mgr_util.h"
 #include "cam_debug_util.h"
+#include "cam_context.h"
 
 static struct cam_req_mgr_util_hdl_tbl *hdl_tbl;
 static DEFINE_SPINLOCK(hdl_tbl_lock);
@@ -209,6 +210,36 @@ int32_t cam_create_device_hdl(struct cam_create_dev_hdl *hdl_data)
 	return handle;
 }
 
+int32_t cam_get_dev_handle_info(uint64_t handle,
+	struct cam_context **ctx, int32_t dev_index)
+{
+	int32_t idx;
+	struct v4l2_subdev *sd = (struct v4l2_subdev *)handle;
+
+	for (idx = dev_index + 1; idx < CAM_REQ_MGR_MAX_HANDLES_V2; idx++) {
+		if (hdl_tbl->hdl[idx].state == HDL_ACTIVE) {
+			*ctx = (struct cam_context *)cam_get_device_priv(
+					hdl_tbl->hdl[idx].hdl_value);
+			if ((*ctx) && !strcmp(sd->name, (*ctx)->dev_name))
+				return idx;
+		}
+	}
+	*ctx = NULL;
+	return CAM_REQ_MGR_MAX_HANDLES_V2;
+}
+
+uint64_t cam_get_dev_handle_status(void)
+{
+	int32_t idx;
+	uint64_t active_dev_hdls = 0;
+
+	for (idx = 0; idx < CAM_REQ_MGR_MAX_HANDLES_V2; idx++)
+		if (hdl_tbl->hdl[idx].state == HDL_ACTIVE)
+			active_dev_hdls |= hdl_tbl->hdl[idx].dev_id;
+
+	return active_dev_hdls;
+}
+
 void *cam_get_device_priv(int32_t dev_hdl)
 {
 	int idx;

+ 7 - 0
drivers/cam_req_mgr/cam_req_mgr_util.h

@@ -166,4 +166,11 @@ int32_t cam_req_mgr_util_deinit(void);
  */
 int32_t cam_req_mgr_util_free_hdls(void);
 
+/**
+ * cam_get_dev_handle_status() - get dev handles status
+ *
+ * Returns dev handle status
+ */
+uint64_t cam_get_dev_handle_status(void);
+
 #endif /* _CAM_REQ_MGR_UTIL_API_H_ */

+ 28 - 1
drivers/cam_req_mgr/cam_subdev.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _CAM_SUBDEV_H_
@@ -20,6 +20,13 @@ enum cam_subdev_message_type_t {
 	CAM_SUBDEV_MESSAGE_IRQ_ERR = 0x1
 };
 
+/* Enum for close sequence priority */
+enum cam_subdev_close_seq_priority {
+	CAM_SD_CLOSE_HIGH_PRIORITY,
+	CAM_SD_CLOSE_MEDIUM_PRIORITY,
+	CAM_SD_CLOSE_LOW_PRIORITY
+};
+
 /**
  * struct cam_subdev - describes a camera sub-device
  *
@@ -38,6 +45,8 @@ enum cam_subdev_message_type_t {
  * @ent_function:          Media entity function type. Can be:
  *                             %CAM_IFE_DEVICE_TYPE - identifies as IFE device.
  *                             %CAM_ICP_DEVICE_TYPE - identifies as ICP device.
+ * @list:                  list pointer
+ * @close_seq_prior:         cam_subdev_close_seq_priority type
  *
  * Each instance of a subdev driver should create this struct, either
  * stand-alone or embedded in a larger struct. This structure should be
@@ -57,6 +66,8 @@ struct cam_subdev {
 					struct v4l2_subdev *sd,
 					enum cam_subdev_message_type_t msg_type,
 					uint32_t data);
+	struct list_head                       list;
+	enum cam_subdev_close_seq_priority     close_seq_prior;
 };
 
 /**
@@ -118,4 +129,20 @@ int cam_register_subdev(struct cam_subdev *sd);
  */
 int cam_unregister_subdev(struct cam_subdev *sd);
 
+/**
+ * cam_req_mgr_is_open()
+ *
+ * @brief:    This common utility function returns the crm active status
+ *
+ * @dev_id: device id type
+ */
+bool  cam_req_mgr_is_open(uint64_t dev_id);
+
+/**
+ * cam_req_mgr_is_shutdown()
+ *
+ * @brief:    This common utility function returns the shutdown state
+ */
+bool cam_req_mgr_is_shutdown(void);
+
 #endif /* _CAM_SUBDEV_H_ */

+ 45 - 20
drivers/cam_sensor_module/cam_actuator/cam_actuator_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include "cam_actuator_dev.h"
@@ -10,6 +10,38 @@
 #include "cam_trace.h"
 #include "camera_main.h"
 
+static int cam_actuator_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_actuator_ctrl_t *a_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!a_ctrl) {
+		CAM_ERR(CAM_ACTUATOR, "a_ctrl ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&(a_ctrl->actuator_mutex));
+	cam_actuator_shutdown(a_ctrl);
+	mutex_unlock(&(a_ctrl->actuator_mutex));
+
+	return 0;
+}
+
+static int cam_actuator_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_ACTUATOR);
+
+	if (crm_active) {
+		CAM_INFO(CAM_ACTUATOR,
+			"CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_actuator_subdev_close_internal(sd, fh);
+}
+
 static long cam_actuator_subdev_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, void *arg)
 {
@@ -24,6 +56,14 @@ static long cam_actuator_subdev_ioctl(struct v4l2_subdev *sd,
 			CAM_ERR(CAM_ACTUATOR,
 				"Failed for driver_cmd: %d", rc);
 		break;
+	case CAM_SD_SHUTDOWN:
+		if (!cam_req_mgr_is_shutdown()) {
+			CAM_ERR(CAM_CORE, "SD shouldn't come from user space");
+			return 0;
+		}
+
+		rc = cam_actuator_subdev_close_internal(sd, NULL);
+		break;
 	default:
 		CAM_ERR(CAM_ACTUATOR, "Invalid ioctl cmd: %u", cmd);
 		rc = -ENOIOCTLCMD;
@@ -77,24 +117,6 @@ static long cam_actuator_init_subdev_do_ioctl(struct v4l2_subdev *sd,
 }
 #endif
 
-static int cam_actuator_subdev_close(struct v4l2_subdev *sd,
-	struct v4l2_subdev_fh *fh)
-{
-	struct cam_actuator_ctrl_t *a_ctrl =
-		v4l2_get_subdevdata(sd);
-
-	if (!a_ctrl) {
-		CAM_ERR(CAM_ACTUATOR, "a_ctrl ptr is NULL");
-		return -EINVAL;
-	}
-
-	mutex_lock(&(a_ctrl->actuator_mutex));
-	cam_actuator_shutdown(a_ctrl);
-	mutex_unlock(&(a_ctrl->actuator_mutex));
-
-	return 0;
-}
-
 static struct v4l2_subdev_core_ops cam_actuator_subdev_core_ops = {
 	.ioctl = cam_actuator_subdev_ioctl,
 #ifdef CONFIG_COMPAT
@@ -127,10 +149,13 @@ static int cam_actuator_init_subdev(struct cam_actuator_ctrl_t *a_ctrl)
 	a_ctrl->v4l2_dev_str.ent_function =
 		CAM_ACTUATOR_DEVICE_TYPE;
 	a_ctrl->v4l2_dev_str.token = a_ctrl;
+	a_ctrl->v4l2_dev_str.close_seq_prior =
+		 CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	rc = cam_register_subdev(&(a_ctrl->v4l2_dev_str));
 	if (rc)
-		CAM_ERR(CAM_SENSOR, "Fail with cam_register_subdev rc: %d", rc);
+		CAM_ERR(CAM_ACTUATOR,
+			"Fail with cam_register_subdev rc: %d", rc);
 
 	return rc;
 }

+ 41 - 18
drivers/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c

@@ -76,6 +76,37 @@ static void cam_csiphy_debug_unregister(void)
 	root_dentry = NULL;
 }
 
+static int cam_csiphy_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct csiphy_device *csiphy_dev =
+		v4l2_get_subdevdata(sd);
+
+	if (!csiphy_dev) {
+		CAM_ERR(CAM_CSIPHY, "csiphy_dev ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&csiphy_dev->mutex);
+	cam_csiphy_shutdown(csiphy_dev);
+	mutex_unlock(&csiphy_dev->mutex);
+
+	return 0;
+}
+
+static int cam_csiphy_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_CSIPHY);
+
+	if (crm_active) {
+		CAM_INFO(CAM_CSIPHY, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_csiphy_subdev_close_internal(sd, fh);
+}
+
 static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, void *arg)
 {
@@ -89,6 +120,14 @@ static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
 			CAM_ERR(CAM_CSIPHY,
 				"Failed in configuring the device: %d", rc);
 		break;
+	case CAM_SD_SHUTDOWN:
+		if (!cam_req_mgr_is_shutdown()) {
+			CAM_ERR(CAM_CORE, "SD shouldn't come from user space");
+			return 0;
+		}
+
+		rc = cam_csiphy_subdev_close_internal(sd, NULL);
+		break;
 	default:
 		CAM_ERR(CAM_CSIPHY, "Wrong ioctl : %d", cmd);
 		rc = -ENOIOCTLCMD;
@@ -98,24 +137,6 @@ static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
 	return rc;
 }
 
-static int cam_csiphy_subdev_close(struct v4l2_subdev *sd,
-	struct v4l2_subdev_fh *fh)
-{
-	struct csiphy_device *csiphy_dev =
-		v4l2_get_subdevdata(sd);
-
-	if (!csiphy_dev) {
-		CAM_ERR(CAM_CSIPHY, "csiphy_dev ptr is NULL");
-		return -EINVAL;
-	}
-
-	mutex_lock(&csiphy_dev->mutex);
-	cam_csiphy_shutdown(csiphy_dev);
-	mutex_unlock(&csiphy_dev->mutex);
-
-	return 0;
-}
-
 #ifdef CONFIG_COMPAT
 static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, unsigned long arg)
@@ -226,6 +247,8 @@ static int cam_csiphy_component_bind(struct device *dev,
 		cam_csiphy_subdev_handle_message;
 	new_csiphy_dev->v4l2_dev_str.token =
 		new_csiphy_dev;
+	new_csiphy_dev->v4l2_dev_str.close_seq_prior =
+		CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	rc = cam_register_subdev(&(new_csiphy_dev->v4l2_dev_str));
 	if (rc < 0) {

+ 41 - 19
drivers/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include "cam_eeprom_dev.h"
@@ -10,6 +10,37 @@
 #include "cam_debug_util.h"
 #include "camera_main.h"
 
+static int cam_eeprom_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_eeprom_ctrl_t *e_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!e_ctrl) {
+		CAM_ERR(CAM_EEPROM, "e_ctrl ptr is NULL");
+			return -EINVAL;
+	}
+
+	mutex_lock(&(e_ctrl->eeprom_mutex));
+	cam_eeprom_shutdown(e_ctrl);
+	mutex_unlock(&(e_ctrl->eeprom_mutex));
+
+	return 0;
+}
+
+static int cam_eeprom_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_EEPROM);
+
+	if (crm_active) {
+		CAM_INFO(CAM_EEPROM, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_eeprom_subdev_close_internal(sd, fh);
+}
+
 static long cam_eeprom_subdev_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, void *arg)
 {
@@ -23,6 +54,14 @@ static long cam_eeprom_subdev_ioctl(struct v4l2_subdev *sd,
 			CAM_ERR(CAM_EEPROM,
 				"Failed in Driver cmd: %d", rc);
 		break;
+	case CAM_SD_SHUTDOWN:
+		if (!cam_req_mgr_is_shutdown()) {
+			CAM_ERR(CAM_CORE, "SD shouldn't come from user space");
+			return 0;
+		}
+
+		rc = cam_eeprom_subdev_close_internal(sd, NULL);
+		break;
 	default:
 		rc = -ENOIOCTLCMD;
 		break;
@@ -31,24 +70,6 @@ static long cam_eeprom_subdev_ioctl(struct v4l2_subdev *sd,
 	return rc;
 }
 
-static int cam_eeprom_subdev_close(struct v4l2_subdev *sd,
-	struct v4l2_subdev_fh *fh)
-{
-	struct cam_eeprom_ctrl_t *e_ctrl =
-		v4l2_get_subdevdata(sd);
-
-	if (!e_ctrl) {
-		CAM_ERR(CAM_EEPROM, "e_ctrl ptr is NULL");
-			return -EINVAL;
-	}
-
-	mutex_lock(&(e_ctrl->eeprom_mutex));
-	cam_eeprom_shutdown(e_ctrl);
-	mutex_unlock(&(e_ctrl->eeprom_mutex));
-
-	return 0;
-}
-
 int32_t cam_eeprom_update_i2c_info(struct cam_eeprom_ctrl_t *e_ctrl,
 	struct cam_eeprom_i2c_info_t *i2c_info)
 {
@@ -147,6 +168,7 @@ static int cam_eeprom_init_subdev(struct cam_eeprom_ctrl_t *e_ctrl)
 		(V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
 	e_ctrl->v4l2_dev_str.ent_function = CAM_EEPROM_DEVICE_TYPE;
 	e_ctrl->v4l2_dev_str.token = e_ctrl;
+	e_ctrl->v4l2_dev_str.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	rc = cam_register_subdev(&(e_ctrl->v4l2_dev_str));
 	if (rc)

+ 41 - 19
drivers/cam_sensor_module/cam_flash/cam_flash_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/module.h>
@@ -236,6 +236,37 @@ static const struct of_device_id cam_flash_dt_match[] = {
 	{}
 };
 
+static int cam_flash_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_flash_ctrl *fctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!fctrl) {
+		CAM_ERR(CAM_FLASH, "Flash ctrl ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&fctrl->flash_mutex);
+	cam_flash_shutdown(fctrl);
+	mutex_unlock(&fctrl->flash_mutex);
+
+	return 0;
+}
+
+static int cam_flash_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_FLASH);
+
+	if (crm_active) {
+		CAM_INFO(CAM_FLASH, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_flash_subdev_close_internal(sd, fh);
+}
+
 static long cam_flash_subdev_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, void *arg)
 {
@@ -257,6 +288,14 @@ static long cam_flash_subdev_ioctl(struct v4l2_subdev *sd,
 				"Failed in driver cmd: %d", rc);
 		break;
 	}
+	case CAM_SD_SHUTDOWN:
+		if (!cam_req_mgr_is_shutdown()) {
+			CAM_ERR(CAM_CORE, "SD shouldn't come from user space");
+			return 0;
+		}
+
+		rc = cam_flash_subdev_close_internal(sd, NULL);
+		break;
 	default:
 		CAM_ERR(CAM_FLASH, "Invalid ioctl cmd type");
 		rc = -ENOIOCTLCMD;
@@ -328,24 +367,6 @@ static int32_t cam_flash_i2c_driver_remove(struct i2c_client *client)
 	return rc;
 }
 
-static int cam_flash_subdev_close(struct v4l2_subdev *sd,
-	struct v4l2_subdev_fh *fh)
-{
-	struct cam_flash_ctrl *fctrl =
-		v4l2_get_subdevdata(sd);
-
-	if (!fctrl) {
-		CAM_ERR(CAM_FLASH, "Flash ctrl ptr is NULL");
-		return -EINVAL;
-	}
-
-	mutex_lock(&fctrl->flash_mutex);
-	cam_flash_shutdown(fctrl);
-	mutex_unlock(&fctrl->flash_mutex);
-
-	return 0;
-}
-
 static struct v4l2_subdev_core_ops cam_flash_subdev_core_ops = {
 	.ioctl = cam_flash_subdev_ioctl,
 #ifdef CONFIG_COMPAT
@@ -375,6 +396,7 @@ static int cam_flash_init_subdev(struct cam_flash_ctrl *fctrl)
 		V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
 	fctrl->v4l2_dev_str.ent_function = CAM_FLASH_DEVICE_TYPE;
 	fctrl->v4l2_dev_str.token = fctrl;
+	fctrl->v4l2_dev_str.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	rc = cam_register_subdev(&(fctrl->v4l2_dev_str));
 	if (rc)

+ 40 - 19
drivers/cam_sensor_module/cam_ois/cam_ois_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include "cam_ois_dev.h"
@@ -10,6 +10,37 @@
 #include "cam_debug_util.h"
 #include "camera_main.h"
 
+static int cam_ois_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_ois_ctrl_t *o_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "o_ctrl ptr is NULL");
+			return -EINVAL;
+	}
+
+	mutex_lock(&(o_ctrl->ois_mutex));
+	cam_ois_shutdown(o_ctrl);
+	mutex_unlock(&(o_ctrl->ois_mutex));
+
+	return 0;
+}
+
+static int cam_ois_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_OIS);
+
+	if (crm_active) {
+		CAM_INFO(CAM_OIS, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_ois_subdev_close_internal(sd, fh);
+}
+
 static long cam_ois_subdev_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, void *arg)
 {
@@ -23,6 +54,13 @@ static long cam_ois_subdev_ioctl(struct v4l2_subdev *sd,
 			CAM_ERR(CAM_OIS,
 				"Failed with driver cmd: %d", rc);
 		break;
+	case CAM_SD_SHUTDOWN:
+		if (!cam_req_mgr_is_shutdown()) {
+			CAM_ERR(CAM_CORE, "SD shouldn't come from user space");
+			return 0;
+		}
+		rc = cam_ois_subdev_close_internal(sd, NULL);
+		break;
 	default:
 		CAM_ERR(CAM_OIS, "Wrong IOCTL cmd: %u", cmd);
 		rc = -ENOIOCTLCMD;
@@ -32,24 +70,6 @@ static long cam_ois_subdev_ioctl(struct v4l2_subdev *sd,
 	return rc;
 }
 
-static int cam_ois_subdev_close(struct v4l2_subdev *sd,
-	struct v4l2_subdev_fh *fh)
-{
-	struct cam_ois_ctrl_t *o_ctrl =
-		v4l2_get_subdevdata(sd);
-
-	if (!o_ctrl) {
-		CAM_ERR(CAM_OIS, "o_ctrl ptr is NULL");
-			return -EINVAL;
-	}
-
-	mutex_lock(&(o_ctrl->ois_mutex));
-	cam_ois_shutdown(o_ctrl);
-	mutex_unlock(&(o_ctrl->ois_mutex));
-
-	return 0;
-}
-
 static int32_t cam_ois_update_i2c_info(struct cam_ois_ctrl_t *o_ctrl,
 	struct cam_ois_i2c_info_t *i2c_info)
 {
@@ -144,6 +164,7 @@ static int cam_ois_init_subdev_param(struct cam_ois_ctrl_t *o_ctrl)
 		(V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
 	o_ctrl->v4l2_dev_str.ent_function = CAM_OIS_DEVICE_TYPE;
 	o_ctrl->v4l2_dev_str.token = o_ctrl;
+	 o_ctrl->v4l2_dev_str.close_seq_prior = CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	rc = cam_register_subdev(&(o_ctrl->v4l2_dev_str));
 	if (rc)

+ 42 - 19
drivers/cam_sensor_module/cam_sensor/cam_sensor_dev.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  */
 
 #include "cam_sensor_dev.h"
@@ -9,6 +9,37 @@
 #include "cam_sensor_core.h"
 #include "camera_main.h"
 
+static int cam_sensor_subdev_close_internal(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_sensor_ctrl_t *s_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!s_ctrl) {
+		CAM_ERR(CAM_SENSOR, "s_ctrl ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&(s_ctrl->cam_sensor_mutex));
+	cam_sensor_shutdown(s_ctrl);
+	mutex_unlock(&(s_ctrl->cam_sensor_mutex));
+
+	return 0;
+}
+
+static int cam_sensor_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	bool crm_active = cam_req_mgr_is_open(CAM_SENSOR);
+
+	if (crm_active) {
+		CAM_INFO(CAM_SENSOR, "CRM is ACTIVE, close should be from CRM");
+		return 0;
+	}
+
+	return cam_sensor_subdev_close_internal(sd, fh);
+}
+
 static long cam_sensor_subdev_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, void *arg)
 {
@@ -23,6 +54,14 @@ static long cam_sensor_subdev_ioctl(struct v4l2_subdev *sd,
 			CAM_ERR(CAM_SENSOR,
 				"Failed in Driver cmd: %d", rc);
 		break;
+	case CAM_SD_SHUTDOWN:
+		if (!cam_req_mgr_is_shutdown()) {
+			CAM_ERR(CAM_CORE, "SD shouldn't come from user space");
+			return 0;
+		}
+
+		rc = cam_sensor_subdev_close_internal(sd, NULL);
+		break;
 	default:
 		CAM_ERR(CAM_SENSOR, "Invalid ioctl cmd: %d", cmd);
 		rc = -ENOIOCTLCMD;
@@ -31,24 +70,6 @@ static long cam_sensor_subdev_ioctl(struct v4l2_subdev *sd,
 	return rc;
 }
 
-static int cam_sensor_subdev_close(struct v4l2_subdev *sd,
-	struct v4l2_subdev_fh *fh)
-{
-	struct cam_sensor_ctrl_t *s_ctrl =
-		v4l2_get_subdevdata(sd);
-
-	if (!s_ctrl) {
-		CAM_ERR(CAM_SENSOR, "s_ctrl ptr is NULL");
-		return -EINVAL;
-	}
-
-	mutex_lock(&(s_ctrl->cam_sensor_mutex));
-	cam_sensor_shutdown(s_ctrl);
-	mutex_unlock(&(s_ctrl->cam_sensor_mutex));
-
-	return 0;
-}
-
 #ifdef CONFIG_COMPAT
 static long cam_sensor_init_subdev_do_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, unsigned long arg)
@@ -122,6 +143,8 @@ static int cam_sensor_init_subdev_params(struct cam_sensor_ctrl_t *s_ctrl)
 	s_ctrl->v4l2_dev_str.ent_function =
 		CAM_SENSOR_DEVICE_TYPE;
 	s_ctrl->v4l2_dev_str.token = s_ctrl;
+	s_ctrl->v4l2_dev_str.close_seq_prior =
+		CAM_SD_CLOSE_MEDIUM_PRIORITY;
 
 	rc = cam_register_subdev(&(s_ctrl->v4l2_dev_str));
 	if (rc)