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