Эх сурвалжийг харах

Merge "msm: camera: req_mgr: Enhance camera v4l2 subdev shutdown sequence" into camera-kernel.lnx.5.0

Savita Patted 4 жил өмнө
parent
commit
68de607556
38 өөрчлөгдсөн 846 нэмэгдсэн , 157 устгасан
  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. 31 0
      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. 39 18
      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

@@ -610,6 +610,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_types.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);
 };
 
 /**
@@ -513,6 +517,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

@@ -779,7 +779,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);
@@ -790,6 +790,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);
@@ -797,6 +802,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)
 {
@@ -812,6 +830,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;
@@ -899,6 +920,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

@@ -1605,6 +1605,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] = {
@@ -1619,6 +1632,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,
@@ -1630,6 +1644,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,
@@ -1648,6 +1663,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,
@@ -1665,6 +1681,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,
@@ -1681,6 +1698,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

@@ -6405,6 +6405,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] = {
@@ -6418,6 +6424,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,
@@ -6429,6 +6436,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,
@@ -6448,6 +6456,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,
@@ -6465,6 +6474,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,
@@ -6481,6 +6491,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

@@ -372,5 +372,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);
@@ -660,6 +669,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;
@@ -688,6 +742,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");
@@ -754,6 +812,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);
@@ -771,6 +830,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)        \

+ 31 - 0
drivers/cam_req_mgr/cam_req_mgr_util.c

@@ -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

@@ -92,6 +92,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)
 {
@@ -105,6 +136,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;
@@ -114,24 +153,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)
@@ -246,6 +267,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)

+ 39 - 18
drivers/cam_sensor_module/cam_ois/cam_ois_dev.c

@@ -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)