Commit | Line | Data |
---|---|---|
576f1b4b HW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Copyright (c) 2018 MediaTek Inc. | |
4 | ||
5 | #include <linux/completion.h> | |
6 | #include <linux/errno.h> | |
7 | #include <linux/dma-mapping.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/mailbox_controller.h> | |
d01e0aec | 10 | #include <linux/of.h> |
576f1b4b HW |
11 | #include <linux/soc/mediatek/mtk-cmdq.h> |
12 | ||
576f1b4b | 13 | #define CMDQ_WRITE_ENABLE_MASK BIT(0) |
b2ff2356 | 14 | #define CMDQ_POLL_ENABLE_MASK BIT(0) |
400e2fa8 JJL |
15 | /* dedicate the last GPR_R15 to assign the register address to be poll */ |
16 | #define CMDQ_POLL_ADDR_GPR (15) | |
576f1b4b | 17 | #define CMDQ_EOC_IRQ_EN BIT(0) |
613c2e2c | 18 | #define CMDQ_REG_TYPE 1 |
ed4d5ab1 CKH |
19 | #define CMDQ_JUMP_RELATIVE 0 |
20 | #define CMDQ_JUMP_ABSOLUTE 1 | |
576f1b4b | 21 | |
5c8b718c BH |
22 | struct cmdq_instruction { |
23 | union { | |
24 | u32 value; | |
25 | u32 mask; | |
5f6e560c DYH |
26 | struct { |
27 | u16 arg_c; | |
28 | u16 src_reg; | |
29 | }; | |
5c8b718c BH |
30 | }; |
31 | union { | |
32 | u16 offset; | |
33 | u16 event; | |
613c2e2c DYH |
34 | u16 reg_dst; |
35 | }; | |
36 | union { | |
37 | u8 subsys; | |
38 | struct { | |
39 | u8 sop:5; | |
40 | u8 arg_c_t:1; | |
41 | u8 src_t:1; | |
42 | u8 dst_t:1; | |
43 | }; | |
5c8b718c | 44 | }; |
5c8b718c BH |
45 | u8 op; |
46 | }; | |
47 | ||
d412f18c BH |
48 | int cmdq_dev_get_client_reg(struct device *dev, |
49 | struct cmdq_client_reg *client_reg, int idx) | |
50 | { | |
51 | struct of_phandle_args spec; | |
52 | int err; | |
53 | ||
54 | if (!client_reg) | |
55 | return -ENOENT; | |
56 | ||
57 | err = of_parse_phandle_with_fixed_args(dev->of_node, | |
58 | "mediatek,gce-client-reg", | |
59 | 3, idx, &spec); | |
60 | if (err < 0) { | |
61 | dev_err(dev, | |
62 | "error %d can't parse gce-client-reg property (%d)", | |
63 | err, idx); | |
64 | ||
65 | return err; | |
66 | } | |
67 | ||
68 | client_reg->subsys = (u8)spec.args[0]; | |
69 | client_reg->offset = (u16)spec.args[1]; | |
70 | client_reg->size = (u16)spec.args[2]; | |
71 | of_node_put(spec.np); | |
72 | ||
73 | return 0; | |
74 | } | |
75 | EXPORT_SYMBOL(cmdq_dev_get_client_reg); | |
76 | ||
a69dcdfc | 77 | struct cmdq_client *cmdq_mbox_create(struct device *dev, int index) |
576f1b4b HW |
78 | { |
79 | struct cmdq_client *client; | |
80 | ||
81 | client = kzalloc(sizeof(*client), GFP_KERNEL); | |
82 | if (!client) | |
83 | return (struct cmdq_client *)-ENOMEM; | |
84 | ||
576f1b4b HW |
85 | client->client.dev = dev; |
86 | client->client.tx_block = false; | |
ce35e21d | 87 | client->client.knows_txdone = true; |
576f1b4b HW |
88 | client->chan = mbox_request_channel(&client->client, index); |
89 | ||
90 | if (IS_ERR(client->chan)) { | |
91 | long err; | |
92 | ||
93 | dev_err(dev, "failed to request channel\n"); | |
94 | err = PTR_ERR(client->chan); | |
95 | kfree(client); | |
96 | ||
97 | return ERR_PTR(err); | |
98 | } | |
99 | ||
100 | return client; | |
101 | } | |
102 | EXPORT_SYMBOL(cmdq_mbox_create); | |
103 | ||
104 | void cmdq_mbox_destroy(struct cmdq_client *client) | |
105 | { | |
576f1b4b HW |
106 | mbox_free_channel(client->chan); |
107 | kfree(client); | |
108 | } | |
109 | EXPORT_SYMBOL(cmdq_mbox_destroy); | |
110 | ||
b81b2d55 | 111 | int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t size) |
576f1b4b | 112 | { |
576f1b4b HW |
113 | struct device *dev; |
114 | dma_addr_t dma_addr; | |
115 | ||
576f1b4b | 116 | pkt->va_base = kzalloc(size, GFP_KERNEL); |
b81b2d55 CKH |
117 | if (!pkt->va_base) |
118 | return -ENOMEM; | |
119 | ||
576f1b4b | 120 | pkt->buf_size = size; |
576f1b4b HW |
121 | |
122 | dev = client->chan->mbox->dev; | |
123 | dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, | |
124 | DMA_TO_DEVICE); | |
125 | if (dma_mapping_error(dev, dma_addr)) { | |
126 | dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); | |
127 | kfree(pkt->va_base); | |
b81b2d55 | 128 | return -ENOMEM; |
576f1b4b HW |
129 | } |
130 | ||
131 | pkt->pa_base = dma_addr; | |
132 | ||
b81b2d55 | 133 | return 0; |
576f1b4b HW |
134 | } |
135 | EXPORT_SYMBOL(cmdq_pkt_create); | |
136 | ||
b81b2d55 | 137 | void cmdq_pkt_destroy(struct cmdq_client *client, struct cmdq_pkt *pkt) |
576f1b4b | 138 | { |
576f1b4b HW |
139 | dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, |
140 | DMA_TO_DEVICE); | |
141 | kfree(pkt->va_base); | |
576f1b4b HW |
142 | } |
143 | EXPORT_SYMBOL(cmdq_pkt_destroy); | |
144 | ||
5c8b718c BH |
145 | static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, |
146 | struct cmdq_instruction inst) | |
576f1b4b | 147 | { |
5c8b718c | 148 | struct cmdq_instruction *cmd_ptr; |
576f1b4b HW |
149 | |
150 | if (unlikely(pkt->cmd_buf_size + CMDQ_INST_SIZE > pkt->buf_size)) { | |
151 | /* | |
152 | * In the case of allocated buffer size (pkt->buf_size) is used | |
153 | * up, the real required size (pkt->cmdq_buf_size) is still | |
154 | * increased, so that the user knows how much memory should be | |
155 | * ultimately allocated after appending all commands and | |
156 | * flushing the command packet. Therefor, the user can call | |
157 | * cmdq_pkt_create() again with the real required buffer size. | |
158 | */ | |
159 | pkt->cmd_buf_size += CMDQ_INST_SIZE; | |
160 | WARN_ONCE(1, "%s: buffer size %u is too small !\n", | |
161 | __func__, (u32)pkt->buf_size); | |
162 | return -ENOMEM; | |
163 | } | |
5c8b718c | 164 | |
576f1b4b | 165 | cmd_ptr = pkt->va_base + pkt->cmd_buf_size; |
5c8b718c | 166 | *cmd_ptr = inst; |
576f1b4b HW |
167 | pkt->cmd_buf_size += CMDQ_INST_SIZE; |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
556030f0 | 172 | int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value) |
576f1b4b | 173 | { |
5c8b718c | 174 | struct cmdq_instruction inst; |
576f1b4b | 175 | |
5c8b718c BH |
176 | inst.op = CMDQ_CODE_WRITE; |
177 | inst.value = value; | |
178 | inst.offset = offset; | |
179 | inst.subsys = subsys; | |
576f1b4b | 180 | |
5c8b718c | 181 | return cmdq_pkt_append_command(pkt, inst); |
576f1b4b HW |
182 | } |
183 | EXPORT_SYMBOL(cmdq_pkt_write); | |
184 | ||
556030f0 BH |
185 | int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, |
186 | u16 offset, u32 value, u32 mask) | |
576f1b4b | 187 | { |
5c8b718c BH |
188 | struct cmdq_instruction inst = { {0} }; |
189 | u16 offset_mask = offset; | |
01d1b408 | 190 | int err; |
576f1b4b HW |
191 | |
192 | if (mask != 0xffffffff) { | |
5c8b718c BH |
193 | inst.op = CMDQ_CODE_MASK; |
194 | inst.mask = ~mask; | |
195 | err = cmdq_pkt_append_command(pkt, inst); | |
01d1b408 BH |
196 | if (err < 0) |
197 | return err; | |
198 | ||
576f1b4b HW |
199 | offset_mask |= CMDQ_WRITE_ENABLE_MASK; |
200 | } | |
01d1b408 | 201 | err = cmdq_pkt_write(pkt, subsys, offset_mask, value); |
576f1b4b HW |
202 | |
203 | return err; | |
204 | } | |
205 | EXPORT_SYMBOL(cmdq_pkt_write_mask); | |
206 | ||
d3b04aab DYH |
207 | int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low, |
208 | u16 reg_idx) | |
209 | { | |
210 | struct cmdq_instruction inst = {}; | |
211 | ||
212 | inst.op = CMDQ_CODE_READ_S; | |
213 | inst.dst_t = CMDQ_REG_TYPE; | |
214 | inst.sop = high_addr_reg_idx; | |
215 | inst.reg_dst = reg_idx; | |
216 | inst.src_reg = addr_low; | |
217 | ||
218 | return cmdq_pkt_append_command(pkt, inst); | |
219 | } | |
220 | EXPORT_SYMBOL(cmdq_pkt_read_s); | |
221 | ||
5f6e560c DYH |
222 | int cmdq_pkt_write_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, |
223 | u16 addr_low, u16 src_reg_idx) | |
224 | { | |
225 | struct cmdq_instruction inst = {}; | |
226 | ||
227 | inst.op = CMDQ_CODE_WRITE_S; | |
228 | inst.src_t = CMDQ_REG_TYPE; | |
229 | inst.sop = high_addr_reg_idx; | |
230 | inst.offset = addr_low; | |
231 | inst.src_reg = src_reg_idx; | |
232 | ||
233 | return cmdq_pkt_append_command(pkt, inst); | |
234 | } | |
235 | EXPORT_SYMBOL(cmdq_pkt_write_s); | |
236 | ||
11c7842d DYH |
237 | int cmdq_pkt_write_s_mask(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, |
238 | u16 addr_low, u16 src_reg_idx, u32 mask) | |
239 | { | |
240 | struct cmdq_instruction inst = {}; | |
241 | int err; | |
242 | ||
243 | inst.op = CMDQ_CODE_MASK; | |
244 | inst.mask = ~mask; | |
245 | err = cmdq_pkt_append_command(pkt, inst); | |
246 | if (err < 0) | |
247 | return err; | |
248 | ||
249 | inst.mask = 0; | |
250 | inst.op = CMDQ_CODE_WRITE_S_MASK; | |
251 | inst.src_t = CMDQ_REG_TYPE; | |
252 | inst.sop = high_addr_reg_idx; | |
253 | inst.offset = addr_low; | |
254 | inst.src_reg = src_reg_idx; | |
255 | ||
256 | return cmdq_pkt_append_command(pkt, inst); | |
257 | } | |
258 | EXPORT_SYMBOL(cmdq_pkt_write_s_mask); | |
259 | ||
1af43fce DYH |
260 | int cmdq_pkt_write_s_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx, |
261 | u16 addr_low, u32 value) | |
262 | { | |
263 | struct cmdq_instruction inst = {}; | |
264 | ||
265 | inst.op = CMDQ_CODE_WRITE_S; | |
266 | inst.sop = high_addr_reg_idx; | |
267 | inst.offset = addr_low; | |
268 | inst.value = value; | |
269 | ||
270 | return cmdq_pkt_append_command(pkt, inst); | |
271 | } | |
272 | EXPORT_SYMBOL(cmdq_pkt_write_s_value); | |
273 | ||
88a2ffc4 DYH |
274 | int cmdq_pkt_write_s_mask_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx, |
275 | u16 addr_low, u32 value, u32 mask) | |
276 | { | |
277 | struct cmdq_instruction inst = {}; | |
278 | int err; | |
279 | ||
280 | inst.op = CMDQ_CODE_MASK; | |
281 | inst.mask = ~mask; | |
282 | err = cmdq_pkt_append_command(pkt, inst); | |
283 | if (err < 0) | |
284 | return err; | |
285 | ||
286 | inst.op = CMDQ_CODE_WRITE_S_MASK; | |
287 | inst.sop = high_addr_reg_idx; | |
288 | inst.offset = addr_low; | |
289 | inst.value = value; | |
290 | ||
291 | return cmdq_pkt_append_command(pkt, inst); | |
292 | } | |
293 | EXPORT_SYMBOL(cmdq_pkt_write_s_mask_value); | |
294 | ||
263801f8 JJL |
295 | int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_addr) |
296 | { | |
297 | const u16 high_addr_reg_idx = CMDQ_THR_SPR_IDX0; | |
298 | const u16 value_reg_idx = CMDQ_THR_SPR_IDX1; | |
299 | int ret; | |
300 | ||
301 | /* read the value of src_addr into high_addr_reg_idx */ | |
302 | ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr)); | |
303 | if (ret < 0) | |
304 | return ret; | |
305 | ret = cmdq_pkt_read_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(src_addr), value_reg_idx); | |
306 | if (ret < 0) | |
307 | return ret; | |
308 | ||
309 | /* write the value of value_reg_idx into dst_addr */ | |
310 | ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr)); | |
311 | if (ret < 0) | |
312 | return ret; | |
313 | ret = cmdq_pkt_write_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(dst_addr), value_reg_idx); | |
314 | if (ret < 0) | |
315 | return ret; | |
316 | ||
317 | return 0; | |
318 | } | |
319 | EXPORT_SYMBOL(cmdq_pkt_mem_move); | |
320 | ||
23c22299 | 321 | int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear) |
576f1b4b | 322 | { |
5c8b718c | 323 | struct cmdq_instruction inst = { {0} }; |
23c22299 | 324 | u32 clear_option = clear ? CMDQ_WFE_UPDATE : 0; |
576f1b4b HW |
325 | |
326 | if (event >= CMDQ_MAX_EVENT) | |
327 | return -EINVAL; | |
328 | ||
5c8b718c | 329 | inst.op = CMDQ_CODE_WFE; |
23c22299 | 330 | inst.value = CMDQ_WFE_OPTION | clear_option; |
5c8b718c | 331 | inst.event = event; |
576f1b4b | 332 | |
5c8b718c | 333 | return cmdq_pkt_append_command(pkt, inst); |
576f1b4b HW |
334 | } |
335 | EXPORT_SYMBOL(cmdq_pkt_wfe); | |
336 | ||
69ff6833 JJL |
337 | int cmdq_pkt_acquire_event(struct cmdq_pkt *pkt, u16 event) |
338 | { | |
339 | struct cmdq_instruction inst = {}; | |
340 | ||
341 | if (event >= CMDQ_MAX_EVENT) | |
342 | return -EINVAL; | |
343 | ||
344 | inst.op = CMDQ_CODE_WFE; | |
345 | inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE | CMDQ_WFE_WAIT; | |
346 | inst.event = event; | |
347 | ||
348 | return cmdq_pkt_append_command(pkt, inst); | |
349 | } | |
350 | EXPORT_SYMBOL(cmdq_pkt_acquire_event); | |
351 | ||
556030f0 | 352 | int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event) |
576f1b4b | 353 | { |
5c8b718c BH |
354 | struct cmdq_instruction inst = { {0} }; |
355 | ||
576f1b4b HW |
356 | if (event >= CMDQ_MAX_EVENT) |
357 | return -EINVAL; | |
358 | ||
5c8b718c BH |
359 | inst.op = CMDQ_CODE_WFE; |
360 | inst.value = CMDQ_WFE_UPDATE; | |
361 | inst.event = event; | |
362 | ||
363 | return cmdq_pkt_append_command(pkt, inst); | |
576f1b4b HW |
364 | } |
365 | EXPORT_SYMBOL(cmdq_pkt_clear_event); | |
366 | ||
7de796ca DYH |
367 | int cmdq_pkt_set_event(struct cmdq_pkt *pkt, u16 event) |
368 | { | |
369 | struct cmdq_instruction inst = {}; | |
370 | ||
371 | if (event >= CMDQ_MAX_EVENT) | |
372 | return -EINVAL; | |
373 | ||
374 | inst.op = CMDQ_CODE_WFE; | |
375 | inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE; | |
376 | inst.event = event; | |
377 | ||
378 | return cmdq_pkt_append_command(pkt, inst); | |
379 | } | |
380 | EXPORT_SYMBOL(cmdq_pkt_set_event); | |
381 | ||
b2ff2356 BH |
382 | int cmdq_pkt_poll(struct cmdq_pkt *pkt, u8 subsys, |
383 | u16 offset, u32 value) | |
384 | { | |
385 | struct cmdq_instruction inst = { {0} }; | |
386 | int err; | |
387 | ||
388 | inst.op = CMDQ_CODE_POLL; | |
389 | inst.value = value; | |
390 | inst.offset = offset; | |
391 | inst.subsys = subsys; | |
392 | err = cmdq_pkt_append_command(pkt, inst); | |
393 | ||
394 | return err; | |
395 | } | |
396 | EXPORT_SYMBOL(cmdq_pkt_poll); | |
397 | ||
398 | int cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys, | |
399 | u16 offset, u32 value, u32 mask) | |
400 | { | |
401 | struct cmdq_instruction inst = { {0} }; | |
402 | int err; | |
403 | ||
404 | inst.op = CMDQ_CODE_MASK; | |
405 | inst.mask = ~mask; | |
406 | err = cmdq_pkt_append_command(pkt, inst); | |
407 | if (err < 0) | |
408 | return err; | |
409 | ||
410 | offset = offset | CMDQ_POLL_ENABLE_MASK; | |
411 | err = cmdq_pkt_poll(pkt, subsys, offset, value); | |
412 | ||
413 | return err; | |
414 | } | |
415 | EXPORT_SYMBOL(cmdq_pkt_poll_mask); | |
416 | ||
400e2fa8 JJL |
417 | int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mask) |
418 | { | |
419 | struct cmdq_instruction inst = { {0} }; | |
420 | u8 use_mask = 0; | |
421 | int ret; | |
422 | ||
423 | /* | |
424 | * Append an MASK instruction to set the mask for following POLL instruction | |
425 | * which enables use_mask bit. | |
426 | */ | |
427 | if (mask != GENMASK(31, 0)) { | |
428 | inst.op = CMDQ_CODE_MASK; | |
429 | inst.mask = ~mask; | |
430 | ret = cmdq_pkt_append_command(pkt, inst); | |
431 | if (ret < 0) | |
432 | return ret; | |
433 | use_mask = CMDQ_POLL_ENABLE_MASK; | |
434 | } | |
435 | ||
436 | /* | |
437 | * POLL is an legacy operation in GCE and it does not support SPR and CMDQ_CODE_LOGIC, | |
438 | * so it can not use cmdq_pkt_assign to keep polling register address to SPR. | |
439 | * If user wants to poll a register address which doesn't have a subsys id, | |
440 | * user needs to use GPR and CMDQ_CODE_MASK to move polling register address to GPR. | |
441 | */ | |
442 | inst.op = CMDQ_CODE_MASK; | |
443 | inst.dst_t = CMDQ_REG_TYPE; | |
444 | inst.sop = CMDQ_POLL_ADDR_GPR; | |
445 | inst.value = addr; | |
446 | ret = cmdq_pkt_append_command(pkt, inst); | |
447 | if (ret < 0) | |
448 | return ret; | |
449 | ||
450 | /* Append POLL instruction to poll the register address assign to GPR previously. */ | |
451 | inst.op = CMDQ_CODE_POLL; | |
452 | inst.dst_t = CMDQ_REG_TYPE; | |
453 | inst.sop = CMDQ_POLL_ADDR_GPR; | |
454 | inst.offset = use_mask; | |
455 | inst.value = value; | |
456 | ret = cmdq_pkt_append_command(pkt, inst); | |
457 | if (ret < 0) | |
458 | return ret; | |
459 | ||
460 | return 0; | |
461 | } | |
462 | EXPORT_SYMBOL(cmdq_pkt_poll_addr); | |
463 | ||
613c2e2c DYH |
464 | int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value) |
465 | { | |
466 | struct cmdq_instruction inst = {}; | |
467 | ||
468 | inst.op = CMDQ_CODE_LOGIC; | |
469 | inst.dst_t = CMDQ_REG_TYPE; | |
470 | inst.reg_dst = reg_idx; | |
471 | inst.value = value; | |
472 | return cmdq_pkt_append_command(pkt, inst); | |
473 | } | |
474 | EXPORT_SYMBOL(cmdq_pkt_assign); | |
475 | ||
7218be3b | 476 | int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa) |
946f1792 DYH |
477 | { |
478 | struct cmdq_instruction inst = {}; | |
479 | ||
480 | inst.op = CMDQ_CODE_JUMP; | |
ed4d5ab1 | 481 | inst.offset = CMDQ_JUMP_ABSOLUTE; |
ade17653 | 482 | inst.value = addr >> shift_pa; |
946f1792 DYH |
483 | return cmdq_pkt_append_command(pkt, inst); |
484 | } | |
7218be3b | 485 | EXPORT_SYMBOL(cmdq_pkt_jump_abs); |
946f1792 | 486 | |
698cdcb1 CKH |
487 | int cmdq_pkt_jump_rel(struct cmdq_pkt *pkt, s32 offset, u8 shift_pa) |
488 | { | |
489 | struct cmdq_instruction inst = { {0} }; | |
490 | ||
491 | inst.op = CMDQ_CODE_JUMP; | |
492 | inst.value = (u32)offset >> shift_pa; | |
493 | return cmdq_pkt_append_command(pkt, inst); | |
494 | } | |
495 | EXPORT_SYMBOL(cmdq_pkt_jump_rel); | |
496 | ||
3d86ced9 CKH |
497 | int cmdq_pkt_eoc(struct cmdq_pkt *pkt) |
498 | { | |
499 | struct cmdq_instruction inst = { {0} }; | |
500 | ||
501 | inst.op = CMDQ_CODE_EOC; | |
502 | inst.value = CMDQ_EOC_IRQ_EN; | |
503 | return cmdq_pkt_append_command(pkt, inst); | |
504 | } | |
505 | EXPORT_SYMBOL(cmdq_pkt_eoc); | |
506 | ||
99581858 | 507 | int cmdq_pkt_finalize(struct cmdq_pkt *pkt) |
576f1b4b | 508 | { |
5c8b718c | 509 | struct cmdq_instruction inst = { {0} }; |
576f1b4b HW |
510 | int err; |
511 | ||
512 | /* insert EOC and generate IRQ for each command iteration */ | |
5c8b718c BH |
513 | inst.op = CMDQ_CODE_EOC; |
514 | inst.value = CMDQ_EOC_IRQ_EN; | |
515 | err = cmdq_pkt_append_command(pkt, inst); | |
01d1b408 BH |
516 | if (err < 0) |
517 | return err; | |
576f1b4b HW |
518 | |
519 | /* JUMP to end */ | |
5c8b718c | 520 | inst.op = CMDQ_CODE_JUMP; |
2b8cf383 DYH |
521 | inst.value = CMDQ_JUMP_PASS >> |
522 | cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan); | |
5c8b718c | 523 | err = cmdq_pkt_append_command(pkt, inst); |
576f1b4b HW |
524 | |
525 | return err; | |
526 | } | |
99581858 | 527 | EXPORT_SYMBOL(cmdq_pkt_finalize); |
576f1b4b | 528 | |
576f1b4b | 529 | MODULE_LICENSE("GPL v2"); |