text: /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 and
   * only version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   */
  
  
  #include <linux/slab.h>
  #include <linux/mod_devicetable.h>
  #include <linux/of_device.h>
  #include "cam_vfe_dev.h"
  #include "cam_vfe_core.h"
  #include "cam_vfe_soc.h"
  #include "cam_debug_util.h"
  
  static struct cam_hw_intf *cam_vfe_hw_list[CAM_VFE_HW_NUM_MAX] = {0, 0, 0, 0};
  
  static char vfe_dev_name[8];
  
  int cam_vfe_probe(struct platform_device *pdev)
  {
  	struct cam_hw_info                *vfe_hw = NULL;
  	struct cam_hw_intf                *vfe_hw_intf = NULL;
  	const struct of_device_id         *match_dev = NULL;
  	struct cam_vfe_hw_core_info       *core_info = NULL;
  	struct cam_vfe_hw_info            *hw_info = NULL;
  	int                                rc = 0;
  
  	vfe_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
  	if (!vfe_hw_intf) {
  		rc = -ENOMEM;
  		goto end;
  	}
  
  	of_property_read_u32(pdev->dev.of_node,
  		"cell-index", &vfe_hw_intf->hw_idx);
  
  	vfe_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
  	if (!vfe_hw) {
  		rc = -ENOMEM;
  		goto free_vfe_hw_intf;
  	}
  
  	memset(vfe_dev_name, 0, sizeof(vfe_dev_name));
  	snprintf(vfe_dev_name, sizeof(vfe_dev_name),
  		"vfe%1u", vfe_hw_intf->hw_idx);
  
  	vfe_hw->soc_info.pdev = pdev;
  	vfe_hw->soc_info.dev = &pdev->dev;
  	vfe_hw->soc_info.dev_name = vfe_dev_name;
  	vfe_hw_intf->hw_priv = vfe_hw;
  	vfe_hw_intf->hw_ops.get_hw_caps = cam_vfe_get_hw_caps;
  	vfe_hw_intf->hw_ops.init = cam_vfe_init_hw;
  	vfe_hw_intf->hw_ops.deinit = cam_vfe_deinit_hw;
  	vfe_hw_intf->hw_ops.reset = cam_vfe_reset;
  	vfe_hw_intf->hw_ops.reserve = cam_vfe_reserve;
  	vfe_hw_intf->hw_ops.release = cam_vfe_release;
  	vfe_hw_intf->hw_ops.start = cam_vfe_start;
  	vfe_hw_intf->hw_ops.stop = cam_vfe_stop;
  	vfe_hw_intf->hw_ops.read = cam_vfe_read;
  	vfe_hw_intf->hw_ops.write = cam_vfe_write;
  	vfe_hw_intf->hw_ops.process_cmd = cam_vfe_process_cmd;
  	vfe_hw_intf->hw_type = CAM_ISP_HW_TYPE_VFE;
  
  	CAM_DBG(CAM_ISP, "type %d index %d",
  		vfe_hw_intf->hw_type, vfe_hw_intf->hw_idx);
  
  	platform_set_drvdata(pdev, vfe_hw_intf);
  
  	vfe_hw->core_info = kzalloc(sizeof(struct cam_vfe_hw_core_info),
  		GFP_KERNEL);
  	if (!vfe_hw->core_info) {
  		CAM_DBG(CAM_ISP, "Failed to alloc for core");
  		rc = -ENOMEM;
  		goto free_vfe_hw;
  	}
  	core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
  
  	match_dev = of_match_device(pdev->dev.driver->of_match_table,
  		&pdev->dev);
  	if (!match_dev) {
  		CAM_ERR(CAM_ISP, "Of_match Failed");
  		rc = -EINVAL;
  		goto free_core_info;
  	}
  	hw_info = (struct cam_vfe_hw_info *)match_dev->data;
  	core_info->vfe_hw_info = hw_info;
  
  	rc = cam_vfe_init_soc_resources(&vfe_hw->soc_info, cam_vfe_irq,
  		vfe_hw);
  	if (rc < 0) {
  		CAM_ERR(CAM_ISP, "Failed to init soc rc=%d", rc);
  		goto free_core_info;
  	}
  
  	rc = cam_vfe_core_init(core_info, &vfe_hw->soc_info,
  		vfe_hw_intf, hw_info);
  	if (rc < 0) {
  		CAM_ERR(CAM_ISP, "Failed to init core rc=%d", rc);
  		goto deinit_soc;
  	}
  
  	vfe_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
  	mutex_init(&vfe_hw->hw_mutex);
  	spin_lock_init(&vfe_hw->hw_lock);
  	init_completion(&vfe_hw->hw_complete);
  
  	if (vfe_hw_intf->hw_idx < CAM_VFE_HW_NUM_MAX)
  		cam_vfe_hw_list[vfe_hw_intf->hw_idx] = vfe_hw_intf;
  
  	cam_vfe_init_hw(vfe_hw, NULL, 0);
  	cam_vfe_deinit_hw(vfe_hw, NULL, 0);
  
  	CAM_DBG(CAM_ISP, "VFE%d probe successful", vfe_hw_intf->hw_idx);
  
  	return rc;
  
  deinit_soc:
  	if (cam_vfe_deinit_soc_resources(&vfe_hw->soc_info))
  		CAM_ERR(CAM_ISP, "Failed to deinit soc");
  free_core_info:
  	kfree(vfe_hw->core_info);
  free_vfe_hw:
  	kfree(vfe_hw);
  free_vfe_hw_intf:
  	kfree(vfe_hw_intf);
  end:
  	return rc;
  }
  
  int cam_vfe_remove(struct platform_device *pdev)
  {
  	struct cam_hw_info                *vfe_hw = NULL;
  	struct cam_hw_intf                *vfe_hw_intf = NULL;
  	struct cam_vfe_hw_core_info       *core_info = NULL;
  	int                                rc = 0;
  
  	vfe_hw_intf = platform_get_drvdata(pdev);
  	if (!vfe_hw_intf) {
  		CAM_ERR(CAM_ISP, "Error! No data in pdev");
  		return -EINVAL;
  	}
  
  	CAM_DBG(CAM_ISP, "type %d index %d",
  		vfe_hw_intf->hw_type, vfe_hw_intf->hw_idx);
  
  	if (vfe_hw_intf->hw_idx < CAM_VFE_HW_NUM_MAX)
  		cam_vfe_hw_list[vfe_hw_intf->hw_idx] = NULL;
  
  	vfe_hw = vfe_hw_intf->hw_priv;
  	if (!vfe_hw) {
  		CAM_ERR(CAM_ISP, "Error! HW data is NULL");
  		rc = -ENODEV;
  		goto free_vfe_hw_intf;
  	}
  
  	core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
  	if (!core_info) {
  		CAM_ERR(CAM_ISP, "Error! core data NULL");
  		rc = -EINVAL;
  		goto deinit_soc;
  	}
  
  	rc = cam_vfe_core_deinit(core_info, core_info->vfe_hw_info);
  	if (rc < 0)
  		CAM_ERR(CAM_ISP, "Failed to deinit core rc=%d", rc);
  
  	kfree(vfe_hw->core_info);
  
  deinit_soc:
  	rc = cam_vfe_deinit_soc_resources(&vfe_hw->soc_info);
  	if (rc < 0)
  		CAM_ERR(CAM_ISP, "Failed to deinit soc rc=%d", rc);
  
  	mutex_destroy(&vfe_hw->hw_mutex);
  	kfree(vfe_hw);
  
  	CAM_DBG(CAM_ISP, "VFE%d remove successful", vfe_hw_intf->hw_idx);
  
  free_vfe_hw_intf:
  	kfree(vfe_hw_intf);
  
  	return rc;
  }
  
  int cam_vfe_hw_init(struct cam_hw_intf **vfe_hw, uint32_t hw_idx)
  {
  	int rc = 0;
  
  	if (cam_vfe_hw_list[hw_idx]) {
  		*vfe_hw = cam_vfe_hw_list[hw_idx];
  		rc = 0;
  	} else {
  		*vfe_hw = NULL;
  		rc = -ENODEV;
  	}
  	return rc;
  }
  