1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2020-2021 NXP |
4 | */ |
5 | |
6 | #include <linux/init.h> |
7 | #include <linux/interconnect.h> |
8 | #include <linux/ioctl.h> |
9 | #include <linux/list.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include "vpu.h" |
13 | #include "vpu_core.h" |
14 | #include "vpu_rpc.h" |
15 | #include "vpu_mbox.h" |
16 | #include "vpu_defs.h" |
17 | #include "vpu_cmds.h" |
18 | #include "vpu_msgs.h" |
19 | #include "vpu_v4l2.h" |
20 | |
21 | #define 3 |
22 | |
23 | struct vpu_msg_handler { |
24 | u32 id; |
25 | void (*done)(struct vpu_inst *inst, struct vpu_rpc_event *pkt); |
26 | u32 is_str; |
27 | }; |
28 | |
29 | static void vpu_session_handle_start_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
30 | { |
31 | vpu_trace(inst->dev, "[%d]\n" , inst->id); |
32 | } |
33 | |
34 | static void vpu_session_handle_mem_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
35 | { |
36 | struct vpu_pkt_mem_req_data req_data = { 0 }; |
37 | |
38 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: (void *)&req_data); |
39 | vpu_trace(inst->dev, "[%d] %d:%d %d:%d %d:%d\n" , |
40 | inst->id, |
41 | req_data.enc_frame_size, |
42 | req_data.enc_frame_num, |
43 | req_data.ref_frame_size, |
44 | req_data.ref_frame_num, |
45 | req_data.act_buf_size, |
46 | req_data.act_buf_num); |
47 | vpu_inst_lock(inst); |
48 | call_void_vop(inst, mem_request, |
49 | req_data.enc_frame_size, |
50 | req_data.enc_frame_num, |
51 | req_data.ref_frame_size, |
52 | req_data.ref_frame_num, |
53 | req_data.act_buf_size, |
54 | req_data.act_buf_num); |
55 | vpu_inst_unlock(inst); |
56 | } |
57 | |
58 | static void vpu_session_handle_stop_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
59 | { |
60 | vpu_trace(inst->dev, "[%d]\n" , inst->id); |
61 | |
62 | call_void_vop(inst, stop_done); |
63 | } |
64 | |
65 | static void vpu_session_handle_seq_hdr(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
66 | { |
67 | struct vpu_dec_codec_info info; |
68 | const struct vpu_core_resources *res; |
69 | |
70 | memset(&info, 0, sizeof(info)); |
71 | res = vpu_get_resource(inst); |
72 | info.stride = res ? res->stride : 1; |
73 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: (void *)&info); |
74 | call_void_vop(inst, event_notify, VPU_MSG_ID_SEQ_HDR_FOUND, &info); |
75 | } |
76 | |
77 | static void vpu_session_handle_resolution_change(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
78 | { |
79 | call_void_vop(inst, event_notify, VPU_MSG_ID_RES_CHANGE, NULL); |
80 | } |
81 | |
82 | static void vpu_session_handle_enc_frame_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
83 | { |
84 | struct vpu_enc_pic_info info = { 0 }; |
85 | |
86 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: (void *)&info); |
87 | dev_dbg(inst->dev, "[%d] frame id = %d, wptr = 0x%x, size = %d\n" , |
88 | inst->id, info.frame_id, info.wptr, info.frame_size); |
89 | call_void_vop(inst, get_one_frame, &info); |
90 | } |
91 | |
92 | static void vpu_session_handle_frame_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
93 | { |
94 | struct vpu_fs_info fs = { 0 }; |
95 | |
96 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: &fs); |
97 | call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_REQ, &fs); |
98 | } |
99 | |
100 | static void vpu_session_handle_frame_release(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
101 | { |
102 | if (inst->core->type == VPU_CORE_TYPE_ENC) { |
103 | struct vpu_frame_info info; |
104 | |
105 | memset(&info, 0, sizeof(info)); |
106 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: (void *)&info.sequence); |
107 | dev_dbg(inst->dev, "[%d] %d\n" , inst->id, info.sequence); |
108 | info.type = inst->out_format.type; |
109 | call_void_vop(inst, buf_done, &info); |
110 | } else if (inst->core->type == VPU_CORE_TYPE_DEC) { |
111 | struct vpu_fs_info fs = { 0 }; |
112 | |
113 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: &fs); |
114 | call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_RELEASE, &fs); |
115 | } |
116 | } |
117 | |
118 | static void vpu_session_handle_input_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
119 | { |
120 | dev_dbg(inst->dev, "[%d]\n" , inst->id); |
121 | call_void_vop(inst, input_done); |
122 | } |
123 | |
124 | static void vpu_session_handle_pic_decoded(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
125 | { |
126 | struct vpu_dec_pic_info info = { 0 }; |
127 | |
128 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: (void *)&info); |
129 | call_void_vop(inst, get_one_frame, &info); |
130 | } |
131 | |
132 | static void vpu_session_handle_pic_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
133 | { |
134 | struct vpu_dec_pic_info info = { 0 }; |
135 | struct vpu_frame_info frame; |
136 | |
137 | memset(&frame, 0, sizeof(frame)); |
138 | vpu_iface_unpack_msg_data(core: inst->core, pkt, data: (void *)&info); |
139 | if (inst->core->type == VPU_CORE_TYPE_DEC) |
140 | frame.type = inst->cap_format.type; |
141 | frame.id = info.id; |
142 | frame.luma = info.luma; |
143 | frame.skipped = info.skipped; |
144 | frame.timestamp = info.timestamp; |
145 | |
146 | call_void_vop(inst, buf_done, &frame); |
147 | } |
148 | |
149 | static void vpu_session_handle_eos(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
150 | { |
151 | call_void_vop(inst, event_notify, VPU_MSG_ID_PIC_EOS, NULL); |
152 | } |
153 | |
154 | static void vpu_session_handle_error(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
155 | { |
156 | char *str = (char *)pkt->data; |
157 | |
158 | if (*str) |
159 | dev_err(inst->dev, "instance %d firmware error : %s\n" , inst->id, str); |
160 | else |
161 | dev_err(inst->dev, "instance %d is unsupported stream\n" , inst->id); |
162 | call_void_vop(inst, event_notify, VPU_MSG_ID_UNSUPPORTED, NULL); |
163 | vpu_v4l2_set_error(inst); |
164 | } |
165 | |
166 | static void vpu_session_handle_firmware_xcpt(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
167 | { |
168 | char *str = (char *)pkt->data; |
169 | |
170 | dev_err(inst->dev, "%s firmware xcpt: %s\n" , |
171 | vpu_core_type_desc(inst->core->type), str); |
172 | call_void_vop(inst, event_notify, VPU_MSG_ID_FIRMWARE_XCPT, NULL); |
173 | set_bit(nr: inst->id, addr: &inst->core->hang_mask); |
174 | vpu_v4l2_set_error(inst); |
175 | } |
176 | |
177 | static void vpu_session_handle_pic_skipped(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
178 | { |
179 | vpu_inst_lock(inst); |
180 | vpu_skip_frame(inst, count: 1); |
181 | vpu_inst_unlock(inst); |
182 | } |
183 | |
184 | static void vpu_session_handle_dbg_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
185 | { |
186 | char *str = (char *)pkt->data; |
187 | |
188 | if (*str) |
189 | dev_info(inst->dev, "instance %d firmware dbg msg : %s\n" , inst->id, str); |
190 | } |
191 | |
192 | static void vpu_terminate_string_msg(struct vpu_rpc_event *pkt) |
193 | { |
194 | if (pkt->hdr.num == ARRAY_SIZE(pkt->data)) |
195 | pkt->hdr.num--; |
196 | pkt->data[pkt->hdr.num] = 0; |
197 | } |
198 | |
199 | static struct vpu_msg_handler handlers[] = { |
200 | {VPU_MSG_ID_START_DONE, vpu_session_handle_start_done}, |
201 | {VPU_MSG_ID_STOP_DONE, vpu_session_handle_stop_done}, |
202 | {VPU_MSG_ID_MEM_REQUEST, vpu_session_handle_mem_request}, |
203 | {VPU_MSG_ID_SEQ_HDR_FOUND, vpu_session_handle_seq_hdr}, |
204 | {VPU_MSG_ID_RES_CHANGE, vpu_session_handle_resolution_change}, |
205 | {VPU_MSG_ID_FRAME_INPUT_DONE, vpu_session_handle_input_done}, |
206 | {VPU_MSG_ID_FRAME_REQ, vpu_session_handle_frame_request}, |
207 | {VPU_MSG_ID_FRAME_RELEASE, vpu_session_handle_frame_release}, |
208 | {VPU_MSG_ID_ENC_DONE, vpu_session_handle_enc_frame_done}, |
209 | {VPU_MSG_ID_PIC_DECODED, vpu_session_handle_pic_decoded}, |
210 | {VPU_MSG_ID_DEC_DONE, vpu_session_handle_pic_done}, |
211 | {VPU_MSG_ID_PIC_EOS, vpu_session_handle_eos}, |
212 | {VPU_MSG_ID_UNSUPPORTED, vpu_session_handle_error, true}, |
213 | {VPU_MSG_ID_FIRMWARE_XCPT, vpu_session_handle_firmware_xcpt, true}, |
214 | {VPU_MSG_ID_PIC_SKIPPED, vpu_session_handle_pic_skipped}, |
215 | {VPU_MSG_ID_DBG_MSG, vpu_session_handle_dbg_msg, true}, |
216 | }; |
217 | |
218 | static int vpu_session_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *msg) |
219 | { |
220 | int ret; |
221 | u32 msg_id; |
222 | struct vpu_msg_handler *handler = NULL; |
223 | unsigned int i; |
224 | |
225 | ret = vpu_iface_convert_msg_id(core: inst->core, msg_id: msg->hdr.id); |
226 | if (ret < 0) |
227 | return -EINVAL; |
228 | |
229 | msg_id = ret; |
230 | dev_dbg(inst->dev, "[%d] receive event(%s)\n" , inst->id, vpu_id_name(msg_id)); |
231 | |
232 | for (i = 0; i < ARRAY_SIZE(handlers); i++) { |
233 | if (handlers[i].id == msg_id) { |
234 | handler = &handlers[i]; |
235 | break; |
236 | } |
237 | } |
238 | |
239 | if (handler) { |
240 | if (handler->is_str) |
241 | vpu_terminate_string_msg(pkt: msg); |
242 | if (handler->done) |
243 | handler->done(inst, msg); |
244 | } |
245 | |
246 | vpu_response_cmd(inst, response: msg_id, handled: 1); |
247 | |
248 | return 0; |
249 | } |
250 | |
251 | static bool vpu_inst_receive_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
252 | { |
253 | unsigned long bytes = sizeof(struct vpu_rpc_event_header); |
254 | u32 ret; |
255 | |
256 | memset(pkt, 0, sizeof(*pkt)); |
257 | if (kfifo_len(&inst->msg_fifo) < bytes) |
258 | return false; |
259 | |
260 | ret = kfifo_out(&inst->msg_fifo, pkt, bytes); |
261 | if (ret != bytes) |
262 | return false; |
263 | |
264 | if (pkt->hdr.num > 0) { |
265 | bytes = pkt->hdr.num * sizeof(u32); |
266 | ret = kfifo_out(&inst->msg_fifo, pkt->data, bytes); |
267 | if (ret != bytes) |
268 | return false; |
269 | } |
270 | |
271 | return true; |
272 | } |
273 | |
274 | void vpu_inst_run_work(struct work_struct *work) |
275 | { |
276 | struct vpu_inst *inst = container_of(work, struct vpu_inst, msg_work); |
277 | struct vpu_rpc_event pkt; |
278 | |
279 | while (vpu_inst_receive_msg(inst, pkt: &pkt)) |
280 | vpu_session_handle_msg(inst, msg: &pkt); |
281 | } |
282 | |
283 | static void vpu_inst_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt) |
284 | { |
285 | unsigned long bytes; |
286 | u32 id = pkt->hdr.id; |
287 | int ret; |
288 | |
289 | if (!inst->workqueue) |
290 | return; |
291 | |
292 | bytes = sizeof(pkt->hdr) + pkt->hdr.num * sizeof(u32); |
293 | ret = kfifo_in(&inst->msg_fifo, pkt, bytes); |
294 | if (ret != bytes) |
295 | dev_err(inst->dev, "[%d:%d]overflow: %d\n" , inst->core->id, inst->id, id); |
296 | queue_work(wq: inst->workqueue, work: &inst->msg_work); |
297 | } |
298 | |
299 | static int vpu_handle_msg(struct vpu_core *core) |
300 | { |
301 | struct vpu_rpc_event pkt; |
302 | struct vpu_inst *inst; |
303 | int ret; |
304 | |
305 | memset(&pkt, 0, sizeof(pkt)); |
306 | while (!vpu_iface_receive_msg(core, msg: &pkt)) { |
307 | dev_dbg(core->dev, "event index = %d, id = %d, num = %d\n" , |
308 | pkt.hdr.index, pkt.hdr.id, pkt.hdr.num); |
309 | |
310 | ret = vpu_iface_convert_msg_id(core, msg_id: pkt.hdr.id); |
311 | if (ret < 0) |
312 | continue; |
313 | |
314 | inst = vpu_core_find_instance(core, index: pkt.hdr.index); |
315 | if (inst) { |
316 | vpu_response_cmd(inst, response: ret, handled: 0); |
317 | mutex_lock(&core->cmd_lock); |
318 | vpu_inst_record_flow(inst, flow: ret); |
319 | mutex_unlock(lock: &core->cmd_lock); |
320 | |
321 | vpu_inst_handle_msg(inst, pkt: &pkt); |
322 | vpu_inst_put(inst); |
323 | } |
324 | memset(&pkt, 0, sizeof(pkt)); |
325 | } |
326 | |
327 | return 0; |
328 | } |
329 | |
330 | static int vpu_isr_thread(struct vpu_core *core, u32 irq_code) |
331 | { |
332 | dev_dbg(core->dev, "irq code = 0x%x\n" , irq_code); |
333 | switch (irq_code) { |
334 | case VPU_IRQ_CODE_SYNC: |
335 | vpu_mbox_send_msg(core, type: PRC_BUF_OFFSET, data: core->rpc.phys - core->fw.phys); |
336 | vpu_mbox_send_msg(core, type: BOOT_ADDRESS, data: core->fw.phys); |
337 | vpu_mbox_send_msg(core, type: INIT_DONE, data: 2); |
338 | break; |
339 | case VPU_IRQ_CODE_BOOT_DONE: |
340 | break; |
341 | case VPU_IRQ_CODE_SNAPSHOT_DONE: |
342 | break; |
343 | default: |
344 | vpu_handle_msg(core); |
345 | break; |
346 | } |
347 | |
348 | return 0; |
349 | } |
350 | |
351 | static void vpu_core_run_msg_work(struct vpu_core *core) |
352 | { |
353 | const unsigned int SIZE = sizeof(u32); |
354 | |
355 | while (kfifo_len(&core->msg_fifo) >= SIZE) { |
356 | u32 data = 0; |
357 | |
358 | if (kfifo_out(&core->msg_fifo, &data, SIZE) == SIZE) |
359 | vpu_isr_thread(core, irq_code: data); |
360 | } |
361 | } |
362 | |
363 | void vpu_msg_run_work(struct work_struct *work) |
364 | { |
365 | struct vpu_core *core = container_of(work, struct vpu_core, msg_work); |
366 | unsigned long delay = msecs_to_jiffies(m: 10); |
367 | |
368 | vpu_core_run_msg_work(core); |
369 | queue_delayed_work(wq: core->workqueue, dwork: &core->msg_delayed_work, delay); |
370 | } |
371 | |
372 | void vpu_msg_delayed_work(struct work_struct *work) |
373 | { |
374 | struct vpu_core *core; |
375 | struct delayed_work *dwork; |
376 | unsigned long bytes = sizeof(u32); |
377 | u32 i; |
378 | |
379 | if (!work) |
380 | return; |
381 | |
382 | dwork = to_delayed_work(work); |
383 | core = container_of(dwork, struct vpu_core, msg_delayed_work); |
384 | if (kfifo_len(&core->msg_fifo) >= bytes) |
385 | vpu_core_run_msg_work(core); |
386 | |
387 | bytes = sizeof(struct vpu_rpc_event_header); |
388 | for (i = 0; i < core->supported_instance_count; i++) { |
389 | struct vpu_inst *inst = vpu_core_find_instance(core, index: i); |
390 | |
391 | if (!inst) |
392 | continue; |
393 | |
394 | if (inst->workqueue && kfifo_len(&inst->msg_fifo) >= bytes) |
395 | queue_work(wq: inst->workqueue, work: &inst->msg_work); |
396 | |
397 | vpu_inst_put(inst); |
398 | } |
399 | } |
400 | |
401 | int vpu_isr(struct vpu_core *core, u32 irq) |
402 | { |
403 | switch (irq) { |
404 | case VPU_IRQ_CODE_SYNC: |
405 | break; |
406 | case VPU_IRQ_CODE_BOOT_DONE: |
407 | complete(&core->cmp); |
408 | break; |
409 | case VPU_IRQ_CODE_SNAPSHOT_DONE: |
410 | complete(&core->cmp); |
411 | break; |
412 | default: |
413 | break; |
414 | } |
415 | |
416 | if (kfifo_in(&core->msg_fifo, &irq, sizeof(irq)) != sizeof(irq)) |
417 | dev_err(core->dev, "[%d]overflow: %d\n" , core->id, irq); |
418 | queue_work(wq: core->workqueue, work: &core->msg_work); |
419 | |
420 | return 0; |
421 | } |
422 | |