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