GNU Linux-libre 4.14.290-gnu1
[releases.git] / drivers / staging / media / atomisp / pci / atomisp2 / atomisp_acc.c
1 /*
2  * Support for Clovertrail PNW Camera Imaging ISP subsystem.
3  *
4  * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version
8  * 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  *
20  */
21
22 /*
23  * This file implements loadable acceleration firmware API,
24  * including ioctls to map and unmap acceleration parameters and buffers.
25  */
26
27 #include <linux/init.h>
28 #include <media/v4l2-event.h>
29
30 #include "atomisp_acc.h"
31 #include "atomisp_internal.h"
32 #include "atomisp_compat.h"
33 #include "atomisp_cmd.h"
34
35 #include "hrt/hive_isp_css_mm_hrt.h"
36 #include "memory_access/memory_access.h"
37 #include "ia_css.h"
38
39 static const struct {
40         unsigned int flag;
41         enum atomisp_css_pipe_id pipe_id;
42 } acc_flag_to_pipe[] = {
43         { ATOMISP_ACC_FW_LOAD_FL_PREVIEW, CSS_PIPE_ID_PREVIEW },
44         { ATOMISP_ACC_FW_LOAD_FL_COPY, CSS_PIPE_ID_COPY },
45         { ATOMISP_ACC_FW_LOAD_FL_VIDEO, CSS_PIPE_ID_VIDEO },
46         { ATOMISP_ACC_FW_LOAD_FL_CAPTURE, CSS_PIPE_ID_CAPTURE },
47         { ATOMISP_ACC_FW_LOAD_FL_ACC, CSS_PIPE_ID_ACC }
48 };
49
50 /*
51  * Allocate struct atomisp_acc_fw along with space for firmware.
52  * The returned struct atomisp_acc_fw is cleared (firmware region is not).
53  */
54 static struct atomisp_acc_fw *acc_alloc_fw(unsigned int fw_size)
55 {
56         struct atomisp_acc_fw *acc_fw;
57
58         acc_fw = kzalloc(sizeof(*acc_fw), GFP_KERNEL);
59         if (!acc_fw)
60                 return NULL;
61
62         acc_fw->fw = vmalloc(fw_size);
63         if (!acc_fw->fw) {
64                 kfree(acc_fw);
65                 return NULL;
66         }
67
68         return acc_fw;
69 }
70
71 static void acc_free_fw(struct atomisp_acc_fw *acc_fw)
72 {
73         vfree(acc_fw->fw);
74         kfree(acc_fw);
75 }
76
77 static struct atomisp_acc_fw *
78 acc_get_fw(struct atomisp_sub_device *asd, unsigned int handle)
79 {
80         struct atomisp_acc_fw *acc_fw;
81
82         list_for_each_entry(acc_fw, &asd->acc.fw, list)
83                 if (acc_fw->handle == handle)
84                         return acc_fw;
85
86         return NULL;
87 }
88
89 static struct atomisp_map *acc_get_map(struct atomisp_sub_device *asd,
90                                        unsigned long css_ptr, size_t length)
91 {
92         struct atomisp_map *atomisp_map;
93
94         list_for_each_entry(atomisp_map, &asd->acc.memory_maps, list) {
95                 if (atomisp_map->ptr == css_ptr &&
96                     atomisp_map->length == length)
97                         return atomisp_map;
98         }
99         return NULL;
100 }
101
102 static int acc_stop_acceleration(struct atomisp_sub_device *asd)
103 {
104         int ret;
105
106         ret = atomisp_css_stop_acc_pipe(asd);
107         atomisp_css_destroy_acc_pipe(asd);
108
109         return ret;
110 }
111
112 void atomisp_acc_cleanup(struct atomisp_device *isp)
113 {
114         int i;
115
116         for (i = 0; i < isp->num_of_streams; i++)
117                 ida_destroy(&isp->asd[i].acc.ida);
118 }
119
120 void atomisp_acc_release(struct atomisp_sub_device *asd)
121 {
122         struct atomisp_acc_fw *acc_fw, *ta;
123         struct atomisp_map *atomisp_map, *tm;
124
125         /* Stop acceleration if already running */
126         if (asd->acc.pipeline)
127                 acc_stop_acceleration(asd);
128
129         /* Unload all loaded acceleration binaries */
130         list_for_each_entry_safe(acc_fw, ta, &asd->acc.fw, list) {
131                 list_del(&acc_fw->list);
132                 ida_remove(&asd->acc.ida, acc_fw->handle);
133                 acc_free_fw(acc_fw);
134         }
135
136         /* Free all mapped memory blocks */
137         list_for_each_entry_safe(atomisp_map, tm, &asd->acc.memory_maps, list) {
138                 list_del(&atomisp_map->list);
139                 hmm_free(atomisp_map->ptr);
140                 kfree(atomisp_map);
141         }
142 }
143
144 int atomisp_acc_load_to_pipe(struct atomisp_sub_device *asd,
145                              struct atomisp_acc_fw_load_to_pipe *user_fw)
146 {
147         static const unsigned int pipeline_flags =
148                 ATOMISP_ACC_FW_LOAD_FL_PREVIEW | ATOMISP_ACC_FW_LOAD_FL_COPY |
149                 ATOMISP_ACC_FW_LOAD_FL_VIDEO |
150                 ATOMISP_ACC_FW_LOAD_FL_CAPTURE | ATOMISP_ACC_FW_LOAD_FL_ACC;
151
152         struct atomisp_acc_fw *acc_fw;
153         int handle;
154
155         if (!user_fw->data || user_fw->size < sizeof(*acc_fw->fw))
156                 return -EINVAL;
157
158         /* Binary has to be enabled at least for one pipeline */
159         if (!(user_fw->flags & pipeline_flags))
160                 return -EINVAL;
161
162         /* We do not support other flags yet */
163         if (user_fw->flags & ~pipeline_flags)
164                 return -EINVAL;
165
166         if (user_fw->type < ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT ||
167             user_fw->type > ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE)
168                 return -EINVAL;
169
170         if (asd->acc.pipeline || asd->acc.extension_mode)
171                 return -EBUSY;
172
173         acc_fw = acc_alloc_fw(user_fw->size);
174         if (!acc_fw)
175                 return -ENOMEM;
176
177         if (copy_from_user(acc_fw->fw, user_fw->data, user_fw->size)) {
178                 acc_free_fw(acc_fw);
179                 return -EFAULT;
180         }
181
182         if (!ida_pre_get(&asd->acc.ida, GFP_KERNEL) ||
183             ida_get_new_above(&asd->acc.ida, 1, &handle)) {
184                 acc_free_fw(acc_fw);
185                 return -ENOSPC;
186         }
187
188         user_fw->fw_handle = handle;
189         acc_fw->handle = handle;
190         acc_fw->flags = user_fw->flags;
191         acc_fw->type = user_fw->type;
192         acc_fw->fw->handle = handle;
193
194         /*
195          * correct isp firmware type in order ISP firmware can be appended
196          * to correct pipe properly
197          */
198         if (acc_fw->fw->type == ia_css_isp_firmware) {
199                 static const int type_to_css[] = {
200                         [ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT] =
201                                 IA_CSS_ACC_OUTPUT,
202                         [ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER] =
203                                 IA_CSS_ACC_VIEWFINDER,
204                         [ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE] =
205                                 IA_CSS_ACC_STANDALONE,
206                 };
207                 acc_fw->fw->info.isp.type = type_to_css[acc_fw->type];
208         }
209
210         list_add_tail(&acc_fw->list, &asd->acc.fw);
211         return 0;
212 }
213
214 int atomisp_acc_load(struct atomisp_sub_device *asd,
215                      struct atomisp_acc_fw_load *user_fw)
216 {
217         struct atomisp_acc_fw_load_to_pipe ltp = {0};
218         int r;
219
220         ltp.flags = ATOMISP_ACC_FW_LOAD_FL_ACC;
221         ltp.type = ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE;
222         ltp.size = user_fw->size;
223         ltp.data = user_fw->data;
224         r = atomisp_acc_load_to_pipe(asd, &ltp);
225         user_fw->fw_handle = ltp.fw_handle;
226         return r;
227 }
228
229 int atomisp_acc_unload(struct atomisp_sub_device *asd, unsigned int *handle)
230 {
231         struct atomisp_acc_fw *acc_fw;
232
233         if (asd->acc.pipeline || asd->acc.extension_mode)
234                 return -EBUSY;
235
236         acc_fw = acc_get_fw(asd, *handle);
237         if (!acc_fw)
238                 return -EINVAL;
239
240         list_del(&acc_fw->list);
241         ida_remove(&asd->acc.ida, acc_fw->handle);
242         acc_free_fw(acc_fw);
243
244         return 0;
245 }
246
247 int atomisp_acc_start(struct atomisp_sub_device *asd, unsigned int *handle)
248 {
249         struct atomisp_device *isp = asd->isp;
250         struct atomisp_acc_fw *acc_fw;
251         int ret;
252         unsigned int nbin;
253
254         if (asd->acc.pipeline || asd->acc.extension_mode)
255                 return -EBUSY;
256
257         /* Invalidate caches. FIXME: should flush only necessary buffers */
258         wbinvd();
259
260         ret = atomisp_css_create_acc_pipe(asd);
261         if (ret)
262                 return ret;
263
264         nbin = 0;
265         list_for_each_entry(acc_fw, &asd->acc.fw, list) {
266                 if (*handle != 0 && *handle != acc_fw->handle)
267                         continue;
268
269                 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_STANDALONE)
270                         continue;
271
272                 /* Add the binary into the pipeline */
273                 ret = atomisp_css_load_acc_binary(asd, acc_fw->fw, nbin);
274                 if (ret < 0) {
275                         dev_err(isp->dev, "acc_load_binary failed\n");
276                         goto err_stage;
277                 }
278
279                 ret = atomisp_css_set_acc_parameters(acc_fw);
280                 if (ret < 0) {
281                         dev_err(isp->dev, "acc_set_parameters failed\n");
282                         goto err_stage;
283                 }
284                 nbin++;
285         }
286         if (nbin < 1) {
287                 /* Refuse creating pipelines with no binaries */
288                 dev_err(isp->dev, "%s: no acc binary available\n", __func__);
289                 ret = -EINVAL;
290                 goto err_stage;
291         }
292
293         ret = atomisp_css_start_acc_pipe(asd);
294         if (ret) {
295                 dev_err(isp->dev, "%s: atomisp_acc_start_acc_pipe failed\n",
296                         __func__);
297                 goto err_stage;
298         }
299
300         return 0;
301
302 err_stage:
303         atomisp_css_destroy_acc_pipe(asd);
304         return ret;
305 }
306
307 int atomisp_acc_wait(struct atomisp_sub_device *asd, unsigned int *handle)
308 {
309         struct atomisp_device *isp = asd->isp;
310         int ret;
311
312         if (!asd->acc.pipeline)
313                 return -ENOENT;
314
315         if (*handle && !acc_get_fw(asd, *handle))
316                 return -EINVAL;
317
318         ret = atomisp_css_wait_acc_finish(asd);
319         if (acc_stop_acceleration(asd) == -EIO) {
320                 atomisp_reset(isp);
321                 return -EINVAL;
322         }
323
324         return ret;
325 }
326
327 void atomisp_acc_done(struct atomisp_sub_device *asd, unsigned int handle)
328 {
329         struct v4l2_event event = { 0 };
330
331         event.type = V4L2_EVENT_ATOMISP_ACC_COMPLETE;
332         event.u.frame_sync.frame_sequence = atomic_read(&asd->sequence);
333         event.id = handle;
334
335         v4l2_event_queue(asd->subdev.devnode, &event);
336 }
337
338 int atomisp_acc_map(struct atomisp_sub_device *asd, struct atomisp_acc_map *map)
339 {
340         struct atomisp_map *atomisp_map;
341         ia_css_ptr cssptr;
342         int pgnr;
343
344         if (map->css_ptr)
345                 return -EINVAL;
346
347         if (asd->acc.pipeline)
348                 return -EBUSY;
349
350         if (map->user_ptr) {
351                 /* Buffer to map must be page-aligned */
352                 if ((unsigned long)map->user_ptr & ~PAGE_MASK) {
353                         dev_err(asd->isp->dev,
354                                 "%s: mapped buffer address %p is not page aligned\n",
355                                 __func__, map->user_ptr);
356                         return -EINVAL;
357                 }
358
359                 pgnr = DIV_ROUND_UP(map->length, PAGE_SIZE);
360                 cssptr = hrt_isp_css_mm_alloc_user_ptr(
361                                 map->length, map->user_ptr,
362                                 pgnr, HRT_USR_PTR,
363                                 (map->flags & ATOMISP_MAP_FLAG_CACHED));
364         } else {
365                 /* Allocate private buffer. */
366                 if (map->flags & ATOMISP_MAP_FLAG_CACHED)
367                         cssptr = hrt_isp_css_mm_calloc_cached(map->length);
368                 else
369                         cssptr = hrt_isp_css_mm_calloc(map->length);
370         }
371
372         if (!cssptr)
373                 return -ENOMEM;
374
375         atomisp_map = kmalloc(sizeof(*atomisp_map), GFP_KERNEL);
376         if (!atomisp_map) {
377                 hmm_free(cssptr);
378                 return -ENOMEM;
379         }
380         atomisp_map->ptr = cssptr;
381         atomisp_map->length = map->length;
382         list_add(&atomisp_map->list, &asd->acc.memory_maps);
383
384         dev_dbg(asd->isp->dev, "%s: userptr %p, css_address 0x%x, size %d\n",
385                 __func__, map->user_ptr, cssptr, map->length);
386         map->css_ptr = cssptr;
387         return 0;
388 }
389
390 int atomisp_acc_unmap(struct atomisp_sub_device *asd, struct atomisp_acc_map *map)
391 {
392         struct atomisp_map *atomisp_map;
393
394         if (asd->acc.pipeline)
395                 return -EBUSY;
396
397         atomisp_map = acc_get_map(asd, map->css_ptr, map->length);
398         if (!atomisp_map)
399                 return -EINVAL;
400
401         list_del(&atomisp_map->list);
402         hmm_free(atomisp_map->ptr);
403         kfree(atomisp_map);
404         return 0;
405 }
406
407 int atomisp_acc_s_mapped_arg(struct atomisp_sub_device *asd,
408                              struct atomisp_acc_s_mapped_arg *arg)
409 {
410         struct atomisp_acc_fw *acc_fw;
411
412         if (arg->memory >= ATOMISP_ACC_NR_MEMORY)
413                 return -EINVAL;
414
415         if (asd->acc.pipeline)
416                 return -EBUSY;
417
418         acc_fw = acc_get_fw(asd, arg->fw_handle);
419         if (!acc_fw)
420                 return -EINVAL;
421
422         if (arg->css_ptr != 0 || arg->length != 0) {
423                 /* Unless the parameter is cleared, check that it exists */
424                 if (!acc_get_map(asd, arg->css_ptr, arg->length))
425                         return -EINVAL;
426         }
427
428         acc_fw->args[arg->memory].length = arg->length;
429         acc_fw->args[arg->memory].css_ptr = arg->css_ptr;
430
431         dev_dbg(asd->isp->dev, "%s: mem %d, address %p, size %ld\n",
432                 __func__, arg->memory, (void *)arg->css_ptr,
433                 (unsigned long)arg->length);
434         return 0;
435 }
436
437 /*
438  * Appends the loaded acceleration binary extensions to the
439  * current ISP mode. Must be called just before sh_css_start().
440  */
441 int atomisp_acc_load_extensions(struct atomisp_sub_device *asd)
442 {
443         struct atomisp_acc_fw *acc_fw;
444         bool ext_loaded = false;
445         bool continuous = asd->continuous_mode->val &&
446                           asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW;
447         int ret = 0, i = -1;
448         struct atomisp_device *isp = asd->isp;
449
450         if (asd->acc.pipeline || asd->acc.extension_mode)
451                 return -EBUSY;
452
453         /* Invalidate caches. FIXME: should flush only necessary buffers */
454         wbinvd();
455
456         list_for_each_entry(acc_fw, &asd->acc.fw, list) {
457                 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
458                     acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
459                         continue;
460
461                 for (i = 0; i < ARRAY_SIZE(acc_flag_to_pipe); i++) {
462                         /* QoS (ACC pipe) acceleration stages are currently
463                          * allowed only in continuous mode. Skip them for
464                          * all other modes. */
465                         if (!continuous &&
466                             acc_flag_to_pipe[i].flag ==
467                             ATOMISP_ACC_FW_LOAD_FL_ACC)
468                                 continue;
469
470                         if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
471                                 ret = atomisp_css_load_acc_extension(asd,
472                                         acc_fw->fw,
473                                         acc_flag_to_pipe[i].pipe_id,
474                                         acc_fw->type);
475                                 if (ret)
476                                         goto error;
477
478                                 ext_loaded = true;
479                         }
480                 }
481
482                 ret = atomisp_css_set_acc_parameters(acc_fw);
483                 if (ret < 0)
484                         goto error;
485         }
486
487         if (!ext_loaded)
488                 return ret;
489
490         ret = atomisp_css_update_stream(asd);
491         if (ret) {
492                 dev_err(isp->dev, "%s: update stream failed.\n", __func__);
493                 goto error;
494         }
495
496         asd->acc.extension_mode = true;
497         return 0;
498
499 error:
500         while (--i >= 0) {
501                 if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
502                         atomisp_css_unload_acc_extension(asd, acc_fw->fw,
503                                         acc_flag_to_pipe[i].pipe_id);
504                 }
505         }
506
507         list_for_each_entry_continue_reverse(acc_fw, &asd->acc.fw, list) {
508                 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
509                     acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
510                         continue;
511
512                 for (i = ARRAY_SIZE(acc_flag_to_pipe) - 1; i >= 0; i--) {
513                         if (!continuous &&
514                             acc_flag_to_pipe[i].flag ==
515                             ATOMISP_ACC_FW_LOAD_FL_ACC)
516                                 continue;
517                         if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
518                                 atomisp_css_unload_acc_extension(asd,
519                                         acc_fw->fw,
520                                         acc_flag_to_pipe[i].pipe_id);
521                         }
522                 }
523         }
524         return ret;
525 }
526
527 void atomisp_acc_unload_extensions(struct atomisp_sub_device *asd)
528 {
529         struct atomisp_acc_fw *acc_fw;
530         int i;
531
532         if (!asd->acc.extension_mode)
533                 return;
534
535         list_for_each_entry_reverse(acc_fw, &asd->acc.fw, list) {
536                 if (acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_OUTPUT &&
537                     acc_fw->type != ATOMISP_ACC_FW_LOAD_TYPE_VIEWFINDER)
538                         continue;
539
540                 for (i = ARRAY_SIZE(acc_flag_to_pipe) - 1; i >= 0; i--) {
541                         if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
542                                 atomisp_css_unload_acc_extension(asd,
543                                         acc_fw->fw,
544                                         acc_flag_to_pipe[i].pipe_id);
545                         }
546                 }
547         }
548
549         asd->acc.extension_mode = false;
550 }
551
552 int atomisp_acc_set_state(struct atomisp_sub_device *asd,
553                           struct atomisp_acc_state *arg)
554 {
555         struct atomisp_acc_fw *acc_fw;
556         bool enable = (arg->flags & ATOMISP_STATE_FLAG_ENABLE) != 0;
557         struct ia_css_pipe *pipe;
558         enum ia_css_err r;
559         int i;
560
561         if (!asd->acc.extension_mode)
562                 return -EBUSY;
563
564         if (arg->flags & ~ATOMISP_STATE_FLAG_ENABLE)
565                 return -EINVAL;
566
567         acc_fw = acc_get_fw(asd, arg->fw_handle);
568         if (!acc_fw)
569                 return -EINVAL;
570
571         if (enable)
572                 wbinvd();
573
574         for (i = 0; i < ARRAY_SIZE(acc_flag_to_pipe); i++) {
575                 if (acc_fw->flags & acc_flag_to_pipe[i].flag) {
576                         pipe = asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].
577                                 pipes[acc_flag_to_pipe[i].pipe_id];
578                         r = ia_css_pipe_set_qos_ext_state(pipe, acc_fw->handle,
579                                                           enable);
580                         if (r != IA_CSS_SUCCESS)
581                                 return -EBADRQC;
582                 }
583         }
584
585         if (enable)
586                 acc_fw->flags |= ATOMISP_ACC_FW_LOAD_FL_ENABLE;
587         else
588                 acc_fw->flags &= ~ATOMISP_ACC_FW_LOAD_FL_ENABLE;
589
590         return 0;
591 }
592
593 int atomisp_acc_get_state(struct atomisp_sub_device *asd,
594                           struct atomisp_acc_state *arg)
595 {
596         struct atomisp_acc_fw *acc_fw;
597
598         if (!asd->acc.extension_mode)
599                 return -EBUSY;
600
601         acc_fw = acc_get_fw(asd, arg->fw_handle);
602         if (!acc_fw)
603                 return -EINVAL;
604
605         arg->flags = acc_fw->flags;
606
607         return 0;
608 }