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
182	if (!ops)
183		return -EINVAL;
184
185	inst->state = INST_UNINIT;
186	init_completion(&inst->done);
187	inst->ops = ops;
188
189	mutex_lock(&core->lock);
190	list_add_tail(&inst->list, &core->instances);
191	atomic_inc(&core->insts_count);
192	mutex_unlock(&core->lock);
193
194	return 0;
195}
196EXPORT_SYMBOL_GPL(hfi_session_create);
197
198int hfi_session_init(struct venus_inst *inst, u32 pixfmt)
199{
200	struct venus_core *core = inst->core;
201	const struct hfi_ops *ops = core->ops;
202	int ret;
203
204	if (inst->state != INST_UNINIT)
205		return -EINVAL;
206
207	inst->hfi_codec = to_codec_type(pixfmt);
208	reinit_completion(&inst->done);
209
210	ret = ops->session_init(inst, inst->session_type, inst->hfi_codec);
211	if (ret)
212		return ret;
213
214	ret = wait_session_msg(inst);
215	if (ret)
216		return ret;
217
218	inst->state = INST_INIT;
219
220	return 0;
221}
222EXPORT_SYMBOL_GPL(hfi_session_init);
223
224void hfi_session_destroy(struct venus_inst *inst)
225{
226	struct venus_core *core = inst->core;
227
228	mutex_lock(&core->lock);
229	list_del_init(&inst->list);
230	if (atomic_dec_and_test(&core->insts_count))
231		wake_up_var(&core->insts_count);
232	mutex_unlock(&core->lock);
233}
234EXPORT_SYMBOL_GPL(hfi_session_destroy);
235
236int hfi_session_deinit(struct venus_inst *inst)
237{
238	const struct hfi_ops *ops = inst->core->ops;
239	int ret;
240
241	if (inst->state == INST_UNINIT)
242		return 0;
243
244	if (inst->state < INST_INIT)
245		return -EINVAL;
246
247	reinit_completion(&inst->done);
248
249	ret = ops->session_end(inst);
250	if (ret)
251		return ret;
252
253	ret = wait_session_msg(inst);
254	if (ret)
255		return ret;
256
257	inst->state = INST_UNINIT;
258
259	return 0;
260}
261EXPORT_SYMBOL_GPL(hfi_session_deinit);
262
263int hfi_session_start(struct venus_inst *inst)
264{
265	const struct hfi_ops *ops = inst->core->ops;
266	int ret;
267
268	if (inst->state != INST_LOAD_RESOURCES)
269		return -EINVAL;
270
271	reinit_completion(&inst->done);
272
273	ret = ops->session_start(inst);
274	if (ret)
275		return ret;
276
277	ret = wait_session_msg(inst);
278	if (ret)
279		return ret;
280
281	inst->state = INST_START;
282
283	return 0;
284}
285EXPORT_SYMBOL_GPL(hfi_session_start);
286
287int hfi_session_stop(struct venus_inst *inst)
288{
289	const struct hfi_ops *ops = inst->core->ops;
290	int ret;
291
292	if (inst->state != INST_START)
293		return -EINVAL;
294
295	reinit_completion(&inst->done);
296
297	ret = ops->session_stop(inst);
298	if (ret)
299		return ret;
300
301	ret = wait_session_msg(inst);
302	if (ret)
303		return ret;
304
305	inst->state = INST_STOP;
306
307	return 0;
308}
309EXPORT_SYMBOL_GPL(hfi_session_stop);
310
311int hfi_session_continue(struct venus_inst *inst)
312{
313	struct venus_core *core = inst->core;
314
315	if (core->res->hfi_version == HFI_VERSION_1XX)
316		return 0;
317
318	return core->ops->session_continue(inst);
319}
320EXPORT_SYMBOL_GPL(hfi_session_continue);
321
322int 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}
339EXPORT_SYMBOL_GPL(hfi_session_abort);
340
341int hfi_session_load_res(struct venus_inst *inst)
342{
343	const struct hfi_ops *ops = inst->core->ops;
344	int ret;
345
346	if (inst->state != INST_INIT)
347		return -EINVAL;
348
349	reinit_completion(&inst->done);
350
351	ret = ops->session_load_res(inst);
352	if (ret)
353		return ret;
354
355	ret = wait_session_msg(inst);
356	if (ret)
357		return ret;
358
359	inst->state = INST_LOAD_RESOURCES;
360
361	return 0;
362}
363
364int hfi_session_unload_res(struct venus_inst *inst)
365{
366	const struct hfi_ops *ops = inst->core->ops;
367	int ret;
368
369	if (inst->state != INST_STOP)
370		return -EINVAL;
371
372	reinit_completion(&inst->done);
373
374	ret = ops->session_release_res(inst);
375	if (ret)
376		return ret;
377
378	ret = wait_session_msg(inst);
379	if (ret)
380		return ret;
381
382	inst->state = INST_RELEASE_RESOURCES;
383
384	return 0;
385}
386EXPORT_SYMBOL_GPL(hfi_session_unload_res);
387
388int hfi_session_flush(struct venus_inst *inst, u32 type, bool block)
389{
390	const struct hfi_ops *ops = inst->core->ops;
391	int ret;
392
393	reinit_completion(&inst->done);
394
395	ret = ops->session_flush(inst, type);
396	if (ret)
397		return ret;
398
399	if (block) {
400		ret = wait_session_msg(inst);
401		if (ret)
402			return ret;
403	}
404
405	return 0;
406}
407EXPORT_SYMBOL_GPL(hfi_session_flush);
408
409int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd)
410{
411	const struct hfi_ops *ops = inst->core->ops;
412
413	return ops->session_set_buffers(inst, bd);
414}
415
416int hfi_session_unset_buffers(struct venus_inst *inst,
417			      struct hfi_buffer_desc *bd)
418{
419	const struct hfi_ops *ops = inst->core->ops;
420	int ret;
421
422	reinit_completion(&inst->done);
423
424	ret = ops->session_unset_buffers(inst, bd);
425	if (ret)
426		return ret;
427
428	if (!bd->response_required)
429		return 0;
430
431	ret = wait_session_msg(inst);
432	if (ret)
433		return ret;
434
435	return 0;
436}
437
438int hfi_session_get_property(struct venus_inst *inst, u32 ptype,
439			     union hfi_get_property *hprop)
440{
441	const struct hfi_ops *ops = inst->core->ops;
442	int ret;
443
444	if (inst->state < INST_INIT || inst->state >= INST_STOP)
445		return -EINVAL;
446
447	reinit_completion(&inst->done);
448
449	ret = ops->session_get_property(inst, ptype);
450	if (ret)
451		return ret;
452
453	ret = wait_session_msg(inst);
454	if (ret)
455		return ret;
456
457	*hprop = inst->hprop;
458
459	return 0;
460}
461EXPORT_SYMBOL_GPL(hfi_session_get_property);
462
463int hfi_session_set_property(struct venus_inst *inst, u32 ptype, void *pdata)
464{
465	const struct hfi_ops *ops = inst->core->ops;
466
467	if (inst->state < INST_INIT || inst->state >= INST_STOP)
468		return -EINVAL;
469
470	return ops->session_set_property(inst, ptype, pdata);
471}
472EXPORT_SYMBOL_GPL(hfi_session_set_property);
473
474int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd)
475{
476	const struct hfi_ops *ops = inst->core->ops;
477
478	if (fd->buffer_type == HFI_BUFFER_INPUT)
479		return ops->session_etb(inst, fd);
480	else if (fd->buffer_type == HFI_BUFFER_OUTPUT ||
481		 fd->buffer_type == HFI_BUFFER_OUTPUT2)
482		return ops->session_ftb(inst, fd);
483
484	return -EINVAL;
485}
486EXPORT_SYMBOL_GPL(hfi_session_process_buf);
487
488irqreturn_t hfi_isr_thread(int irq, void *dev_id)
489{
490	struct venus_core *core = dev_id;
491
492	return core->ops->isr_thread(core);
493}
494
495irqreturn_t hfi_isr(int irq, void *dev)
496{
497	struct venus_core *core = dev;
498
499	return core->ops->isr(core);
500}
501
502int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops)
503{
504	int ret;
505
506	if (!ops)
507		return -EINVAL;
508
509	atomic_set(&core->insts_count, 0);
510	core->core_ops = ops;
511	core->state = CORE_UNINIT;
512	init_completion(&core->done);
513	pkt_set_version(core->res->hfi_version);
514	ret = venus_hfi_create(core);
515
516	return ret;
517}
518
519void hfi_destroy(struct venus_core *core)
520{
521	venus_hfi_destroy(core);
522}
523
524void hfi_reinit(struct venus_core *core)
525{
526	venus_hfi_queues_reinit(core);
527}
528