blob: 00ead5d8d20b12ca3c014281071919ba7c0b51c9 [file] [log] [blame]
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
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/device.h>
14#include <linux/platform_device.h>
15#include <linux/of.h>
16#include <linux/msm-bus.h>
17#include <linux/pm_opp.h>
18#include <linux/slab.h>
19
20#include "cam_cpas_hw.h"
21#include "cam_cpas_hw_intf.h"
22#include "cam_cpas_soc.h"
23
Pavan Kumar Chilamkurthi03184972017-11-03 02:16:02 -070024#define CAM_CPAS_AXI_MIN_BW (2048 * 1024)
25
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -070026int cam_cpas_util_reg_update(struct cam_hw_info *cpas_hw,
27 enum cam_cpas_reg_base reg_base, struct cam_cpas_reg *reg_info)
28{
29 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
30 struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
31 uint32_t value;
32 int reg_base_index;
33
34 if (reg_info->enable == false)
35 return 0;
36
37 reg_base_index = cpas_core->regbase_index[reg_base];
38 if (reg_base_index == -1)
39 return -EINVAL;
40
41 if (reg_info->masked_value) {
42 value = cam_io_r_mb(
43 soc_info->reg_map[reg_base_index].mem_base +
44 reg_info->offset);
45 value = value & (~reg_info->mask);
46 value = value | (reg_info->value << reg_info->shift);
47 } else {
48 value = reg_info->value;
49 }
50
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -070051 CAM_DBG(CAM_CPAS, "Base[%d] Offset[0x%8x] Value[0x%8x]",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -070052 reg_base, reg_info->offset, value);
53
54 cam_io_w_mb(value, soc_info->reg_map[reg_base_index].mem_base +
55 reg_info->offset);
56
57 return 0;
58}
59
60static int cam_cpas_util_vote_bus_client_level(
61 struct cam_cpas_bus_client *bus_client, unsigned int level)
62{
63 if (!bus_client->valid || (bus_client->dyn_vote == true)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -070064 CAM_ERR(CAM_CPAS, "Invalid params %d %d", bus_client->valid,
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -070065 bus_client->dyn_vote);
66 return -EINVAL;
67 }
68
69 if (level >= bus_client->num_usecases) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -070070 CAM_ERR(CAM_CPAS, "Invalid vote level=%d, usecases=%d", level,
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -070071 bus_client->num_usecases);
72 return -EINVAL;
73 }
74
75 if (level == bus_client->curr_vote_level)
76 return 0;
77
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -070078 CAM_DBG(CAM_CPAS, "Bus client[%d] index[%d]", bus_client->client_id,
79 level);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -070080 msm_bus_scale_client_update_request(bus_client->client_id, level);
81 bus_client->curr_vote_level = level;
82
83 return 0;
84}
85
86static int cam_cpas_util_vote_bus_client_bw(
87 struct cam_cpas_bus_client *bus_client, uint64_t ab, uint64_t ib)
88{
89 struct msm_bus_paths *path;
90 struct msm_bus_scale_pdata *pdata;
91 int idx = 0;
92
93 if (!bus_client->valid) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -070094 CAM_ERR(CAM_CPAS, "bus client not valid");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -070095 return -EINVAL;
96 }
97
98 if ((bus_client->num_usecases != 2) ||
99 (bus_client->num_paths != 1) ||
100 (bus_client->dyn_vote != true)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700101 CAM_ERR(CAM_CPAS, "dynamic update not allowed %d %d %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700102 bus_client->num_usecases, bus_client->num_paths,
103 bus_client->dyn_vote);
104 return -EINVAL;
105 }
106
107 mutex_lock(&bus_client->lock);
108
109 if (bus_client->curr_vote_level > 1) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700110 CAM_ERR(CAM_CPAS, "curr_vote_level %d cannot be greater than 1",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700111 bus_client->curr_vote_level);
112 mutex_unlock(&bus_client->lock);
113 return -EINVAL;
114 }
115
116 idx = bus_client->curr_vote_level;
117 idx = 1 - idx;
118 bus_client->curr_vote_level = idx;
119 mutex_unlock(&bus_client->lock);
120
Pavan Kumar Chilamkurthi03184972017-11-03 02:16:02 -0700121 if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_BW))
122 ab = CAM_CPAS_AXI_MIN_BW;
123
124 if ((ib > 0) && (ib < CAM_CPAS_AXI_MIN_BW))
125 ib = CAM_CPAS_AXI_MIN_BW;
126
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700127 pdata = bus_client->pdata;
128 path = &(pdata->usecase[idx]);
129 path->vectors[0].ab = ab;
130 path->vectors[0].ib = ib;
131
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700132 CAM_DBG(CAM_CPAS, "Bus client[%d] :ab[%llu] ib[%llu], index[%d]",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700133 bus_client->client_id, ab, ib, idx);
134 msm_bus_scale_client_update_request(bus_client->client_id, idx);
135
136 return 0;
137}
138
139static int cam_cpas_util_register_bus_client(
140 struct cam_hw_soc_info *soc_info, struct device_node *dev_node,
141 struct cam_cpas_bus_client *bus_client)
142{
143 struct msm_bus_scale_pdata *pdata = NULL;
144 uint32_t client_id;
145 int rc;
146
147 pdata = msm_bus_pdata_from_node(soc_info->pdev,
148 dev_node);
149 if (!pdata) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700150 CAM_ERR(CAM_CPAS, "failed get_pdata");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700151 return -EINVAL;
152 }
153
154 if ((pdata->num_usecases == 0) ||
155 (pdata->usecase[0].num_paths == 0)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700156 CAM_ERR(CAM_CPAS, "usecase=%d", pdata->num_usecases);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700157 rc = -EINVAL;
158 goto error;
159 }
160
161 client_id = msm_bus_scale_register_client(pdata);
162 if (!client_id) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700163 CAM_ERR(CAM_CPAS, "failed in register ahb bus client");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700164 rc = -EINVAL;
165 goto error;
166 }
167
168 bus_client->dyn_vote = of_property_read_bool(dev_node,
169 "qcom,msm-bus-vector-dyn-vote");
170
171 if (bus_client->dyn_vote && (pdata->num_usecases != 2)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700172 CAM_ERR(CAM_CPAS, "Excess or less vectors %d",
173 pdata->num_usecases);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700174 rc = -EINVAL;
175 goto fail_unregister_client;
176 }
177
178 msm_bus_scale_client_update_request(client_id, 0);
179
180 bus_client->src = pdata->usecase[0].vectors[0].src;
181 bus_client->dst = pdata->usecase[0].vectors[0].dst;
182 bus_client->pdata = pdata;
183 bus_client->client_id = client_id;
184 bus_client->num_usecases = pdata->num_usecases;
185 bus_client->num_paths = pdata->usecase[0].num_paths;
186 bus_client->curr_vote_level = 0;
187 bus_client->valid = true;
188 mutex_init(&bus_client->lock);
189
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700190 CAM_DBG(CAM_CPAS, "Bus Client : src=%d, dst=%d, bus_client=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700191 bus_client->src, bus_client->dst, bus_client->client_id);
192
193 return 0;
194fail_unregister_client:
195 msm_bus_scale_unregister_client(bus_client->client_id);
196error:
197 return rc;
198
199}
200
201static int cam_cpas_util_unregister_bus_client(
202 struct cam_cpas_bus_client *bus_client)
203{
204 if (!bus_client->valid)
205 return -EINVAL;
206
207 if (bus_client->dyn_vote)
208 cam_cpas_util_vote_bus_client_bw(bus_client, 0, 0);
209 else
210 cam_cpas_util_vote_bus_client_level(bus_client, 0);
211
212 msm_bus_scale_unregister_client(bus_client->client_id);
213 bus_client->valid = false;
214
215 mutex_destroy(&bus_client->lock);
216
217 return 0;
218}
219
220static int cam_cpas_util_axi_cleanup(struct cam_cpas *cpas_core,
221 struct cam_hw_soc_info *soc_info)
222{
223 struct cam_cpas_private_soc *soc_private =
224 (struct cam_cpas_private_soc *)soc_info->soc_private;
225 struct cam_cpas_axi_port *curr_port;
226 struct cam_cpas_axi_port *temp_port;
227
228 list_for_each_entry_safe(curr_port, temp_port,
229 &cpas_core->axi_ports_list_head, sibling_port) {
230 cam_cpas_util_unregister_bus_client(&curr_port->mnoc_bus);
231 of_node_put(curr_port->axi_port_mnoc_node);
232 if (soc_private->axi_camnoc_based) {
233 cam_cpas_util_unregister_bus_client(
234 &curr_port->camnoc_bus);
235 of_node_put(curr_port->axi_port_camnoc_node);
236 }
237 of_node_put(curr_port->axi_port_node);
238 list_del(&curr_port->sibling_port);
239 mutex_destroy(&curr_port->lock);
240 kfree(curr_port);
241 }
242
243 of_node_put(soc_private->axi_port_list_node);
244
245 return 0;
246}
247
248static int cam_cpas_util_axi_setup(struct cam_cpas *cpas_core,
249 struct cam_hw_soc_info *soc_info)
250{
251 struct cam_cpas_private_soc *soc_private =
252 (struct cam_cpas_private_soc *)soc_info->soc_private;
253 struct cam_cpas_axi_port *axi_port;
254 int rc;
255 struct device_node *axi_port_list_node;
256 struct device_node *axi_port_node = NULL;
257 struct device_node *axi_port_mnoc_node = NULL;
258 struct device_node *axi_port_camnoc_node = NULL;
259
260 INIT_LIST_HEAD(&cpas_core->axi_ports_list_head);
261
262 axi_port_list_node = of_find_node_by_name(soc_info->pdev->dev.of_node,
263 "qcom,axi-port-list");
264 if (!axi_port_list_node) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700265 CAM_ERR(CAM_CPAS, "Node qcom,axi-port-list not found.");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700266 return -EINVAL;
267 }
268
269 soc_private->axi_port_list_node = axi_port_list_node;
270
271 for_each_available_child_of_node(axi_port_list_node, axi_port_node) {
272 axi_port = kzalloc(sizeof(*axi_port), GFP_KERNEL);
273 if (!axi_port) {
274 rc = -ENOMEM;
275 goto error_previous_axi_cleanup;
276 }
277 axi_port->axi_port_node = axi_port_node;
278
279 rc = of_property_read_string_index(axi_port_node,
280 "qcom,axi-port-name", 0,
281 (const char **)&axi_port->axi_port_name);
282 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700283 CAM_ERR(CAM_CPAS,
284 "failed to read qcom,axi-port-name rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700285 goto port_name_fail;
286 }
287
288 axi_port_mnoc_node = of_find_node_by_name(axi_port_node,
289 "qcom,axi-port-mnoc");
290 if (!axi_port_mnoc_node) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700291 CAM_ERR(CAM_CPAS, "Node qcom,axi-port-mnoc not found.");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700292 rc = -EINVAL;
293 goto mnoc_node_get_fail;
294 }
295 axi_port->axi_port_mnoc_node = axi_port_mnoc_node;
296
297 rc = cam_cpas_util_register_bus_client(soc_info,
298 axi_port_mnoc_node, &axi_port->mnoc_bus);
299 if (rc)
300 goto mnoc_register_fail;
301
302 if (soc_private->axi_camnoc_based) {
303 axi_port_camnoc_node = of_find_node_by_name(
304 axi_port_node, "qcom,axi-port-camnoc");
305 if (!axi_port_camnoc_node) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700306 CAM_ERR(CAM_CPAS,
307 "Node qcom,axi-port-camnoc not found");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700308 rc = -EINVAL;
309 goto camnoc_node_get_fail;
310 }
311 axi_port->axi_port_camnoc_node = axi_port_camnoc_node;
312
313 rc = cam_cpas_util_register_bus_client(soc_info,
314 axi_port_camnoc_node, &axi_port->camnoc_bus);
315 if (rc)
316 goto camnoc_register_fail;
317 }
318
319 mutex_init(&axi_port->lock);
320
321 INIT_LIST_HEAD(&axi_port->sibling_port);
322 list_add_tail(&axi_port->sibling_port,
323 &cpas_core->axi_ports_list_head);
324 INIT_LIST_HEAD(&axi_port->clients_list_head);
325 }
326
327 return 0;
328camnoc_register_fail:
329 of_node_put(axi_port->axi_port_camnoc_node);
330camnoc_node_get_fail:
331 cam_cpas_util_unregister_bus_client(&axi_port->mnoc_bus);
332mnoc_register_fail:
333 of_node_put(axi_port->axi_port_mnoc_node);
334mnoc_node_get_fail:
335port_name_fail:
336 of_node_put(axi_port->axi_port_node);
337 kfree(axi_port);
338error_previous_axi_cleanup:
339 cam_cpas_util_axi_cleanup(cpas_core, soc_info);
340 return rc;
341}
342
343static int cam_cpas_util_vote_default_ahb_axi(struct cam_hw_info *cpas_hw,
344 int enable)
345{
346 int rc;
347 struct cam_cpas *cpas_core = (struct cam_cpas *)cpas_hw->core_info;
348 struct cam_cpas_axi_port *curr_port;
349 struct cam_cpas_axi_port *temp_port;
350 uint64_t camnoc_bw, mnoc_bw;
351 struct cam_cpas_private_soc *soc_private =
352 (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
353
354 rc = cam_cpas_util_vote_bus_client_level(&cpas_core->ahb_bus_client,
355 (enable == true) ? CAM_SVS_VOTE : CAM_SUSPEND_VOTE);
356 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700357 CAM_ERR(CAM_CPAS, "Failed in AHB vote, enable=%d, rc=%d",
358 enable, rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700359 return rc;
360 }
361
362 if (enable) {
363 mnoc_bw = CAM_CPAS_DEFAULT_AXI_BW;
364 camnoc_bw = CAM_CPAS_DEFAULT_AXI_BW;
365 } else {
366 mnoc_bw = 0;
367 camnoc_bw = 0;
368 }
369
370 list_for_each_entry_safe(curr_port, temp_port,
371 &cpas_core->axi_ports_list_head, sibling_port) {
372 rc = cam_cpas_util_vote_bus_client_bw(&curr_port->mnoc_bus,
Pavan Kumar Chilamkurthi03184972017-11-03 02:16:02 -0700373 mnoc_bw, mnoc_bw);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700374 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700375 CAM_ERR(CAM_CPAS,
376 "Failed in mnoc vote, enable=%d, rc=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700377 enable, rc);
378 goto remove_ahb_vote;
379 }
380
381 if (soc_private->axi_camnoc_based) {
382 cam_cpas_util_vote_bus_client_bw(
Pavan Kumar Chilamkurthi03184972017-11-03 02:16:02 -0700383 &curr_port->camnoc_bus, 0, camnoc_bw);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700384 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700385 CAM_ERR(CAM_CPAS,
386 "Failed in mnoc vote, enable=%d, %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700387 enable, rc);
388 cam_cpas_util_vote_bus_client_bw(
389 &curr_port->mnoc_bus, 0, 0);
390 goto remove_ahb_vote;
391 }
392 }
393 }
394
395 return 0;
396remove_ahb_vote:
397 cam_cpas_util_vote_bus_client_level(&cpas_core->ahb_bus_client,
398 CAM_SUSPEND_VOTE);
399 return rc;
400}
401
402static int cam_cpas_util_insert_client_to_axi_port(struct cam_cpas *cpas_core,
403 struct cam_cpas_private_soc *soc_private,
404 struct cam_cpas_client *cpas_client, int32_t client_indx)
405{
406 struct cam_cpas_axi_port *curr_port;
407 struct cam_cpas_axi_port *temp_port;
408
409 list_for_each_entry_safe(curr_port, temp_port,
410 &cpas_core->axi_ports_list_head, sibling_port) {
411 if (strnstr(curr_port->axi_port_name,
412 soc_private->client_axi_port_name[client_indx],
413 strlen(curr_port->axi_port_name))) {
414
415 cpas_client->axi_port = curr_port;
416 INIT_LIST_HEAD(&cpas_client->axi_sibling_client);
417
418 mutex_lock(&curr_port->lock);
419 list_add_tail(&cpas_client->axi_sibling_client,
420 &cpas_client->axi_port->clients_list_head);
421 mutex_unlock(&curr_port->lock);
422 break;
423 }
424 }
425
426 return 0;
427}
428
429static void cam_cpas_util_remove_client_from_axi_port(
430 struct cam_cpas_client *cpas_client)
431{
432 mutex_lock(&cpas_client->axi_port->lock);
433 list_del(&cpas_client->axi_sibling_client);
434 mutex_unlock(&cpas_client->axi_port->lock);
435}
436
437static int cam_cpas_hw_reg_write(struct cam_hw_info *cpas_hw,
438 uint32_t client_handle, enum cam_cpas_reg_base reg_base,
439 uint32_t offset, bool mb, uint32_t value)
440{
441 struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
442 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
443 int reg_base_index = cpas_core->regbase_index[reg_base];
444 uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
445 int rc = 0;
446
447 if (reg_base_index < 0 || reg_base_index >= soc_info->num_reg_map) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700448 CAM_ERR(CAM_CPAS,
449 "Invalid reg_base=%d, reg_base_index=%d, num_map=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700450 reg_base, reg_base_index, soc_info->num_reg_map);
451 return -EINVAL;
452 }
453
454 if (!CAM_CPAS_CLIENT_VALID(client_indx))
455 return -EINVAL;
456
457 mutex_lock(&cpas_core->client_mutex[client_indx]);
458
459 if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700460 CAM_ERR(CAM_CPAS, "client has not started%d", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700461 rc = -EPERM;
462 goto unlock_client;
463 }
464
465 if (mb)
466 cam_io_w_mb(value,
467 soc_info->reg_map[reg_base_index].mem_base + offset);
468 else
469 cam_io_w(value,
470 soc_info->reg_map[reg_base_index].mem_base + offset);
471
472unlock_client:
473 mutex_unlock(&cpas_core->client_mutex[client_indx]);
474 return rc;
475}
476
477static int cam_cpas_hw_reg_read(struct cam_hw_info *cpas_hw,
478 uint32_t client_handle, enum cam_cpas_reg_base reg_base,
479 uint32_t offset, bool mb, uint32_t *value)
480{
481 struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
482 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
483 int reg_base_index = cpas_core->regbase_index[reg_base];
484 uint32_t reg_value;
485 uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
486 int rc = 0;
487
488 if (!value)
489 return -EINVAL;
490
491 if (reg_base_index < 0 || reg_base_index >= soc_info->num_reg_map) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700492 CAM_ERR(CAM_CPAS,
493 "Invalid reg_base=%d, reg_base_index=%d, num_map=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700494 reg_base, reg_base_index, soc_info->num_reg_map);
495 return -EINVAL;
496 }
497
498 if (!CAM_CPAS_CLIENT_VALID(client_indx))
499 return -EINVAL;
500
501 mutex_lock(&cpas_core->client_mutex[client_indx]);
502
503 if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700504 CAM_ERR(CAM_CPAS, "client has not started%d", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700505 rc = -EPERM;
506 goto unlock_client;
507 }
508
509 if (mb)
510 reg_value = cam_io_r_mb(
511 soc_info->reg_map[reg_base_index].mem_base + offset);
512 else
513 reg_value = cam_io_r(
514 soc_info->reg_map[reg_base_index].mem_base + offset);
515
516 *value = reg_value;
517
518unlock_client:
519 mutex_unlock(&cpas_core->client_mutex[client_indx]);
520 return rc;
521}
522
523static int cam_cpas_util_apply_client_axi_vote(
524 struct cam_cpas *cpas_core, struct cam_cpas_private_soc *soc_private,
525 struct cam_cpas_client *cpas_client, struct cam_axi_vote *axi_vote)
526{
527 struct cam_cpas_client *curr_client;
528 struct cam_cpas_client *temp_client;
529 struct cam_axi_vote req_axi_vote = *axi_vote;
530 struct cam_cpas_axi_port *axi_port = cpas_client->axi_port;
531 uint64_t camnoc_bw = 0, mnoc_bw = 0;
532 int rc = 0;
533
534 if (!axi_port) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700535 CAM_ERR(CAM_CPAS, "axi port does not exists");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700536 return -EINVAL;
537 }
538
539 /*
540 * Make sure we use same bw for both compressed, uncompressed
541 * in case client has requested either of one only
542 */
543 if (req_axi_vote.compressed_bw == 0)
544 req_axi_vote.compressed_bw = req_axi_vote.uncompressed_bw;
545
546 if (req_axi_vote.uncompressed_bw == 0)
547 req_axi_vote.uncompressed_bw = req_axi_vote.compressed_bw;
548
549 if ((cpas_client->axi_vote.compressed_bw ==
550 req_axi_vote.compressed_bw) &&
551 (cpas_client->axi_vote.uncompressed_bw ==
552 req_axi_vote.uncompressed_bw))
553 return 0;
554
555 mutex_lock(&axi_port->lock);
556 cpas_client->axi_vote = req_axi_vote;
557
558 list_for_each_entry_safe(curr_client, temp_client,
559 &axi_port->clients_list_head, axi_sibling_client) {
560 camnoc_bw += curr_client->axi_vote.uncompressed_bw;
561 mnoc_bw += curr_client->axi_vote.compressed_bw;
562 }
563
564 if ((!soc_private->axi_camnoc_based) && (mnoc_bw < camnoc_bw))
565 mnoc_bw = camnoc_bw;
566
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700567 CAM_DBG(CAM_CPAS,
568 "axi[(%d, %d),(%d, %d)] : camnoc_bw[%llu], mnoc_bw[%llu]",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700569 axi_port->mnoc_bus.src, axi_port->mnoc_bus.dst,
570 axi_port->camnoc_bus.src, axi_port->camnoc_bus.dst,
571 camnoc_bw, mnoc_bw);
572
573 rc = cam_cpas_util_vote_bus_client_bw(&axi_port->mnoc_bus,
Pavan Kumar Chilamkurthi03184972017-11-03 02:16:02 -0700574 mnoc_bw, mnoc_bw);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700575 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700576 CAM_ERR(CAM_CPAS,
577 "Failed in mnoc vote ab[%llu] ib[%llu] rc=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700578 mnoc_bw, mnoc_bw, rc);
579 goto unlock_axi_port;
580 }
581
582 if (soc_private->axi_camnoc_based) {
583 rc = cam_cpas_util_vote_bus_client_bw(&axi_port->camnoc_bus,
Pavan Kumar Chilamkurthi03184972017-11-03 02:16:02 -0700584 0, camnoc_bw);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700585 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700586 CAM_ERR(CAM_CPAS,
587 "Failed camnoc vote ab[%llu] ib[%llu] rc=%d",
Jigarkumar Zala3865c1c2017-11-28 12:08:50 -0800588 (uint64_t)0, camnoc_bw, rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700589 goto unlock_axi_port;
590 }
591 }
592
593unlock_axi_port:
594 mutex_unlock(&axi_port->lock);
595 return rc;
596}
597
598static int cam_cpas_hw_update_axi_vote(struct cam_hw_info *cpas_hw,
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800599 uint32_t client_handle, struct cam_axi_vote *client_axi_vote)
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700600{
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800601 struct cam_axi_vote axi_vote;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700602 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
603 uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
604 int rc = 0;
605
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800606 if (!client_axi_vote) {
607 CAM_ERR(CAM_CPAS, "Invalid arg client_handle=%d",
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700608 client_handle);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700609 return -EINVAL;
610 }
611
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800612 axi_vote = *client_axi_vote;
613
614 if ((axi_vote.compressed_bw == 0) &&
615 (axi_vote.uncompressed_bw == 0)) {
616 CAM_DBG(CAM_CPAS, "0 vote from client_handle=%d",
617 client_handle);
618 axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
619 axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
620 }
621
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700622 if (!CAM_CPAS_CLIENT_VALID(client_indx))
623 return -EINVAL;
624
625 mutex_lock(&cpas_core->client_mutex[client_indx]);
626
627 if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700628 CAM_ERR(CAM_CPAS, "client has not started %d", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700629 rc = -EPERM;
630 goto unlock_client;
631 }
632
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700633 CAM_DBG(CAM_CPAS,
634 "Client[%d] Requested compressed[%llu], uncompressed[%llu]",
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800635 client_indx, axi_vote.compressed_bw,
636 axi_vote.uncompressed_bw);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700637
638 rc = cam_cpas_util_apply_client_axi_vote(cpas_core,
639 cpas_hw->soc_info.soc_private,
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800640 cpas_core->cpas_client[client_indx], &axi_vote);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700641
642unlock_client:
643 mutex_unlock(&cpas_core->client_mutex[client_indx]);
644 return rc;
645}
646
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700647static int cam_cpas_util_get_ahb_level(struct cam_hw_info *cpas_hw,
648 struct device *dev, unsigned long freq, enum cam_vote_level *req_level)
649{
650 struct cam_cpas_private_soc *soc_private =
651 (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
652 struct dev_pm_opp *opp;
653 unsigned int corner;
654 enum cam_vote_level level = CAM_SVS_VOTE;
655 unsigned long corner_freq = freq;
656 int i;
657
658 if (!dev || !req_level) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700659 CAM_ERR(CAM_CPAS, "Invalid params %pK, %pK", dev, req_level);
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700660 return -EINVAL;
661 }
662
663 opp = dev_pm_opp_find_freq_ceil(dev, &corner_freq);
664 if (IS_ERR(opp)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700665 CAM_ERR(CAM_CPAS, "Error on OPP freq :%ld, %pK",
666 corner_freq, opp);
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700667 return -EINVAL;
668 }
669
670 corner = dev_pm_opp_get_voltage(opp);
671
672 for (i = 0; i < soc_private->num_vdd_ahb_mapping; i++)
673 if (corner == soc_private->vdd_ahb[i].vdd_corner)
674 level = soc_private->vdd_ahb[i].ahb_level;
675
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700676 CAM_DBG(CAM_CPAS,
677 "From OPP table : freq=[%ld][%ld], corner=%d, level=%d",
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700678 freq, corner_freq, corner, level);
679
680 *req_level = level;
681
682 return 0;
683}
684
685static int cam_cpas_util_apply_client_ahb_vote(struct cam_hw_info *cpas_hw,
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700686 struct cam_cpas_client *cpas_client, struct cam_ahb_vote *ahb_vote,
687 enum cam_vote_level *applied_level)
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700688{
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700689 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700690 struct cam_cpas_bus_client *ahb_bus_client = &cpas_core->ahb_bus_client;
691 enum cam_vote_level required_level;
692 enum cam_vote_level highest_level;
693 int i, rc = 0;
694
695 if (!ahb_bus_client->valid) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700696 CAM_ERR(CAM_CPAS, "AHB Bus client not valid");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700697 return -EINVAL;
698 }
699
700 if (ahb_vote->type == CAM_VOTE_DYNAMIC) {
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700701 rc = cam_cpas_util_get_ahb_level(cpas_hw, cpas_client->data.dev,
702 ahb_vote->vote.freq, &required_level);
703 if (rc)
704 return rc;
705 } else {
706 required_level = ahb_vote->vote.level;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700707 }
708
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700709 if (cpas_client->ahb_level == required_level)
710 return 0;
711
712 mutex_lock(&ahb_bus_client->lock);
713 cpas_client->ahb_level = required_level;
714
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700715 CAM_DBG(CAM_CPAS, "Clients required level[%d], curr_level[%d]",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700716 required_level, ahb_bus_client->curr_vote_level);
717
718 if (required_level == ahb_bus_client->curr_vote_level)
719 goto unlock_bus_client;
720
721 highest_level = required_level;
722 for (i = 0; i < cpas_core->num_clients; i++) {
723 if (cpas_core->cpas_client[i] && (highest_level <
724 cpas_core->cpas_client[i]->ahb_level))
725 highest_level = cpas_core->cpas_client[i]->ahb_level;
726 }
727
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700728 CAM_DBG(CAM_CPAS, "Required highest_level[%d]", highest_level);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700729
730 rc = cam_cpas_util_vote_bus_client_level(ahb_bus_client,
731 highest_level);
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700732 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700733 CAM_ERR(CAM_CPAS, "Failed in ahb vote, level=%d, rc=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700734 highest_level, rc);
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700735 goto unlock_bus_client;
736 }
737
738 rc = cam_soc_util_set_clk_rate_level(&cpas_hw->soc_info, highest_level);
739 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700740 CAM_ERR(CAM_CPAS,
741 "Failed in scaling clock rate level %d for AHB",
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700742 highest_level);
743 goto unlock_bus_client;
744 }
745
746 if (applied_level)
747 *applied_level = highest_level;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700748
749unlock_bus_client:
750 mutex_unlock(&ahb_bus_client->lock);
751 return rc;
752}
753
754static int cam_cpas_hw_update_ahb_vote(struct cam_hw_info *cpas_hw,
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800755 uint32_t client_handle, struct cam_ahb_vote *client_ahb_vote)
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700756{
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800757 struct cam_ahb_vote ahb_vote;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700758 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
759 uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
760 int rc = 0;
761
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800762 if (!client_ahb_vote) {
763 CAM_ERR(CAM_CPAS, "Invalid input arg");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700764 return -EINVAL;
765 }
766
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800767 ahb_vote = *client_ahb_vote;
768
769 if (ahb_vote.vote.level == 0) {
770 CAM_DBG(CAM_CPAS, "0 ahb vote from client %d",
771 client_handle);
772 ahb_vote.type = CAM_VOTE_ABSOLUTE;
773 ahb_vote.vote.level = CAM_SVS_VOTE;
774 }
775
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700776 if (!CAM_CPAS_CLIENT_VALID(client_indx))
777 return -EINVAL;
778
779 mutex_lock(&cpas_core->client_mutex[client_indx]);
780
781 if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700782 CAM_ERR(CAM_CPAS, "client has not started %d", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700783 rc = -EPERM;
784 goto unlock_client;
785 }
786
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700787 CAM_DBG(CAM_CPAS,
788 "client[%d] : type[%d], level[%d], freq[%ld], applied[%d]",
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800789 client_indx, ahb_vote.type, ahb_vote.vote.level,
790 ahb_vote.vote.freq,
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700791 cpas_core->cpas_client[client_indx]->ahb_level);
792
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700793 rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw,
Pavan Kumar Chilamkurthi663a9f02017-11-22 12:09:01 -0800794 cpas_core->cpas_client[client_indx], &ahb_vote, NULL);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700795
796unlock_client:
797 mutex_unlock(&cpas_core->client_mutex[client_indx]);
798 return rc;
799}
800
801static int cam_cpas_hw_start(void *hw_priv, void *start_args,
802 uint32_t arg_size)
803{
804 struct cam_hw_info *cpas_hw;
805 struct cam_cpas *cpas_core;
806 uint32_t client_indx;
807 struct cam_cpas_hw_cmd_start *cmd_hw_start;
808 struct cam_cpas_client *cpas_client;
809 struct cam_ahb_vote *ahb_vote;
810 struct cam_axi_vote *axi_vote;
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700811 enum cam_vote_level applied_level = CAM_SVS_VOTE;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700812 int rc;
813
814 if (!hw_priv || !start_args) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700815 CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK",
816 hw_priv, start_args);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700817 return -EINVAL;
818 }
819
820 if (sizeof(struct cam_cpas_hw_cmd_start) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700821 CAM_ERR(CAM_CPAS, "HW_CAPS size mismatch %ld %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700822 sizeof(struct cam_cpas_hw_cmd_start), arg_size);
823 return -EINVAL;
824 }
825
826 cpas_hw = (struct cam_hw_info *)hw_priv;
827 cpas_core = (struct cam_cpas *) cpas_hw->core_info;
828 cmd_hw_start = (struct cam_cpas_hw_cmd_start *)start_args;
829 client_indx = CAM_CPAS_GET_CLIENT_IDX(cmd_hw_start->client_handle);
830 ahb_vote = cmd_hw_start->ahb_vote;
831 axi_vote = cmd_hw_start->axi_vote;
832
833 if (!ahb_vote || !axi_vote)
834 return -EINVAL;
835
836 if ((ahb_vote->vote.level == 0) || ((axi_vote->compressed_bw == 0) &&
837 (axi_vote->uncompressed_bw == 0))) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700838 CAM_ERR(CAM_CPAS, "Invalid vote ahb[%d], axi[%llu], [%llu]",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700839 ahb_vote->vote.level, axi_vote->compressed_bw,
840 axi_vote->uncompressed_bw);
841 return -EINVAL;
842 }
843
844 if (!CAM_CPAS_CLIENT_VALID(client_indx))
845 return -EINVAL;
846
847 mutex_lock(&cpas_hw->hw_mutex);
848 mutex_lock(&cpas_core->client_mutex[client_indx]);
849
850 if (!CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700851 CAM_ERR(CAM_CPAS, "client is not registered %d", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700852 rc = -EPERM;
853 goto done;
854 }
855
856 if (CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700857 CAM_ERR(CAM_CPAS, "Client %d is in start state", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700858 rc = -EPERM;
859 goto done;
860 }
861
862 cpas_client = cpas_core->cpas_client[client_indx];
863
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700864 CAM_DBG(CAM_CPAS, "AHB :client[%d] type[%d], level[%d], applied[%d]",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700865 client_indx, ahb_vote->type, ahb_vote->vote.level,
866 cpas_client->ahb_level);
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700867 rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700868 ahb_vote, &applied_level);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700869 if (rc)
870 goto done;
871
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700872 CAM_DBG(CAM_CPAS,
873 "AXI client[%d] compressed_bw[%llu], uncompressed_bw[%llu]",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700874 client_indx, axi_vote->compressed_bw,
875 axi_vote->uncompressed_bw);
876 rc = cam_cpas_util_apply_client_axi_vote(cpas_core,
877 cpas_hw->soc_info.soc_private, cpas_client, axi_vote);
878 if (rc)
879 goto done;
880
881 if (cpas_core->streamon_clients == 0) {
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700882 rc = cam_cpas_soc_enable_resources(&cpas_hw->soc_info,
883 applied_level);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700884 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700885 CAM_ERR(CAM_CPAS, "enable_resorce failed, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700886 goto done;
887 }
888
Pavan Kumar Chilamkurthi12ae8502017-05-29 19:07:15 -0700889 if (cpas_core->internal_ops.power_on) {
890 rc = cpas_core->internal_ops.power_on(cpas_hw);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700891 if (rc) {
892 cam_cpas_soc_disable_resources(
893 &cpas_hw->soc_info);
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700894 CAM_ERR(CAM_CPAS,
895 "failed in power_on settings rc=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700896 rc);
897 goto done;
898 }
899 }
900 cpas_hw->hw_state = CAM_HW_STATE_POWER_UP;
901 }
902
903 cpas_client->started = true;
904 cpas_core->streamon_clients++;
905
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700906 CAM_DBG(CAM_CPAS, "client_indx=%d, streamon_clients=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700907 client_indx, cpas_core->streamon_clients);
908done:
909 mutex_unlock(&cpas_core->client_mutex[client_indx]);
910 mutex_unlock(&cpas_hw->hw_mutex);
911 return rc;
912}
913
914
915static int cam_cpas_hw_stop(void *hw_priv, void *stop_args,
916 uint32_t arg_size)
917{
918 struct cam_hw_info *cpas_hw;
919 struct cam_cpas *cpas_core;
920 uint32_t client_indx;
921 struct cam_cpas_hw_cmd_stop *cmd_hw_stop;
922 struct cam_cpas_client *cpas_client;
923 struct cam_ahb_vote ahb_vote;
924 struct cam_axi_vote axi_vote;
925 int rc = 0;
926
927 if (!hw_priv || !stop_args) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700928 CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK",
929 hw_priv, stop_args);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700930 return -EINVAL;
931 }
932
933 if (sizeof(struct cam_cpas_hw_cmd_stop) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700934 CAM_ERR(CAM_CPAS, "HW_CAPS size mismatch %ld %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700935 sizeof(struct cam_cpas_hw_cmd_stop), arg_size);
936 return -EINVAL;
937 }
938
939 cpas_hw = (struct cam_hw_info *)hw_priv;
940 cpas_core = (struct cam_cpas *) cpas_hw->core_info;
941 cmd_hw_stop = (struct cam_cpas_hw_cmd_stop *)stop_args;
942 client_indx = CAM_CPAS_GET_CLIENT_IDX(cmd_hw_stop->client_handle);
943
944 if (!CAM_CPAS_CLIENT_VALID(client_indx))
945 return -EINVAL;
946
947 mutex_lock(&cpas_hw->hw_mutex);
948 mutex_lock(&cpas_core->client_mutex[client_indx]);
949
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700950 CAM_DBG(CAM_CPAS, "client_indx=%d, streamon_clients=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700951 client_indx, cpas_core->streamon_clients);
952
953 if (!CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700954 CAM_ERR(CAM_CPAS, "Client %d is not started", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700955 rc = -EPERM;
956 goto done;
957 }
958
959 cpas_client = cpas_core->cpas_client[client_indx];
960 cpas_client->started = false;
961 cpas_core->streamon_clients--;
962
963 if (cpas_core->streamon_clients == 0) {
Pavan Kumar Chilamkurthi12ae8502017-05-29 19:07:15 -0700964 if (cpas_core->internal_ops.power_off) {
965 rc = cpas_core->internal_ops.power_off(cpas_hw);
966 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700967 CAM_ERR(CAM_CPAS,
968 "failed in power_off settings rc=%d",
Pavan Kumar Chilamkurthi12ae8502017-05-29 19:07:15 -0700969 rc);
970 /* Do not return error, passthrough */
971 }
972 }
973
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700974 rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
975 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -0700976 CAM_ERR(CAM_CPAS, "disable_resorce failed, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700977 goto done;
978 }
979 cpas_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
980 }
981
982 ahb_vote.type = CAM_VOTE_ABSOLUTE;
983 ahb_vote.vote.level = CAM_SUSPEND_VOTE;
Pavan Kumar Chilamkurthie210ec82017-05-31 22:29:35 -0700984 rc = cam_cpas_util_apply_client_ahb_vote(cpas_hw, cpas_client,
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700985 &ahb_vote, NULL);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -0700986 if (rc)
987 goto done;
988
989 axi_vote.uncompressed_bw = 0;
990 axi_vote.compressed_bw = 0;
991 rc = cam_cpas_util_apply_client_axi_vote(cpas_core,
992 cpas_hw->soc_info.soc_private, cpas_client, &axi_vote);
993
994done:
995 mutex_unlock(&cpas_core->client_mutex[client_indx]);
996 mutex_unlock(&cpas_hw->hw_mutex);
997 return rc;
998}
999
1000static int cam_cpas_hw_init(void *hw_priv, void *init_hw_args,
1001 uint32_t arg_size)
1002{
1003 struct cam_hw_info *cpas_hw;
1004 struct cam_cpas *cpas_core;
1005 int rc = 0;
1006
1007 if (!hw_priv || !init_hw_args) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001008 CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK",
1009 hw_priv, init_hw_args);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001010 return -EINVAL;
1011 }
1012
1013 if (sizeof(struct cam_cpas_hw_caps) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001014 CAM_ERR(CAM_CPAS, "INIT HW size mismatch %ld %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001015 sizeof(struct cam_cpas_hw_caps), arg_size);
1016 return -EINVAL;
1017 }
1018
1019 cpas_hw = (struct cam_hw_info *)hw_priv;
1020 cpas_core = (struct cam_cpas *)cpas_hw->core_info;
1021
1022 if (cpas_core->internal_ops.init_hw_version) {
1023 rc = cpas_core->internal_ops.init_hw_version(cpas_hw,
1024 (struct cam_cpas_hw_caps *)init_hw_args);
1025 }
1026
1027 return rc;
1028}
1029
1030static int cam_cpas_hw_register_client(struct cam_hw_info *cpas_hw,
1031 struct cam_cpas_register_params *register_params)
1032{
1033 int rc;
1034 struct cam_cpas_client *cpas_client;
1035 char client_name[CAM_HW_IDENTIFIER_LENGTH + 3];
1036 int32_t client_indx = -1;
1037 struct cam_cpas *cpas_core = (struct cam_cpas *)cpas_hw->core_info;
1038 struct cam_cpas_private_soc *soc_private =
1039 (struct cam_cpas_private_soc *) cpas_hw->soc_info.soc_private;
1040
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001041 CAM_DBG(CAM_CPAS, "Register params : identifier=%s, cell_index=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001042 register_params->identifier, register_params->cell_index);
1043
1044 if (soc_private->client_id_based)
1045 snprintf(client_name, sizeof(client_name), "%s%d",
1046 register_params->identifier,
1047 register_params->cell_index);
1048 else
1049 snprintf(client_name, sizeof(client_name), "%s",
1050 register_params->identifier);
1051
1052 mutex_lock(&cpas_hw->hw_mutex);
1053
Pavan Kumar Chilamkurthieb8833c2017-07-15 19:44:13 -07001054 rc = cam_common_util_get_string_index(soc_private->client_name,
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001055 soc_private->num_clients, client_name, &client_indx);
1056 if (rc || !CAM_CPAS_CLIENT_VALID(client_indx) ||
1057 CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001058 CAM_ERR(CAM_CPAS, "Invalid Client register : %s %d, %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001059 register_params->identifier,
1060 register_params->cell_index, client_indx);
1061 mutex_unlock(&cpas_hw->hw_mutex);
1062 return -EPERM;
1063 }
1064
1065 cpas_client = kzalloc(sizeof(struct cam_cpas_client), GFP_KERNEL);
1066 if (!cpas_client) {
1067 mutex_unlock(&cpas_hw->hw_mutex);
1068 return -ENOMEM;
1069 }
1070
1071 rc = cam_cpas_util_insert_client_to_axi_port(cpas_core, soc_private,
1072 cpas_client, client_indx);
1073 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001074 CAM_ERR(CAM_CPAS,
1075 "axi_port_insert failed client_indx=%d, rc=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001076 client_indx, rc);
1077 kfree(cpas_client);
1078 mutex_unlock(&cpas_hw->hw_mutex);
1079 return -EINVAL;
1080 }
1081
1082 register_params->client_handle =
1083 CAM_CPAS_GET_CLIENT_HANDLE(client_indx);
1084 memcpy(&cpas_client->data, register_params,
1085 sizeof(struct cam_cpas_register_params));
1086 cpas_core->cpas_client[client_indx] = cpas_client;
1087 cpas_core->registered_clients++;
1088
1089 mutex_unlock(&cpas_hw->hw_mutex);
1090
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001091 CAM_DBG(CAM_CPAS, "client_indx=%d, registered_clients=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001092 client_indx, cpas_core->registered_clients);
1093
1094 return 0;
1095}
1096
1097static int cam_cpas_hw_unregister_client(struct cam_hw_info *cpas_hw,
1098 uint32_t client_handle)
1099{
1100 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
1101 uint32_t client_indx = CAM_CPAS_GET_CLIENT_IDX(client_handle);
1102 int rc = 0;
1103
1104 if (!CAM_CPAS_CLIENT_VALID(client_indx))
1105 return -EINVAL;
1106
1107 mutex_lock(&cpas_hw->hw_mutex);
1108 mutex_lock(&cpas_core->client_mutex[client_indx]);
1109
1110 if (!CAM_CPAS_CLIENT_REGISTERED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001111 CAM_ERR(CAM_CPAS, "client not registered %d", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001112 rc = -EPERM;
1113 goto done;
1114 }
1115
1116 if (CAM_CPAS_CLIENT_STARTED(cpas_core, client_indx)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001117 CAM_ERR(CAM_CPAS, "Client %d is not stopped", client_indx);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001118 rc = -EPERM;
1119 goto done;
1120 }
1121
1122 cam_cpas_util_remove_client_from_axi_port(
1123 cpas_core->cpas_client[client_indx]);
1124
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001125 CAM_DBG(CAM_CPAS, "client_indx=%d, registered_clients=%d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001126 client_indx, cpas_core->registered_clients);
1127
1128 kfree(cpas_core->cpas_client[client_indx]);
1129 cpas_core->cpas_client[client_indx] = NULL;
1130 cpas_core->registered_clients--;
1131done:
1132 mutex_unlock(&cpas_core->client_mutex[client_indx]);
1133 mutex_unlock(&cpas_hw->hw_mutex);
1134 return rc;
1135}
1136
1137static int cam_cpas_hw_get_hw_info(void *hw_priv,
1138 void *get_hw_cap_args, uint32_t arg_size)
1139{
1140 struct cam_hw_info *cpas_hw;
1141 struct cam_cpas *cpas_core;
1142 struct cam_cpas_hw_caps *hw_caps;
1143
1144 if (!hw_priv || !get_hw_cap_args) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001145 CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK",
1146 hw_priv, get_hw_cap_args);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001147 return -EINVAL;
1148 }
1149
1150 if (sizeof(struct cam_cpas_hw_caps) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001151 CAM_ERR(CAM_CPAS, "HW_CAPS size mismatch %ld %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001152 sizeof(struct cam_cpas_hw_caps), arg_size);
1153 return -EINVAL;
1154 }
1155
1156 cpas_hw = (struct cam_hw_info *)hw_priv;
1157 cpas_core = (struct cam_cpas *) cpas_hw->core_info;
1158 hw_caps = (struct cam_cpas_hw_caps *)get_hw_cap_args;
1159
1160 *hw_caps = cpas_core->hw_caps;
1161
1162 return 0;
1163}
1164
1165
1166static int cam_cpas_hw_process_cmd(void *hw_priv,
1167 uint32_t cmd_type, void *cmd_args, uint32_t arg_size)
1168{
1169 int rc = -EINVAL;
1170
1171 if (!hw_priv || !cmd_args ||
1172 (cmd_type >= CAM_CPAS_HW_CMD_INVALID)) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001173 CAM_ERR(CAM_CPAS, "Invalid arguments %pK %pK %d",
1174 hw_priv, cmd_args, cmd_type);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001175 return -EINVAL;
1176 }
1177
1178 switch (cmd_type) {
1179 case CAM_CPAS_HW_CMD_REGISTER_CLIENT: {
1180 struct cam_cpas_register_params *register_params;
1181
1182 if (sizeof(struct cam_cpas_register_params) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001183 CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001184 cmd_type, arg_size);
1185 break;
1186 }
1187
1188 register_params = (struct cam_cpas_register_params *)cmd_args;
1189 rc = cam_cpas_hw_register_client(hw_priv, register_params);
1190 break;
1191 }
1192 case CAM_CPAS_HW_CMD_UNREGISTER_CLIENT: {
1193 uint32_t *client_handle;
1194
1195 if (sizeof(uint32_t) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001196 CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001197 cmd_type, arg_size);
1198 break;
1199 }
1200
1201 client_handle = (uint32_t *)cmd_args;
1202 rc = cam_cpas_hw_unregister_client(hw_priv, *client_handle);
1203 break;
1204 }
1205 case CAM_CPAS_HW_CMD_REG_WRITE: {
1206 struct cam_cpas_hw_cmd_reg_read_write *reg_write;
1207
1208 if (sizeof(struct cam_cpas_hw_cmd_reg_read_write) !=
1209 arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001210 CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001211 cmd_type, arg_size);
1212 break;
1213 }
1214
1215 reg_write =
1216 (struct cam_cpas_hw_cmd_reg_read_write *)cmd_args;
1217 rc = cam_cpas_hw_reg_write(hw_priv, reg_write->client_handle,
1218 reg_write->reg_base, reg_write->offset, reg_write->mb,
1219 reg_write->value);
1220 break;
1221 }
1222 case CAM_CPAS_HW_CMD_REG_READ: {
1223 struct cam_cpas_hw_cmd_reg_read_write *reg_read;
1224
1225 if (sizeof(struct cam_cpas_hw_cmd_reg_read_write) !=
1226 arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001227 CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001228 cmd_type, arg_size);
1229 break;
1230 }
1231
1232 reg_read =
1233 (struct cam_cpas_hw_cmd_reg_read_write *)cmd_args;
1234 rc = cam_cpas_hw_reg_read(hw_priv,
1235 reg_read->client_handle, reg_read->reg_base,
1236 reg_read->offset, reg_read->mb, &reg_read->value);
1237
1238 break;
1239 }
1240 case CAM_CPAS_HW_CMD_AHB_VOTE: {
1241 struct cam_cpas_hw_cmd_ahb_vote *cmd_ahb_vote;
1242
1243 if (sizeof(struct cam_cpas_hw_cmd_ahb_vote) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001244 CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001245 cmd_type, arg_size);
1246 break;
1247 }
1248
1249 cmd_ahb_vote = (struct cam_cpas_hw_cmd_ahb_vote *)cmd_args;
1250 rc = cam_cpas_hw_update_ahb_vote(hw_priv,
1251 cmd_ahb_vote->client_handle, cmd_ahb_vote->ahb_vote);
1252 break;
1253 }
1254 case CAM_CPAS_HW_CMD_AXI_VOTE: {
1255 struct cam_cpas_hw_cmd_axi_vote *cmd_axi_vote;
1256
1257 if (sizeof(struct cam_cpas_hw_cmd_axi_vote) != arg_size) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001258 CAM_ERR(CAM_CPAS, "cmd_type %d, size mismatch %d",
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001259 cmd_type, arg_size);
1260 break;
1261 }
1262
1263 cmd_axi_vote = (struct cam_cpas_hw_cmd_axi_vote *)cmd_args;
1264 rc = cam_cpas_hw_update_axi_vote(hw_priv,
1265 cmd_axi_vote->client_handle, cmd_axi_vote->axi_vote);
1266 break;
1267 }
1268 default:
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001269 CAM_ERR(CAM_CPAS, "CPAS HW command not valid =%d", cmd_type);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001270 break;
1271 }
1272
1273 return rc;
1274}
1275
1276static int cam_cpas_util_client_setup(struct cam_hw_info *cpas_hw)
1277{
1278 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
1279 int i;
1280
Junzhe Zou2eec4f22017-10-17 19:08:18 -07001281 for (i = 0; i < CAM_CPAS_MAX_CLIENTS; i++) {
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001282 mutex_init(&cpas_core->client_mutex[i]);
1283 cpas_core->cpas_client[i] = NULL;
1284 }
1285
1286 return 0;
1287}
1288
1289static int cam_cpas_util_client_cleanup(struct cam_hw_info *cpas_hw)
1290{
1291 struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
1292 int i;
1293
Junzhe Zou2eec4f22017-10-17 19:08:18 -07001294 for (i = 0; i < CAM_CPAS_MAX_CLIENTS; i++) {
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001295 if (cpas_core->cpas_client[i]) {
1296 cam_cpas_hw_unregister_client(cpas_hw, i);
1297 cpas_core->cpas_client[i] = NULL;
1298 }
1299 mutex_destroy(&cpas_core->client_mutex[i]);
1300 }
1301
1302 return 0;
1303}
1304
1305static int cam_cpas_util_get_internal_ops(struct platform_device *pdev,
1306 struct cam_hw_intf *hw_intf, struct cam_cpas_internal_ops *internal_ops)
1307{
1308 struct device_node *of_node = pdev->dev.of_node;
1309 int rc;
1310 const char *compat_str = NULL;
1311
1312 rc = of_property_read_string_index(of_node, "arch-compat", 0,
1313 (const char **)&compat_str);
1314 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001315 CAM_ERR(CAM_CPAS, "failed to get arch-compat rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001316 return -EINVAL;
1317 }
1318
1319 if (strnstr(compat_str, "camss_top", strlen(compat_str))) {
1320 hw_intf->hw_type = CAM_HW_CAMSSTOP;
1321 rc = cam_camsstop_get_internal_ops(internal_ops);
1322 } else if (strnstr(compat_str, "cpas_top", strlen(compat_str))) {
1323 hw_intf->hw_type = CAM_HW_CPASTOP;
1324 rc = cam_cpastop_get_internal_ops(internal_ops);
1325 } else {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001326 CAM_ERR(CAM_CPAS, "arch-compat %s not supported", compat_str);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001327 rc = -EINVAL;
1328 }
1329
1330 return rc;
1331}
1332
Mark Woh8a2dc2d2017-10-10 19:52:39 -07001333static int cam_cpas_util_get_hw_version(struct platform_device *pdev,
1334 struct cam_hw_soc_info *soc_info)
1335{
1336 struct device_node *of_node = pdev->dev.of_node;
1337 int rc;
1338
1339 soc_info->hw_version = 0;
1340
1341 rc = of_property_read_u32(of_node,
1342 "qcom,cpas-hw-ver", &soc_info->hw_version);
1343
1344 CAM_DBG(CAM_CPAS, "CPAS HW VERSION %x", soc_info->hw_version);
1345
1346 if (rc) {
1347 CAM_ERR(CAM_CPAS, "failed to get CPAS HW Version rc=%d", rc);
1348 return -EINVAL;
1349 }
1350
1351 return rc;
1352}
1353
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001354int cam_cpas_hw_probe(struct platform_device *pdev,
1355 struct cam_hw_intf **hw_intf)
1356{
1357 int rc = 0;
1358 int i;
1359 struct cam_hw_info *cpas_hw = NULL;
1360 struct cam_hw_intf *cpas_hw_intf = NULL;
1361 struct cam_cpas *cpas_core = NULL;
1362 struct cam_cpas_private_soc *soc_private;
1363 struct cam_cpas_internal_ops *internal_ops;
1364
1365 cpas_hw_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
1366 if (!cpas_hw_intf)
1367 return -ENOMEM;
1368
1369 cpas_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
1370 if (!cpas_hw) {
1371 kfree(cpas_hw_intf);
1372 return -ENOMEM;
1373 }
1374
1375 cpas_core = kzalloc(sizeof(struct cam_cpas), GFP_KERNEL);
1376 if (!cpas_core) {
1377 kfree(cpas_hw);
1378 kfree(cpas_hw_intf);
1379 return -ENOMEM;
1380 }
1381
1382 for (i = 0; i < CAM_CPAS_REG_MAX; i++)
1383 cpas_core->regbase_index[i] = -1;
1384
1385 cpas_hw_intf->hw_priv = cpas_hw;
1386 cpas_hw->core_info = cpas_core;
1387
1388 cpas_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
1389 cpas_hw->soc_info.pdev = pdev;
Jigarkumar Zalab7b49f12017-08-21 16:45:38 -07001390 cpas_hw->soc_info.dev = &pdev->dev;
1391 cpas_hw->soc_info.dev_name = pdev->name;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001392 cpas_hw->open_count = 0;
1393 mutex_init(&cpas_hw->hw_mutex);
1394 spin_lock_init(&cpas_hw->hw_lock);
1395 init_completion(&cpas_hw->hw_complete);
1396
1397 cpas_hw_intf->hw_ops.get_hw_caps = cam_cpas_hw_get_hw_info;
1398 cpas_hw_intf->hw_ops.init = cam_cpas_hw_init;
1399 cpas_hw_intf->hw_ops.deinit = NULL;
1400 cpas_hw_intf->hw_ops.reset = NULL;
1401 cpas_hw_intf->hw_ops.reserve = NULL;
1402 cpas_hw_intf->hw_ops.release = NULL;
1403 cpas_hw_intf->hw_ops.start = cam_cpas_hw_start;
1404 cpas_hw_intf->hw_ops.stop = cam_cpas_hw_stop;
1405 cpas_hw_intf->hw_ops.read = NULL;
1406 cpas_hw_intf->hw_ops.write = NULL;
1407 cpas_hw_intf->hw_ops.process_cmd = cam_cpas_hw_process_cmd;
1408
Pavan Kumar Chilamkurthi31c35ff2017-05-29 04:26:14 -07001409 cpas_core->work_queue = alloc_workqueue("cam-cpas",
1410 WQ_UNBOUND | WQ_MEM_RECLAIM, CAM_CPAS_INFLIGHT_WORKS);
1411 if (!cpas_core->work_queue) {
1412 rc = -ENOMEM;
1413 goto release_mem;
1414 }
1415
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001416 internal_ops = &cpas_core->internal_ops;
1417 rc = cam_cpas_util_get_internal_ops(pdev, cpas_hw_intf, internal_ops);
Pavan Kumar Chilamkurthi31c35ff2017-05-29 04:26:14 -07001418 if (rc)
1419 goto release_workq;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001420
1421 rc = cam_cpas_soc_init_resources(&cpas_hw->soc_info,
1422 internal_ops->handle_irq, cpas_hw);
1423 if (rc)
Pavan Kumar Chilamkurthi31c35ff2017-05-29 04:26:14 -07001424 goto release_workq;
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001425
1426 soc_private = (struct cam_cpas_private_soc *)
1427 cpas_hw->soc_info.soc_private;
1428 cpas_core->num_clients = soc_private->num_clients;
1429
1430 if (internal_ops->setup_regbase) {
1431 rc = internal_ops->setup_regbase(&cpas_hw->soc_info,
1432 cpas_core->regbase_index, CAM_CPAS_REG_MAX);
1433 if (rc)
1434 goto deinit_platform_res;
1435 }
1436
1437 rc = cam_cpas_util_client_setup(cpas_hw);
1438 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001439 CAM_ERR(CAM_CPAS, "failed in client setup, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001440 goto deinit_platform_res;
1441 }
1442
1443 rc = cam_cpas_util_register_bus_client(&cpas_hw->soc_info,
1444 cpas_hw->soc_info.pdev->dev.of_node,
1445 &cpas_core->ahb_bus_client);
1446 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001447 CAM_ERR(CAM_CPAS, "failed in ahb setup, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001448 goto client_cleanup;
1449 }
1450
1451 rc = cam_cpas_util_axi_setup(cpas_core, &cpas_hw->soc_info);
1452 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001453 CAM_ERR(CAM_CPAS, "failed in axi setup, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001454 goto ahb_cleanup;
1455 }
1456
1457 /* Need to vote first before enabling clocks */
1458 rc = cam_cpas_util_vote_default_ahb_axi(cpas_hw, true);
1459 if (rc)
1460 goto axi_cleanup;
1461
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -07001462 rc = cam_cpas_soc_enable_resources(&cpas_hw->soc_info, CAM_SVS_VOTE);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001463 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001464 CAM_ERR(CAM_CPAS, "failed in soc_enable_resources, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001465 goto remove_default_vote;
1466 }
1467
1468 if (internal_ops->get_hw_info) {
1469 rc = internal_ops->get_hw_info(cpas_hw, &cpas_core->hw_caps);
1470 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001471 CAM_ERR(CAM_CPAS, "failed in get_hw_info, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001472 goto disable_soc_res;
1473 }
1474 } else {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001475 CAM_ERR(CAM_CPAS, "Invalid get_hw_info");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001476 goto disable_soc_res;
1477 }
1478
1479 rc = cam_cpas_hw_init(cpas_hw_intf->hw_priv,
1480 &cpas_core->hw_caps, sizeof(struct cam_cpas_hw_caps));
1481 if (rc)
1482 goto disable_soc_res;
1483
1484 rc = cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
1485 if (rc) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001486 CAM_ERR(CAM_CPAS, "failed in soc_disable_resources, rc=%d", rc);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001487 goto remove_default_vote;
1488 }
1489
1490 rc = cam_cpas_util_vote_default_ahb_axi(cpas_hw, false);
1491 if (rc)
1492 goto axi_cleanup;
1493
Mark Woh8a2dc2d2017-10-10 19:52:39 -07001494 rc = cam_cpas_util_get_hw_version(pdev, &cpas_hw->soc_info);
1495 if (rc)
1496 goto axi_cleanup;
1497
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001498 *hw_intf = cpas_hw_intf;
1499 return 0;
1500
1501disable_soc_res:
1502 cam_cpas_soc_disable_resources(&cpas_hw->soc_info);
1503remove_default_vote:
1504 cam_cpas_util_vote_default_ahb_axi(cpas_hw, false);
1505axi_cleanup:
1506 cam_cpas_util_axi_cleanup(cpas_core, &cpas_hw->soc_info);
1507ahb_cleanup:
1508 cam_cpas_util_unregister_bus_client(&cpas_core->ahb_bus_client);
1509client_cleanup:
1510 cam_cpas_util_client_cleanup(cpas_hw);
1511deinit_platform_res:
1512 cam_cpas_soc_deinit_resources(&cpas_hw->soc_info);
Pavan Kumar Chilamkurthi31c35ff2017-05-29 04:26:14 -07001513release_workq:
1514 flush_workqueue(cpas_core->work_queue);
1515 destroy_workqueue(cpas_core->work_queue);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001516release_mem:
1517 mutex_destroy(&cpas_hw->hw_mutex);
1518 kfree(cpas_core);
1519 kfree(cpas_hw);
1520 kfree(cpas_hw_intf);
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001521 CAM_ERR(CAM_CPAS, "failed in hw probe");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001522 return rc;
1523}
1524
1525int cam_cpas_hw_remove(struct cam_hw_intf *cpas_hw_intf)
1526{
1527 struct cam_hw_info *cpas_hw;
1528 struct cam_cpas *cpas_core;
1529
1530 if (!cpas_hw_intf) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001531 CAM_ERR(CAM_CPAS, "cpas interface not initialized");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001532 return -EINVAL;
1533 }
1534
1535 cpas_hw = (struct cam_hw_info *)cpas_hw_intf->hw_priv;
1536 cpas_core = (struct cam_cpas *)cpas_hw->core_info;
1537
1538 if (cpas_hw->hw_state == CAM_HW_STATE_POWER_UP) {
Pavan Kumar Chilamkurthi36aac922017-07-17 01:06:52 -07001539 CAM_ERR(CAM_CPAS, "cpas hw is in power up state");
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001540 return -EINVAL;
1541 }
1542
1543 cam_cpas_util_axi_cleanup(cpas_core, &cpas_hw->soc_info);
1544 cam_cpas_util_unregister_bus_client(&cpas_core->ahb_bus_client);
1545 cam_cpas_util_client_cleanup(cpas_hw);
1546 cam_cpas_soc_deinit_resources(&cpas_hw->soc_info);
Pavan Kumar Chilamkurthi31c35ff2017-05-29 04:26:14 -07001547 flush_workqueue(cpas_core->work_queue);
1548 destroy_workqueue(cpas_core->work_queue);
Pavan Kumar Chilamkurthi76678e92017-05-08 17:15:51 -07001549 mutex_destroy(&cpas_hw->hw_mutex);
1550 kfree(cpas_core);
1551 kfree(cpas_hw);
1552 kfree(cpas_hw_intf);
1553
1554 return 0;
1555}