GNU Linux-libre 4.14.332-gnu1
[releases.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         if (!core->ops)
121                 goto unlock;
122
123         ret = core->ops->core_deinit(core);
124
125         if (!ret)
126                 core->state = CORE_UNINIT;
127
128 unlock:
129         mutex_unlock(&core->lock);
130         return ret;
131 }
132
133 int hfi_core_suspend(struct venus_core *core)
134 {
135         if (core->state != CORE_INIT)
136                 return 0;
137
138         return core->ops->suspend(core);
139 }
140
141 int hfi_core_resume(struct venus_core *core, bool force)
142 {
143         if (!force && core->state != CORE_INIT)
144                 return 0;
145
146         return core->ops->resume(core);
147 }
148
149 int hfi_core_trigger_ssr(struct venus_core *core, u32 type)
150 {
151         return core->ops->core_trigger_ssr(core, type);
152 }
153
154 int hfi_core_ping(struct venus_core *core)
155 {
156         int ret;
157
158         mutex_lock(&core->lock);
159
160         ret = core->ops->core_ping(core, 0xbeef);
161         if (ret)
162                 goto unlock;
163
164         ret = wait_for_completion_timeout(&core->done, TIMEOUT);
165         if (!ret) {
166                 ret = -ETIMEDOUT;
167                 goto unlock;
168         }
169         ret = 0;
170         if (core->error != HFI_ERR_NONE)
171                 ret = -ENODEV;
172 unlock:
173         mutex_unlock(&core->lock);
174         return ret;
175 }
176
177 static int wait_session_msg(struct venus_inst *inst)
178 {
179         int ret;
180
181         ret = wait_for_completion_timeout(&inst->done, TIMEOUT);
182         if (!ret)
183                 return -ETIMEDOUT;
184
185         if (inst->error != HFI_ERR_NONE)
186                 return -EIO;
187
188         return 0;
189 }
190
191 int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops)
192 {
193         struct venus_core *core = inst->core;
194
195         if (!ops)
196                 return -EINVAL;
197
198         inst->state = INST_UNINIT;
199         init_completion(&inst->done);
200         inst->ops = ops;
201
202         mutex_lock(&core->lock);
203         list_add_tail(&inst->list, &core->instances);
204         atomic_inc(&core->insts_count);
205         mutex_unlock(&core->lock);
206
207         return 0;
208 }
209 EXPORT_SYMBOL_GPL(hfi_session_create);
210
211 int hfi_session_init(struct venus_inst *inst, u32 pixfmt)
212 {
213         struct venus_core *core = inst->core;
214         const struct hfi_ops *ops = core->ops;
215         u32 codec;
216         int ret;
217
218         codec = to_codec_type(pixfmt);
219         reinit_completion(&inst->done);
220
221         ret = ops->session_init(inst, inst->session_type, codec);
222         if (ret)
223                 return ret;
224
225         ret = wait_session_msg(inst);
226         if (ret)
227                 return ret;
228
229         inst->state = INST_INIT;
230
231         return 0;
232 }
233 EXPORT_SYMBOL_GPL(hfi_session_init);
234
235 void hfi_session_destroy(struct venus_inst *inst)
236 {
237         struct venus_core *core = inst->core;
238
239         mutex_lock(&core->lock);
240         list_del_init(&inst->list);
241         atomic_dec(&core->insts_count);
242         wake_up_atomic_t(&core->insts_count);
243         mutex_unlock(&core->lock);
244 }
245 EXPORT_SYMBOL_GPL(hfi_session_destroy);
246
247 int hfi_session_deinit(struct venus_inst *inst)
248 {
249         const struct hfi_ops *ops = inst->core->ops;
250         int ret;
251
252         if (inst->state == INST_UNINIT)
253                 return 0;
254
255         if (inst->state < INST_INIT)
256                 return -EINVAL;
257
258         reinit_completion(&inst->done);
259
260         ret = ops->session_end(inst);
261         if (ret)
262                 return ret;
263
264         ret = wait_session_msg(inst);
265         if (ret)
266                 return ret;
267
268         inst->state = INST_UNINIT;
269
270         return 0;
271 }
272 EXPORT_SYMBOL_GPL(hfi_session_deinit);
273
274 int hfi_session_start(struct venus_inst *inst)
275 {
276         const struct hfi_ops *ops = inst->core->ops;
277         int ret;
278
279         if (inst->state != INST_LOAD_RESOURCES)
280                 return -EINVAL;
281
282         reinit_completion(&inst->done);
283
284         ret = ops->session_start(inst);
285         if (ret)
286                 return ret;
287
288         ret = wait_session_msg(inst);
289         if (ret)
290                 return ret;
291
292         inst->state = INST_START;
293
294         return 0;
295 }
296
297 int hfi_session_stop(struct venus_inst *inst)
298 {
299         const struct hfi_ops *ops = inst->core->ops;
300         int ret;
301
302         if (inst->state != INST_START)
303                 return -EINVAL;
304
305         reinit_completion(&inst->done);
306
307         ret = ops->session_stop(inst);
308         if (ret)
309                 return ret;
310
311         ret = wait_session_msg(inst);
312         if (ret)
313                 return ret;
314
315         inst->state = INST_STOP;
316
317         return 0;
318 }
319
320 int hfi_session_continue(struct venus_inst *inst)
321 {
322         struct venus_core *core = inst->core;
323
324         if (core->res->hfi_version != HFI_VERSION_3XX)
325                 return 0;
326
327         return core->ops->session_continue(inst);
328 }
329 EXPORT_SYMBOL_GPL(hfi_session_continue);
330
331 int hfi_session_abort(struct venus_inst *inst)
332 {
333         const struct hfi_ops *ops = inst->core->ops;
334         int ret;
335
336         reinit_completion(&inst->done);
337
338         ret = ops->session_abort(inst);
339         if (ret)
340                 return ret;
341
342         ret = wait_session_msg(inst);
343         if (ret)
344                 return ret;
345
346         return 0;
347 }
348
349 int hfi_session_load_res(struct venus_inst *inst)
350 {
351         const struct hfi_ops *ops = inst->core->ops;
352         int ret;
353
354         if (inst->state != INST_INIT)
355                 return -EINVAL;
356
357         reinit_completion(&inst->done);
358
359         ret = ops->session_load_res(inst);
360         if (ret)
361                 return ret;
362
363         ret = wait_session_msg(inst);
364         if (ret)
365                 return ret;
366
367         inst->state = INST_LOAD_RESOURCES;
368
369         return 0;
370 }
371
372 int hfi_session_unload_res(struct venus_inst *inst)
373 {
374         const struct hfi_ops *ops = inst->core->ops;
375         int ret;
376
377         if (inst->state != INST_STOP)
378                 return -EINVAL;
379
380         reinit_completion(&inst->done);
381
382         ret = ops->session_release_res(inst);
383         if (ret)
384                 return ret;
385
386         ret = wait_session_msg(inst);
387         if (ret)
388                 return ret;
389
390         inst->state = INST_RELEASE_RESOURCES;
391
392         return 0;
393 }
394
395 int hfi_session_flush(struct venus_inst *inst)
396 {
397         const struct hfi_ops *ops = inst->core->ops;
398         int ret;
399
400         reinit_completion(&inst->done);
401
402         ret = ops->session_flush(inst, HFI_FLUSH_ALL);
403         if (ret)
404                 return ret;
405
406         ret = wait_session_msg(inst);
407         if (ret)
408                 return ret;
409
410         return 0;
411 }
412 EXPORT_SYMBOL_GPL(hfi_session_flush);
413
414 int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd)
415 {
416         const struct hfi_ops *ops = inst->core->ops;
417
418         return ops->session_set_buffers(inst, bd);
419 }
420
421 int hfi_session_unset_buffers(struct venus_inst *inst,
422                               struct hfi_buffer_desc *bd)
423 {
424         const struct hfi_ops *ops = inst->core->ops;
425         int ret;
426
427         reinit_completion(&inst->done);
428
429         ret = ops->session_unset_buffers(inst, bd);
430         if (ret)
431                 return ret;
432
433         if (!bd->response_required)
434                 return 0;
435
436         ret = wait_session_msg(inst);
437         if (ret)
438                 return ret;
439
440         return 0;
441 }
442
443 int hfi_session_get_property(struct venus_inst *inst, u32 ptype,
444                              union hfi_get_property *hprop)
445 {
446         const struct hfi_ops *ops = inst->core->ops;
447         int ret;
448
449         if (inst->state < INST_INIT || inst->state >= INST_STOP)
450                 return -EINVAL;
451
452         reinit_completion(&inst->done);
453
454         ret = ops->session_get_property(inst, ptype);
455         if (ret)
456                 return ret;
457
458         ret = wait_session_msg(inst);
459         if (ret)
460                 return ret;
461
462         *hprop = inst->hprop;
463
464         return 0;
465 }
466 EXPORT_SYMBOL_GPL(hfi_session_get_property);
467
468 int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata)
469 {
470         const struct hfi_ops *ops = inst->core->ops;
471
472         if (inst->state < INST_INIT || inst->state >= INST_STOP)
473                 return -EINVAL;
474
475         return ops->session_set_property(inst, ptype, pdata);
476 }
477 EXPORT_SYMBOL_GPL(hfi_session_set_property);
478
479 int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd)
480 {
481         const struct hfi_ops *ops = inst->core->ops;
482
483         if (fd->buffer_type == HFI_BUFFER_INPUT)
484                 return ops->session_etb(inst, fd);
485         else if (fd->buffer_type == HFI_BUFFER_OUTPUT)
486                 return ops->session_ftb(inst, fd);
487
488         return -EINVAL;
489 }
490 EXPORT_SYMBOL_GPL(hfi_session_process_buf);
491
492 irqreturn_t hfi_isr_thread(int irq, void *dev_id)
493 {
494         struct venus_core *core = dev_id;
495
496         return core->ops->isr_thread(core);
497 }
498
499 irqreturn_t hfi_isr(int irq, void *dev)
500 {
501         struct venus_core *core = dev;
502
503         return core->ops->isr(core);
504 }
505
506 int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
507 {
508         int ret;
509
510         if (!ops)
511                 return -EINVAL;
512
513         atomic_set(&core->insts_count, 0);
514         core->core_ops = ops;
515         core->state = CORE_UNINIT;
516         init_completion(&core->done);
517         pkt_set_version(core->res->hfi_version);
518         ret = venus_hfi_create(core);
519
520         return ret;
521 }
522
523 void hfi_destroy(struct venus_core *core)
524 {
525         venus_hfi_destroy(core);
526 }