Merge tag 'pinctrl-v4.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[sfrench/cifs-2.6.git] / drivers / media / platform / qcom / venus / hfi.c
1 /*
2  * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
3  * Copyright (C) 2017 Linaro Ltd.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 and
7  * only version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  */
15 #include <linux/slab.h>
16 #include <linux/mutex.h>
17 #include <linux/list.h>
18 #include <linux/completion.h>
19 #include <linux/platform_device.h>
20 #include <linux/videodev2.h>
21
22 #include "core.h"
23 #include "hfi.h"
24 #include "hfi_cmds.h"
25 #include "hfi_venus.h"
26
27 #define TIMEOUT         msecs_to_jiffies(1000)
28
29 static u32 to_codec_type(u32 pixfmt)
30 {
31         switch (pixfmt) {
32         case V4L2_PIX_FMT_H264:
33         case V4L2_PIX_FMT_H264_NO_SC:
34                 return HFI_VIDEO_CODEC_H264;
35         case V4L2_PIX_FMT_H263:
36                 return HFI_VIDEO_CODEC_H263;
37         case V4L2_PIX_FMT_MPEG1:
38                 return HFI_VIDEO_CODEC_MPEG1;
39         case V4L2_PIX_FMT_MPEG2:
40                 return HFI_VIDEO_CODEC_MPEG2;
41         case V4L2_PIX_FMT_MPEG4:
42                 return HFI_VIDEO_CODEC_MPEG4;
43         case V4L2_PIX_FMT_VC1_ANNEX_G:
44         case V4L2_PIX_FMT_VC1_ANNEX_L:
45                 return HFI_VIDEO_CODEC_VC1;
46         case V4L2_PIX_FMT_VP8:
47                 return HFI_VIDEO_CODEC_VP8;
48         case V4L2_PIX_FMT_VP9:
49                 return HFI_VIDEO_CODEC_VP9;
50         case V4L2_PIX_FMT_XVID:
51                 return HFI_VIDEO_CODEC_DIVX;
52         default:
53                 return 0;
54         }
55 }
56
57 int hfi_core_init(struct venus_core *core)
58 {
59         int ret = 0;
60
61         mutex_lock(&core->lock);
62
63         if (core->state >= CORE_INIT)
64                 goto unlock;
65
66         reinit_completion(&core->done);
67
68         ret = core->ops->core_init(core);
69         if (ret)
70                 goto unlock;
71
72         ret = wait_for_completion_timeout(&core->done, TIMEOUT);
73         if (!ret) {
74                 ret = -ETIMEDOUT;
75                 goto unlock;
76         }
77
78         ret = 0;
79
80         if (core->error != HFI_ERR_NONE) {
81                 ret = -EIO;
82                 goto unlock;
83         }
84
85         core->state = CORE_INIT;
86 unlock:
87         mutex_unlock(&core->lock);
88         return ret;
89 }
90
91 static int core_deinit_wait_atomic_t(atomic_t *p)
92 {
93         schedule();
94         return 0;
95 }
96
97 int hfi_core_deinit(struct venus_core *core, bool blocking)
98 {
99         int ret = 0, empty;
100
101         mutex_lock(&core->lock);
102
103         if (core->state == CORE_UNINIT)
104                 goto unlock;
105
106         empty = list_empty(&core->instances);
107
108         if (!empty && !blocking) {
109                 ret = -EBUSY;
110                 goto unlock;
111         }
112
113         if (!empty) {
114                 mutex_unlock(&core->lock);
115                 wait_on_atomic_t(&core->insts_count, core_deinit_wait_atomic_t,
116                                  TASK_UNINTERRUPTIBLE);
117                 mutex_lock(&core->lock);
118         }
119
120         ret = core->ops->core_deinit(core);
121
122         if (!ret)
123                 core->state = CORE_UNINIT;
124
125 unlock:
126         mutex_unlock(&core->lock);
127         return ret;
128 }
129
130 int hfi_core_suspend(struct venus_core *core)
131 {
132         if (core->state != CORE_INIT)
133                 return 0;
134
135         return core->ops->suspend(core);
136 }
137
138 int hfi_core_resume(struct venus_core *core, bool force)
139 {
140         if (!force && core->state != CORE_INIT)
141                 return 0;
142
143         return core->ops->resume(core);
144 }
145
146 int hfi_core_trigger_ssr(struct venus_core *core, u32 type)
147 {
148         return core->ops->core_trigger_ssr(core, type);
149 }
150
151 int hfi_core_ping(struct venus_core *core)
152 {
153         int ret;
154
155         mutex_lock(&core->lock);
156
157         ret = core->ops->core_ping(core, 0xbeef);
158         if (ret)
159                 goto unlock;
160
161         ret = wait_for_completion_timeout(&core->done, TIMEOUT);
162         if (!ret) {
163                 ret = -ETIMEDOUT;
164                 goto unlock;
165         }
166         ret = 0;
167         if (core->error != HFI_ERR_NONE)
168                 ret = -ENODEV;
169 unlock:
170         mutex_unlock(&core->lock);
171         return ret;
172 }
173
174 static int wait_session_msg(struct venus_inst *inst)
175 {
176         int ret;
177
178         ret = wait_for_completion_timeout(&inst->done, TIMEOUT);
179         if (!ret)
180                 return -ETIMEDOUT;
181
182         if (inst->error != HFI_ERR_NONE)
183                 return -EIO;
184
185         return 0;
186 }
187
188 int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops)
189 {
190         struct venus_core *core = inst->core;
191
192         if (!ops)
193                 return -EINVAL;
194
195         inst->state = INST_UNINIT;
196         init_completion(&inst->done);
197         inst->ops = ops;
198
199         mutex_lock(&core->lock);
200         list_add_tail(&inst->list, &core->instances);
201         atomic_inc(&core->insts_count);
202         mutex_unlock(&core->lock);
203
204         return 0;
205 }
206 EXPORT_SYMBOL_GPL(hfi_session_create);
207
208 int hfi_session_init(struct venus_inst *inst, u32 pixfmt)
209 {
210         struct venus_core *core = inst->core;
211         const struct hfi_ops *ops = core->ops;
212         u32 codec;
213         int ret;
214
215         codec = to_codec_type(pixfmt);
216         reinit_completion(&inst->done);
217
218         ret = ops->session_init(inst, inst->session_type, codec);
219         if (ret)
220                 return ret;
221
222         ret = wait_session_msg(inst);
223         if (ret)
224                 return ret;
225
226         inst->state = INST_INIT;
227
228         return 0;
229 }
230 EXPORT_SYMBOL_GPL(hfi_session_init);
231
232 void hfi_session_destroy(struct venus_inst *inst)
233 {
234         struct venus_core *core = inst->core;
235
236         mutex_lock(&core->lock);
237         list_del_init(&inst->list);
238         atomic_dec(&core->insts_count);
239         wake_up_atomic_t(&core->insts_count);
240         mutex_unlock(&core->lock);
241 }
242 EXPORT_SYMBOL_GPL(hfi_session_destroy);
243
244 int hfi_session_deinit(struct venus_inst *inst)
245 {
246         const struct hfi_ops *ops = inst->core->ops;
247         int ret;
248
249         if (inst->state == INST_UNINIT)
250                 return 0;
251
252         if (inst->state < INST_INIT)
253                 return -EINVAL;
254
255         reinit_completion(&inst->done);
256
257         ret = ops->session_end(inst);
258         if (ret)
259                 return ret;
260
261         ret = wait_session_msg(inst);
262         if (ret)
263                 return ret;
264
265         inst->state = INST_UNINIT;
266
267         return 0;
268 }
269 EXPORT_SYMBOL_GPL(hfi_session_deinit);
270
271 int hfi_session_start(struct venus_inst *inst)
272 {
273         const struct hfi_ops *ops = inst->core->ops;
274         int ret;
275
276         if (inst->state != INST_LOAD_RESOURCES)
277                 return -EINVAL;
278
279         reinit_completion(&inst->done);
280
281         ret = ops->session_start(inst);
282         if (ret)
283                 return ret;
284
285         ret = wait_session_msg(inst);
286         if (ret)
287                 return ret;
288
289         inst->state = INST_START;
290
291         return 0;
292 }
293
294 int hfi_session_stop(struct venus_inst *inst)
295 {
296         const struct hfi_ops *ops = inst->core->ops;
297         int ret;
298
299         if (inst->state != INST_START)
300                 return -EINVAL;
301
302         reinit_completion(&inst->done);
303
304         ret = ops->session_stop(inst);
305         if (ret)
306                 return ret;
307
308         ret = wait_session_msg(inst);
309         if (ret)
310                 return ret;
311
312         inst->state = INST_STOP;
313
314         return 0;
315 }
316
317 int hfi_session_continue(struct venus_inst *inst)
318 {
319         struct venus_core *core = inst->core;
320
321         if (core->res->hfi_version != HFI_VERSION_3XX)
322                 return 0;
323
324         return core->ops->session_continue(inst);
325 }
326 EXPORT_SYMBOL_GPL(hfi_session_continue);
327
328 int hfi_session_abort(struct venus_inst *inst)
329 {
330         const struct hfi_ops *ops = inst->core->ops;
331         int ret;
332
333         reinit_completion(&inst->done);
334
335         ret = ops->session_abort(inst);
336         if (ret)
337                 return ret;
338
339         ret = wait_session_msg(inst);
340         if (ret)
341                 return ret;
342
343         return 0;
344 }
345
346 int hfi_session_load_res(struct venus_inst *inst)
347 {
348         const struct hfi_ops *ops = inst->core->ops;
349         int ret;
350
351         if (inst->state != INST_INIT)
352                 return -EINVAL;
353
354         reinit_completion(&inst->done);
355
356         ret = ops->session_load_res(inst);
357         if (ret)
358                 return ret;
359
360         ret = wait_session_msg(inst);
361         if (ret)
362                 return ret;
363
364         inst->state = INST_LOAD_RESOURCES;
365
366         return 0;
367 }
368
369 int hfi_session_unload_res(struct venus_inst *inst)
370 {
371         const struct hfi_ops *ops = inst->core->ops;
372         int ret;
373
374         if (inst->state != INST_STOP)
375                 return -EINVAL;
376
377         reinit_completion(&inst->done);
378
379         ret = ops->session_release_res(inst);
380         if (ret)
381                 return ret;
382
383         ret = wait_session_msg(inst);
384         if (ret)
385                 return ret;
386
387         inst->state = INST_RELEASE_RESOURCES;
388
389         return 0;
390 }
391
392 int hfi_session_flush(struct venus_inst *inst)
393 {
394         const struct hfi_ops *ops = inst->core->ops;
395         int ret;
396
397         reinit_completion(&inst->done);
398
399         ret = ops->session_flush(inst, HFI_FLUSH_ALL);
400         if (ret)
401                 return ret;
402
403         ret = wait_session_msg(inst);
404         if (ret)
405                 return ret;
406
407         return 0;
408 }
409 EXPORT_SYMBOL_GPL(hfi_session_flush);
410
411 int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd)
412 {
413         const struct hfi_ops *ops = inst->core->ops;
414
415         return ops->session_set_buffers(inst, bd);
416 }
417
418 int hfi_session_unset_buffers(struct venus_inst *inst,
419                               struct hfi_buffer_desc *bd)
420 {
421         const struct hfi_ops *ops = inst->core->ops;
422         int ret;
423
424         reinit_completion(&inst->done);
425
426         ret = ops->session_unset_buffers(inst, bd);
427         if (ret)
428                 return ret;
429
430         if (!bd->response_required)
431                 return 0;
432
433         ret = wait_session_msg(inst);
434         if (ret)
435                 return ret;
436
437         return 0;
438 }
439
440 int hfi_session_get_property(struct venus_inst *inst, u32 ptype,
441                              union hfi_get_property *hprop)
442 {
443         const struct hfi_ops *ops = inst->core->ops;
444         int ret;
445
446         if (inst->state < INST_INIT || inst->state >= INST_STOP)
447                 return -EINVAL;
448
449         reinit_completion(&inst->done);
450
451         ret = ops->session_get_property(inst, ptype);
452         if (ret)
453                 return ret;
454
455         ret = wait_session_msg(inst);
456         if (ret)
457                 return ret;
458
459         *hprop = inst->hprop;
460
461         return 0;
462 }
463 EXPORT_SYMBOL_GPL(hfi_session_get_property);
464
465 int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata)
466 {
467         const struct hfi_ops *ops = inst->core->ops;
468
469         if (inst->state < INST_INIT || inst->state >= INST_STOP)
470                 return -EINVAL;
471
472         return ops->session_set_property(inst, ptype, pdata);
473 }
474 EXPORT_SYMBOL_GPL(hfi_session_set_property);
475
476 int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd)
477 {
478         const struct hfi_ops *ops = inst->core->ops;
479
480         if (fd->buffer_type == HFI_BUFFER_INPUT)
481                 return ops->session_etb(inst, fd);
482         else if (fd->buffer_type == HFI_BUFFER_OUTPUT)
483                 return ops->session_ftb(inst, fd);
484
485         return -EINVAL;
486 }
487 EXPORT_SYMBOL_GPL(hfi_session_process_buf);
488
489 irqreturn_t hfi_isr_thread(int irq, void *dev_id)
490 {
491         struct venus_core *core = dev_id;
492
493         return core->ops->isr_thread(core);
494 }
495
496 irqreturn_t hfi_isr(int irq, void *dev)
497 {
498         struct venus_core *core = dev;
499
500         return core->ops->isr(core);
501 }
502
503 int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
504 {
505         int ret;
506
507         if (!ops)
508                 return -EINVAL;
509
510         atomic_set(&core->insts_count, 0);
511         core->core_ops = ops;
512         core->state = CORE_UNINIT;
513         init_completion(&core->done);
514         pkt_set_version(core->res->hfi_version);
515         ret = venus_hfi_create(core);
516
517         return ret;
518 }
519
520 void hfi_destroy(struct venus_core *core)
521 {
522         venus_hfi_destroy(core);
523 }