1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <linux/init.h>
25#include <media/v4l2-event.h>
26
27#include "hmm.h"
28
29#include "atomisp_acc.h"
30#include "atomisp_internal.h"
31#include "atomisp_compat.h"
32#include "atomisp_cmd.h"
33
34#include "ia_css.h"
35
36static const struct {
37 unsigned int flag;
38 enum ia_css_pipe_id pipe_id;
39} acc_flag_to_pipe[] = {
40 { ATOMISP_ACC_FW_LOAD_FL_PREVIEW, IA_CSS_PIPE_ID_PREVIEW },
41 { ATOMISP_ACC_FW_LOAD_FL_COPY, IA_CSS_PIPE_ID_COPY },
42 { ATOMISP_ACC_FW_LOAD_FL_VIDEO, IA_CSS_PIPE_ID_VIDEO },
43 { ATOMISP_ACC_FW_LOAD_FL_CAPTURE, IA_CSS_PIPE_ID_CAPTURE },
44 { ATOMISP_ACC_FW_LOAD_FL_ACC, IA_CSS_PIPE_ID_ACC }
45};
46
47
48
49
50
51static struct atomisp_acc_fw *acc_alloc_fw(unsigned int fw_size)
52{
53 struct atomisp_acc_fw *acc_fw;
54
55 acc_fw = kzalloc(sizeof(*acc_fw), GFP_KERNEL);
56 if (!acc_fw)
57 return NULL;
58
59 acc_fw->fw = vmalloc(fw_size);
60 if (!acc_fw->fw) {
61 kfree(acc_fw);
62 return NULL;
63 }
64
65 return acc_fw;
66}
67
68static void acc_free_fw(struct atomisp_acc_fw *acc_fw)
69{
70 vfree(acc_fw->fw);
71 kfree(acc_fw);
72}
73
74static struct atomisp_acc_fw *
75acc_get_fw(struct atomisp_sub_device *asd, unsigned int handle)
76{
77 struct atomisp_acc_fw *acc_fw;
78
79 list_for_each_entry(acc_fw, &asd->acc.fw, list)
80 if (acc_fw->handle == handle)
81 return acc_fw;
82
83 return NULL;
84}
85
86static struct atomisp_map *acc_get_map(struct atomisp_sub_device *asd,
87 unsigned long css_ptr, size_t length)
88{
89 struct atomisp_map *atomisp_map;
90
91 list_for_each_entry(atomisp_map, &asd->acc.memory_maps, list) {
92 if (atomisp_map->ptr == css_ptr &&
93 atomisp_map->length == length)
94 return atomisp_map;
95 }
96 return NULL;
97}
98
99static int acc_stop_acceleration(struct atomisp_sub_device *asd)
100{
101 int ret;
102
103 ret = atomisp_css_stop_acc_pipe(asd);
104 atomisp_css_destroy_acc_pipe(asd);
105
106 return ret;
107}
108
109void atomisp_acc_cleanup(struct atomisp_device *isp)
110{
111 int i;
112
113 for (i = 0; i < isp->num_of_streams; i++)
114 ida_destroy(&isp->asd[i].acc.ida);
115}
116
117void atomisp_acc_release(struct atomisp_sub_device *asd)
118{
119 struct atomisp_acc_fw *acc_fw, *ta;
120 struct atomisp_map *atomisp_map, *tm;
121
122
123 if (asd->acc.pipeline)
124 acc_stop_acceleration(asd);
125
126
127 list_for_each_entry_safe(acc_fw, ta, &asd->acc.fw, list) {
128 list_del(&acc_fw->list);
129 ida_free(&asd->acc.ida, acc_fw->handle);
130 acc_free_fw(acc_fw);
131 }
132
133
134 list_for_each_entry_safe(atomisp_map, tm, &asd->acc.memory_maps, list) {
135 list_del(&atomisp_map->list);
136 hmm_free(atomisp_map->ptr);
137 kfree(atomisp_map);
138 }
139}
140
141int atomisp_acc_load_to_pipe(struct atomisp_sub_device *asd,
142 struct atomisp_acc_fw_load_to_pipe *user_fw)
143{
144 static const unsigned int pipeline_flags =
145 ATOMISP_ACC_FW_LOAD_FL_PREVIEW | ATOMISP_ACC_FW_LOAD_FL_COPY |
146 ATOMISP_ACC_FW_LOAD_FL_VIDEO |
147 ATOMISP_ACC_FW_LOAD_FL_CAPTURE | ATOMISP_ACC_FW_LOAD_FL_ACC;
148
149 struct atomisp_acc_fw *acc_fw;
150 int handle;
151
152 if (!user_fw->data || user_fw->size < sizeof(*acc_fw->fw))
153 return -EINVAL;
154
155
156 if (!(user_fw->flags & pipeline_flags))
157 return -EINVAL;
158
159
160 if (user_fw->flags & ~pipeline_flags)
161 return -EINVAL;
162
163 if (user_fw->type < ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT ||
164 user_fw->type > ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE)
165 return -EINVAL;
166
167 if (asd->acc.pipeline || asd->acc.extension_mode)
168 return -EBUSY;
169
170 acc_fw = acc_alloc_fw(user_fw->size);
171 if (!acc_fw)
172 return -ENOMEM;
173
174 if (copy_from_user(acc_fw->fw, user_fw->data, user_fw->size)) {
175 acc_free_fw(acc_fw);
176 return -EFAULT;
177 }
178
179 handle = ida_alloc(&asd->acc.ida, GFP_KERNEL);
180 if (handle < 0) {
181 acc_free_fw(acc_fw);
182 return -ENOSPC;
183 }
184
185 user_fw->fw_handle = handle;
186 acc_fw->handle = handle;
187 acc_fw->flags = user_fw->flags;
188 acc_fw->type = user_fw->type;
189 acc_fw->fw->handle = handle;
190
191
192
193
194
195 if (acc_fw->fw->type == ia_css_isp_firmware) {
196 static const int type_to_css[] = {
197 [ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT] =
198 IA_CSS_ACC_OUTPUT,
199 [ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER] =
200 IA_CSS_ACC_VIEWFINDER,
201 [ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE] =
202 IA_CSS_ACC_STANDALONE,
203 };
204 acc_fw->fw->info.isp.type = type_to_css[acc_fw->type];
205 }
206
207 list_add_tail(&acc_fw->list, &asd->acc.fw);
208 return 0;
209}
210
211int atomisp_acc_load(struct atomisp_sub_device *asd,
212 struct atomisp_acc_fw_load *user_fw)
213{
214 struct atomisp_acc_fw_load_to_pipe ltp = {0};
215 int r;
216
217 ltp.flags = ATOMISP_ACC_FW_LOAD_FL_ACC;
218 ltp.type = ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE;
219 ltp.size = user_fw->size;
220 ltp.data = user_fw->data;
221 r = atomisp_acc_load_to_pipe(asd, <p);
222 user_fw->fw_handle = ltp.fw_handle;
223 return r;
224}
225
226int atomisp_acc_unload(struct atomisp_sub_device *asd, unsigned int *handle)
227{
228 struct atomisp_acc_fw *acc_fw;
229
230 if (asd->acc.pipeline || asd->acc.extension_mode)
231 return -EBUSY;
232
233 acc_fw = acc_get_fw(asd, *handle);
234 if (!acc_fw)
235 return -EINVAL;
236
237 list_del(&acc_fw->list);
238 ida_free(&asd->acc.ida, acc_fw->handle);
239 acc_free_fw(acc_fw);
240
241 return 0;
242}
243
244int atomisp_acc_start(struct atomisp_sub_device *asd, unsigned int *handle)
245{
246 struct atomisp_device *isp = asd->isp;
247 struct atomisp_acc_fw *acc_fw;
248 int ret;
249 unsigned int nbin;
250
251 if (asd->acc.pipeline || asd->acc.extension_mode)
252 return -EBUSY;
253
254
255 wbinvd();
256
257 ret = atomisp_css_create_acc_pipe(asd);
258 if (ret)
259 return ret;
260
261 nbin = 0;
262 list_for_each_entry(acc_fw, &asd->acc.fw, list) {
263 if (*handle != 0 && *handle != acc_fw->handle)
264 continue;
265
266 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE)
267 continue;
268
269
270 ret = atomisp_css_load_acc_binary(asd, acc_fw->fw, nbin);
271 if (ret < 0) {
272 dev_err(isp->dev, "acc_load_binary failed\n");
273 goto err_stage;
274 }
275
276 ret = atomisp_css_set_acc_parameters(acc_fw);
277 if (ret < 0) {
278 dev_err(isp->dev, "acc_set_parameters failed\n");
279 goto err_stage;
280 }
281 nbin++;
282 }
283 if (nbin < 1) {
284
285 dev_err(isp->dev, "%s: no acc binary available\n", __func__);
286 ret = -EINVAL;
287 goto err_stage;
288 }
289
290 ret = atomisp_css_start_acc_pipe(asd);
291 if (ret) {
292 dev_err(isp->dev, "%s: atomisp_acc_start_acc_pipe failed\n",
293 __func__);
294 goto err_stage;
295 }
296
297 return 0;
298
299err_stage:
300 atomisp_css_destroy_acc_pipe(asd);
301 return ret;
302}
303
304int atomisp_acc_wait(struct atomisp_sub_device *asd, unsigned int *handle)
305{
306 struct atomisp_device *isp = asd->isp;
307 int ret;
308
309 if (!asd->acc.pipeline)
310 return -ENOENT;
311
312 if (*handle && !acc_get_fw(asd, *handle))
313 return -EINVAL;
314
315 ret = atomisp_css_wait_acc_finish(asd);
316 if (acc_stop_acceleration(asd) == -EIO) {
317 atomisp_reset(isp);
318 return -EINVAL;
319 }
320
321 return ret;
322}
323
324void atomisp_acc_done(struct atomisp_sub_device *asd, unsigned int handle)
325{
326 struct v4l2_event event = { 0 };
327
328 event.type = V4L2_EVENT_ATOMISP_ACC_COMPLETE;
329 event.u.frame_sync.frame_sequence = atomic_read(&asd->sequence);
330 event.id = handle;
331
332 v4l2_event_queue(asd->subdev.devnode, &event);
333}
334
335int atomisp_acc_map(struct atomisp_sub_device *asd, struct atomisp_acc_map *map)
336{
337 struct atomisp_map *atomisp_map;
338 ia_css_ptr cssptr;
339 int pgnr;
340
341 if (map->css_ptr)
342 return -EINVAL;
343
344 if (asd->acc.pipeline)
345 return -EBUSY;
346
347 if (map->user_ptr) {
348
349 if ((unsigned long)map->user_ptr & ~PAGE_MASK) {
350 dev_err(asd->isp->dev,
351 "%s: mapped buffer address %p is not page aligned\n",
352 __func__, map->user_ptr);
353 return -EINVAL;
354 }
355
356 pgnr = DIV_ROUND_UP(map->length, PAGE_SIZE);
357 if (pgnr < ((PAGE_ALIGN(map->length)) >> PAGE_SHIFT)) {
358 dev_err(asd->isp->dev,
359 "user space memory size is less than the expected size..\n");
360 return -ENOMEM;
361 } else if (pgnr > ((PAGE_ALIGN(map->length)) >> PAGE_SHIFT)) {
362 dev_err(asd->isp->dev,
363 "user space memory size is large than the expected size..\n");
364 return -ENOMEM;
365 }
366
367 cssptr = hmm_alloc(map->length, HMM_BO_USER, 0, map->user_ptr,
368 map->flags & ATOMISP_MAP_FLAG_CACHED);
369
370 } else {
371
372 cssptr = hmm_alloc(map->length, HMM_BO_PRIVATE, 0, NULL,
373 map->flags & ATOMISP_MAP_FLAG_CACHED);
374 }
375
376 if (!cssptr)
377 return -ENOMEM;
378
379 atomisp_map = kmalloc(sizeof(*atomisp_map), GFP_KERNEL);
380 if (!atomisp_map) {
381 hmm_free(cssptr);
382 return -ENOMEM;
383 }
384 atomisp_map->ptr = cssptr;
385 atomisp_map->length = map->length;
386 list_add(&atomisp_map->list, &asd->acc.memory_maps);
387
388 dev_dbg(asd->isp->dev, "%s: userptr %p, css_address 0x%x, size %d\n",
389 __func__, map->user_ptr, cssptr, map->length);
390 map->css_ptr = cssptr;
391 return 0;
392}
393
394int atomisp_acc_unmap(struct atomisp_sub_device *asd,
395 struct atomisp_acc_map *map)
396{
397 struct atomisp_map *atomisp_map;
398
399 if (asd->acc.pipeline)
400 return -EBUSY;
401
402 atomisp_map = acc_get_map(asd, map->css_ptr, map->length);
403 if (!atomisp_map)
404 return -EINVAL;
405
406 list_del(&atomisp_map->list);
407 hmm_free(atomisp_map->ptr);
408 kfree(atomisp_map);
409 return 0;
410}
411
412int atomisp_acc_s_mapped_arg(struct atomisp_sub_device *asd,
413 struct atomisp_acc_s_mapped_arg *arg)
414{
415 struct atomisp_acc_fw *acc_fw;
416
417 if (arg->memory >= ATOMISP_ACC_NR_MEMORY)
418 return -EINVAL;
419
420 if (asd->acc.pipeline)
421 return -EBUSY;
422
423 acc_fw = acc_get_fw(asd, arg->fw_handle);
424 if (!acc_fw)
425 return -EINVAL;
426
427 if (arg->css_ptr != 0 || arg->length != 0) {
428
429 if (!acc_get_map(asd, arg->css_ptr, arg->length))
430 return -EINVAL;
431 }
432
433 acc_fw->args[arg->memory].length = arg->length;
434 acc_fw->args[arg->memory].css_ptr = arg->css_ptr;
435
436 dev_dbg(asd->isp->dev, "%s: mem %d, address %p, size %ld\n",
437 __func__, arg->memory, (void *)arg->css_ptr,
438 (unsigned long)arg->length);
439 return 0;
440}
441
442
443
444
445
446int atomisp_acc_load_extensions(struct atomisp_sub_device *asd)
447{
448 struct atomisp_acc_fw *acc_fw;
449 bool ext_loaded = false;
450 bool continuous = asd->continuous_mode->val &&
451 asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW;
452 int ret = 0, i = -1;
453 struct atomisp_device *isp = asd->isp;
454
455 if (asd->acc.pipeline || asd->acc.extension_mode)
456 return -EBUSY;
457
458
459 wbinvd();
460
461 list_for_each_entry(acc_fw, &asd->acc.fw, list) {
462 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
463 acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
464 continue;
465
466 for (i = 0; i < ARRAY_SIZE(acc_flag_to_pipe); i++) {
467
468
469
470
471
472 if (!continuous &&
473 acc_flag_to_pipe[i].flag ==
474 ATOMISP_ACC_FW_LOAD_FL_ACC)
475 continue;
476
477 if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
478 ret = atomisp_css_load_acc_extension(asd,
479 acc_fw->fw,
480 acc_flag_to_pipe[i].pipe_id,
481 acc_fw->type);
482 if (ret)
483 goto error;
484
485 ext_loaded = true;
486 }
487 }
488
489 ret = atomisp_css_set_acc_parameters(acc_fw);
490 if (ret < 0)
491 goto error;
492 }
493
494 if (!ext_loaded)
495 return ret;
496
497 ret = atomisp_css_update_stream(asd);
498 if (ret) {
499 dev_err(isp->dev, "%s: update stream failed.\n", __func__);
500 goto error;
501 }
502
503 asd->acc.extension_mode = true;
504 return 0;
505
506error:
507 while (--i >= 0) {
508 if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
509 atomisp_css_unload_acc_extension(asd, acc_fw->fw,
510 acc_flag_to_pipe[i].pipe_id);
511 }
512 }
513
514 list_for_each_entry_continue_reverse(acc_fw, &asd->acc.fw, list) {
515 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
516 acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
517 continue;
518
519 for (i = ARRAY_SIZE(acc_flag_to_pipe) - 1; i >= 0; i--) {
520 if (!continuous &&
521 acc_flag_to_pipe[i].flag ==
522 ATOMISP_ACC_FW_LOAD_FL_ACC)
523 continue;
524 if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
525 atomisp_css_unload_acc_extension(asd,
526 acc_fw->fw,
527 acc_flag_to_pipe[i].pipe_id);
528 }
529 }
530 }
531 return ret;
532}
533
534void atomisp_acc_unload_extensions(struct atomisp_sub_device *asd)
535{
536 struct atomisp_acc_fw *acc_fw;
537 int i;
538
539 if (!asd->acc.extension_mode)
540 return;
541
542 list_for_each_entry_reverse(acc_fw, &asd->acc.fw, list) {
543 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
544 acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
545 continue;
546
547 for (i = ARRAY_SIZE(acc_flag_to_pipe) - 1; i >= 0; i--) {
548 if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
549 atomisp_css_unload_acc_extension(asd,
550 acc_fw->fw,
551 acc_flag_to_pipe[i].pipe_id);
552 }
553 }
554 }
555
556 asd->acc.extension_mode = false;
557}
558
559int atomisp_acc_set_state(struct atomisp_sub_device *asd,
560 struct atomisp_acc_state *arg)
561{
562 struct atomisp_acc_fw *acc_fw;
563 bool enable = (arg->flags & ATOMISP_STATE_FLAG_ENABLE) != 0;
564 struct ia_css_pipe *pipe;
565 int r;
566 int i;
567
568 if (!asd->acc.extension_mode)
569 return -EBUSY;
570
571 if (arg->flags & ~ATOMISP_STATE_FLAG_ENABLE)
572 return -EINVAL;
573
574 acc_fw = acc_get_fw(asd, arg->fw_handle);
575 if (!acc_fw)
576 return -EINVAL;
577
578 if (enable)
579 wbinvd();
580
581 for (i = 0; i < ARRAY_SIZE(acc_flag_to_pipe); i++) {
582 if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
583 pipe = asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].
584 pipes[acc_flag_to_pipe[i].pipe_id];
585 r = ia_css_pipe_set_qos_ext_state(pipe, acc_fw->handle,
586 enable);
587 if (r)
588 return -EBADRQC;
589 }
590 }
591
592 if (enable)
593 acc_fw->flags |= ATOMISP_ACC_FW_LOAD_FL_ENABLE;
594 else
595 acc_fw->flags &= ~ATOMISP_ACC_FW_LOAD_FL_ENABLE;
596
597 return 0;
598}
599
600int atomisp_acc_get_state(struct atomisp_sub_device *asd,
601 struct atomisp_acc_state *arg)
602{
603 struct atomisp_acc_fw *acc_fw;
604
605 if (!asd->acc.extension_mode)
606 return -EBUSY;
607
608 acc_fw = acc_get_fw(asd, arg->fw_handle);
609 if (!acc_fw)
610 return -EINVAL;
611
612 arg->flags = acc_fw->flags;
613
614 return 0;
615}
616