Karthik Anantha Ram | d114a20 | 2018-06-19 12:51:45 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. |
Junzhe Zou | cc79a57 | 2017-08-22 16:18:57 -0700 | [diff] [blame] | 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | */ |
| 12 | |
| 13 | #include <linux/platform_device.h> |
| 14 | #include <linux/of.h> |
| 15 | #include <linux/of_device.h> |
| 16 | #include <linux/slab.h> |
| 17 | #include <linux/module.h> |
| 18 | #include <linux/kernel.h> |
| 19 | #include <media/cam_req_mgr.h> |
| 20 | |
| 21 | #include "cam_subdev.h" |
| 22 | #include "cam_lrme_hw_intf.h" |
| 23 | #include "cam_lrme_hw_core.h" |
| 24 | #include "cam_lrme_hw_soc.h" |
| 25 | #include "cam_lrme_hw_reg.h" |
| 26 | #include "cam_req_mgr_workq.h" |
| 27 | #include "cam_lrme_hw_mgr.h" |
| 28 | #include "cam_mem_mgr_api.h" |
| 29 | #include "cam_smmu_api.h" |
| 30 | |
Junzhe Zou | cc79a57 | 2017-08-22 16:18:57 -0700 | [diff] [blame] | 31 | static int cam_lrme_hw_dev_util_cdm_acquire(struct cam_lrme_core *lrme_core, |
| 32 | struct cam_hw_info *lrme_hw) |
| 33 | { |
| 34 | int rc, i; |
| 35 | struct cam_cdm_bl_request *cdm_cmd; |
| 36 | struct cam_cdm_acquire_data cdm_acquire; |
| 37 | struct cam_lrme_cdm_info *hw_cdm_info; |
| 38 | |
| 39 | hw_cdm_info = kzalloc(sizeof(struct cam_lrme_cdm_info), |
| 40 | GFP_KERNEL); |
| 41 | if (!hw_cdm_info) { |
| 42 | CAM_ERR(CAM_LRME, "No memory for hw_cdm_info"); |
| 43 | return -ENOMEM; |
| 44 | } |
| 45 | |
| 46 | cdm_cmd = kzalloc((sizeof(struct cam_cdm_bl_request) + |
| 47 | ((CAM_LRME_MAX_HW_ENTRIES - 1) * |
| 48 | sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL); |
| 49 | if (!cdm_cmd) { |
| 50 | CAM_ERR(CAM_LRME, "No memory for cdm_cmd"); |
| 51 | kfree(hw_cdm_info); |
| 52 | return -ENOMEM; |
| 53 | } |
| 54 | |
| 55 | memset(&cdm_acquire, 0, sizeof(cdm_acquire)); |
| 56 | strlcpy(cdm_acquire.identifier, "lrmecdm", sizeof("lrmecdm")); |
| 57 | cdm_acquire.cell_index = lrme_hw->soc_info.index; |
| 58 | cdm_acquire.handle = 0; |
| 59 | cdm_acquire.userdata = hw_cdm_info; |
| 60 | cdm_acquire.cam_cdm_callback = NULL; |
| 61 | cdm_acquire.id = CAM_CDM_VIRTUAL; |
| 62 | cdm_acquire.base_array_cnt = lrme_hw->soc_info.num_reg_map; |
| 63 | for (i = 0; i < lrme_hw->soc_info.num_reg_map; i++) |
| 64 | cdm_acquire.base_array[i] = &lrme_hw->soc_info.reg_map[i]; |
| 65 | |
| 66 | rc = cam_cdm_acquire(&cdm_acquire); |
| 67 | if (rc) { |
| 68 | CAM_ERR(CAM_LRME, "Can't acquire cdm"); |
| 69 | goto error; |
| 70 | } |
| 71 | |
| 72 | hw_cdm_info->cdm_cmd = cdm_cmd; |
| 73 | hw_cdm_info->cdm_ops = cdm_acquire.ops; |
| 74 | hw_cdm_info->cdm_handle = cdm_acquire.handle; |
| 75 | |
| 76 | lrme_core->hw_cdm_info = hw_cdm_info; |
| 77 | CAM_DBG(CAM_LRME, "cdm acquire done"); |
| 78 | |
| 79 | return 0; |
| 80 | error: |
| 81 | kfree(cdm_cmd); |
| 82 | kfree(hw_cdm_info); |
| 83 | return rc; |
| 84 | } |
| 85 | |
| 86 | static int cam_lrme_hw_dev_probe(struct platform_device *pdev) |
| 87 | { |
| 88 | struct cam_hw_info *lrme_hw; |
| 89 | struct cam_hw_intf lrme_hw_intf; |
| 90 | struct cam_lrme_core *lrme_core; |
| 91 | const struct of_device_id *match_dev = NULL; |
| 92 | struct cam_lrme_hw_info *hw_info; |
| 93 | int rc, i; |
| 94 | |
| 95 | lrme_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL); |
| 96 | if (!lrme_hw) { |
| 97 | CAM_ERR(CAM_LRME, "No memory to create lrme_hw"); |
| 98 | return -ENOMEM; |
| 99 | } |
| 100 | |
| 101 | lrme_core = kzalloc(sizeof(struct cam_lrme_core), GFP_KERNEL); |
| 102 | if (!lrme_core) { |
| 103 | CAM_ERR(CAM_LRME, "No memory to create lrme_core"); |
| 104 | kfree(lrme_hw); |
| 105 | return -ENOMEM; |
| 106 | } |
| 107 | |
| 108 | lrme_hw->core_info = lrme_core; |
| 109 | lrme_hw->hw_state = CAM_HW_STATE_POWER_DOWN; |
| 110 | lrme_hw->soc_info.pdev = pdev; |
| 111 | lrme_hw->soc_info.dev = &pdev->dev; |
| 112 | lrme_hw->soc_info.dev_name = pdev->name; |
| 113 | lrme_hw->open_count = 0; |
| 114 | lrme_core->state = CAM_LRME_CORE_STATE_INIT; |
| 115 | |
| 116 | mutex_init(&lrme_hw->hw_mutex); |
| 117 | spin_lock_init(&lrme_hw->hw_lock); |
| 118 | init_completion(&lrme_hw->hw_complete); |
| 119 | init_completion(&lrme_core->reset_complete); |
| 120 | |
| 121 | rc = cam_req_mgr_workq_create("cam_lrme_hw_worker", |
| 122 | CAM_LRME_HW_WORKQ_NUM_TASK, |
Karthik Anantha Ram | d114a20 | 2018-06-19 12:51:45 -0700 | [diff] [blame] | 123 | &lrme_core->work, CRM_WORKQ_USAGE_IRQ, 0); |
Junzhe Zou | cc79a57 | 2017-08-22 16:18:57 -0700 | [diff] [blame] | 124 | if (rc) { |
| 125 | CAM_ERR(CAM_LRME, "Unable to create a workq, rc=%d", rc); |
| 126 | goto free_memory; |
| 127 | } |
| 128 | |
| 129 | for (i = 0; i < CAM_LRME_HW_WORKQ_NUM_TASK; i++) |
| 130 | lrme_core->work->task.pool[i].payload = |
| 131 | &lrme_core->work_data[i]; |
| 132 | |
| 133 | match_dev = of_match_device(pdev->dev.driver->of_match_table, |
| 134 | &pdev->dev); |
| 135 | if (!match_dev || !match_dev->data) { |
| 136 | CAM_ERR(CAM_LRME, "No Of_match data, %pK", match_dev); |
| 137 | rc = -EINVAL; |
| 138 | goto destroy_workqueue; |
| 139 | } |
| 140 | hw_info = (struct cam_lrme_hw_info *)match_dev->data; |
| 141 | lrme_core->hw_info = hw_info; |
| 142 | |
| 143 | rc = cam_lrme_soc_init_resources(&lrme_hw->soc_info, |
| 144 | cam_lrme_hw_irq, lrme_hw); |
| 145 | if (rc) { |
| 146 | CAM_ERR(CAM_LRME, "Failed to init soc, rc=%d", rc); |
| 147 | goto destroy_workqueue; |
| 148 | } |
| 149 | |
| 150 | rc = cam_lrme_hw_dev_util_cdm_acquire(lrme_core, lrme_hw); |
| 151 | if (rc) { |
| 152 | CAM_ERR(CAM_LRME, "Failed to acquire cdm"); |
| 153 | goto deinit_platform_res; |
| 154 | } |
| 155 | |
| 156 | rc = cam_smmu_get_handle("lrme", &lrme_core->device_iommu.non_secure); |
| 157 | if (rc) { |
| 158 | CAM_ERR(CAM_LRME, "Get iommu handle failed"); |
| 159 | goto release_cdm; |
| 160 | } |
| 161 | |
| 162 | rc = cam_smmu_ops(lrme_core->device_iommu.non_secure, CAM_SMMU_ATTACH); |
| 163 | if (rc) { |
| 164 | CAM_ERR(CAM_LRME, "LRME attach iommu handle failed, rc=%d", rc); |
| 165 | goto destroy_smmu; |
| 166 | } |
| 167 | |
| 168 | rc = cam_lrme_hw_start(lrme_hw, NULL, 0); |
| 169 | if (rc) { |
| 170 | CAM_ERR(CAM_LRME, "Failed to hw init, rc=%d", rc); |
| 171 | goto detach_smmu; |
| 172 | } |
| 173 | |
| 174 | rc = cam_lrme_hw_util_get_caps(lrme_hw, &lrme_core->hw_caps); |
| 175 | if (rc) { |
| 176 | CAM_ERR(CAM_LRME, "Failed to get hw caps, rc=%d", rc); |
| 177 | if (cam_lrme_hw_stop(lrme_hw, NULL, 0)) |
| 178 | CAM_ERR(CAM_LRME, "Failed in hw deinit"); |
| 179 | goto detach_smmu; |
| 180 | } |
| 181 | |
| 182 | rc = cam_lrme_hw_stop(lrme_hw, NULL, 0); |
| 183 | if (rc) { |
| 184 | CAM_ERR(CAM_LRME, "Failed to deinit hw, rc=%d", rc); |
| 185 | goto detach_smmu; |
| 186 | } |
| 187 | |
| 188 | lrme_core->hw_idx = lrme_hw->soc_info.index; |
| 189 | lrme_hw_intf.hw_priv = lrme_hw; |
| 190 | lrme_hw_intf.hw_idx = lrme_hw->soc_info.index; |
| 191 | lrme_hw_intf.hw_ops.get_hw_caps = cam_lrme_hw_get_caps; |
| 192 | lrme_hw_intf.hw_ops.init = NULL; |
| 193 | lrme_hw_intf.hw_ops.deinit = NULL; |
| 194 | lrme_hw_intf.hw_ops.reset = cam_lrme_hw_reset; |
| 195 | lrme_hw_intf.hw_ops.reserve = NULL; |
| 196 | lrme_hw_intf.hw_ops.release = NULL; |
| 197 | lrme_hw_intf.hw_ops.start = cam_lrme_hw_start; |
| 198 | lrme_hw_intf.hw_ops.stop = cam_lrme_hw_stop; |
| 199 | lrme_hw_intf.hw_ops.read = NULL; |
| 200 | lrme_hw_intf.hw_ops.write = NULL; |
| 201 | lrme_hw_intf.hw_ops.process_cmd = cam_lrme_hw_process_cmd; |
Junzhe Zou | 4b11e7a | 2017-11-13 17:21:32 -0800 | [diff] [blame] | 202 | lrme_hw_intf.hw_ops.flush = cam_lrme_hw_flush; |
Junzhe Zou | cc79a57 | 2017-08-22 16:18:57 -0700 | [diff] [blame] | 203 | lrme_hw_intf.hw_type = CAM_HW_LRME; |
| 204 | |
| 205 | rc = cam_cdm_get_iommu_handle("lrmecdm", &lrme_core->cdm_iommu); |
| 206 | if (rc) { |
| 207 | CAM_ERR(CAM_LRME, "Failed to acquire the CDM iommu handles"); |
| 208 | goto detach_smmu; |
| 209 | } |
| 210 | |
| 211 | rc = cam_lrme_mgr_register_device(&lrme_hw_intf, |
| 212 | &lrme_core->device_iommu, |
| 213 | &lrme_core->cdm_iommu); |
| 214 | if (rc) { |
| 215 | CAM_ERR(CAM_LRME, "Failed to register device"); |
| 216 | goto detach_smmu; |
| 217 | } |
| 218 | |
| 219 | platform_set_drvdata(pdev, lrme_hw); |
| 220 | CAM_DBG(CAM_LRME, "LRME-%d probe successful", lrme_hw_intf.hw_idx); |
| 221 | |
| 222 | return rc; |
| 223 | |
| 224 | detach_smmu: |
| 225 | cam_smmu_ops(lrme_core->device_iommu.non_secure, CAM_SMMU_DETACH); |
| 226 | destroy_smmu: |
| 227 | cam_smmu_destroy_handle(lrme_core->device_iommu.non_secure); |
| 228 | release_cdm: |
| 229 | cam_cdm_release(lrme_core->hw_cdm_info->cdm_handle); |
| 230 | kfree(lrme_core->hw_cdm_info->cdm_cmd); |
| 231 | kfree(lrme_core->hw_cdm_info); |
| 232 | deinit_platform_res: |
| 233 | if (cam_lrme_soc_deinit_resources(&lrme_hw->soc_info)) |
| 234 | CAM_ERR(CAM_LRME, "Failed in soc deinit"); |
| 235 | mutex_destroy(&lrme_hw->hw_mutex); |
| 236 | destroy_workqueue: |
| 237 | cam_req_mgr_workq_destroy(&lrme_core->work); |
| 238 | free_memory: |
| 239 | mutex_destroy(&lrme_hw->hw_mutex); |
| 240 | kfree(lrme_hw); |
| 241 | kfree(lrme_core); |
| 242 | |
| 243 | return rc; |
| 244 | } |
| 245 | |
| 246 | static int cam_lrme_hw_dev_remove(struct platform_device *pdev) |
| 247 | { |
| 248 | int rc = 0; |
| 249 | struct cam_hw_info *lrme_hw; |
| 250 | struct cam_lrme_core *lrme_core; |
| 251 | |
| 252 | lrme_hw = platform_get_drvdata(pdev); |
| 253 | if (!lrme_hw) { |
| 254 | CAM_ERR(CAM_LRME, "Invalid lrme_hw from fd_hw_intf"); |
| 255 | rc = -ENODEV; |
| 256 | goto deinit_platform_res; |
| 257 | } |
| 258 | |
| 259 | lrme_core = (struct cam_lrme_core *)lrme_hw->core_info; |
| 260 | if (!lrme_core) { |
| 261 | CAM_ERR(CAM_LRME, "Invalid lrme_core from fd_hw"); |
| 262 | rc = -EINVAL; |
| 263 | goto deinit_platform_res; |
| 264 | } |
| 265 | |
| 266 | cam_smmu_ops(lrme_core->device_iommu.non_secure, CAM_SMMU_DETACH); |
| 267 | cam_smmu_destroy_handle(lrme_core->device_iommu.non_secure); |
| 268 | cam_cdm_release(lrme_core->hw_cdm_info->cdm_handle); |
| 269 | cam_lrme_mgr_deregister_device(lrme_core->hw_idx); |
| 270 | |
| 271 | kfree(lrme_core->hw_cdm_info->cdm_cmd); |
| 272 | kfree(lrme_core->hw_cdm_info); |
| 273 | kfree(lrme_core); |
| 274 | |
| 275 | deinit_platform_res: |
| 276 | rc = cam_lrme_soc_deinit_resources(&lrme_hw->soc_info); |
| 277 | if (rc) |
| 278 | CAM_ERR(CAM_LRME, "Error in LRME soc deinit, rc=%d", rc); |
| 279 | |
| 280 | mutex_destroy(&lrme_hw->hw_mutex); |
| 281 | kfree(lrme_hw); |
| 282 | |
| 283 | return rc; |
| 284 | } |
| 285 | |
| 286 | static const struct of_device_id cam_lrme_hw_dt_match[] = { |
| 287 | { |
| 288 | .compatible = "qcom,lrme", |
| 289 | .data = &cam_lrme10_hw_info, |
| 290 | }, |
| 291 | {} |
| 292 | }; |
| 293 | |
| 294 | MODULE_DEVICE_TABLE(of, cam_lrme_hw_dt_match); |
| 295 | |
| 296 | static struct platform_driver cam_lrme_hw_driver = { |
| 297 | .probe = cam_lrme_hw_dev_probe, |
| 298 | .remove = cam_lrme_hw_dev_remove, |
| 299 | .driver = { |
| 300 | .name = "cam_lrme_hw", |
| 301 | .owner = THIS_MODULE, |
| 302 | .of_match_table = cam_lrme_hw_dt_match, |
| 303 | }, |
| 304 | }; |
| 305 | |
| 306 | static int __init cam_lrme_hw_init_module(void) |
| 307 | { |
| 308 | return platform_driver_register(&cam_lrme_hw_driver); |
| 309 | } |
| 310 | |
| 311 | static void __exit cam_lrme_hw_exit_module(void) |
| 312 | { |
| 313 | platform_driver_unregister(&cam_lrme_hw_driver); |
| 314 | } |
| 315 | |
| 316 | module_init(cam_lrme_hw_init_module); |
| 317 | module_exit(cam_lrme_hw_exit_module); |
| 318 | MODULE_DESCRIPTION("CAM LRME HW driver"); |
| 319 | MODULE_LICENSE("GPL v2"); |