blob: 138f4eace3bab0b8a97a3ae9b5ae31dc98ce4121 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14/* #define DEBUG */
15#define ALIGN_CPU
16
17#include <linux/spinlock.h>
18#include <linux/debugfs.h>
19#include <linux/relay.h>
20#include <linux/slab.h>
21#include <linux/time.h>
22#include <linux/sched.h>
23
24#include "kgsl.h"
25#include "kgsl_cffdump.h"
26#include "kgsl_debugfs.h"
27
28static struct rchan *chan;
29static struct dentry *dir;
30static int suspended;
31static size_t dropped;
32static size_t subbuf_size = 256*1024;
33static size_t n_subbufs = 64;
34
35/* forward declarations */
36static void destroy_channel(void);
37static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs);
38
39static spinlock_t cffdump_lock;
40static ulong serial_nr;
41static ulong total_bytes;
42static ulong total_syncmem;
43static long last_sec;
44
45#define MEMBUF_SIZE 64
46
47#define CFF_OP_WRITE_REG 0x00000002
48struct cff_op_write_reg {
49 unsigned char op;
50 uint addr;
51 uint value;
52} __attribute__((packed));
53
54#define CFF_OP_POLL_REG 0x00000004
55struct cff_op_poll_reg {
56 unsigned char op;
57 uint addr;
58 uint value;
59 uint mask;
60} __attribute__((packed));
61
62#define CFF_OP_WAIT_IRQ 0x00000005
63struct cff_op_wait_irq {
64 unsigned char op;
65} __attribute__((packed));
66
67#define CFF_OP_VERIFY_MEM_FILE 0x00000007
68#define CFF_OP_RMW 0x0000000a
69
70#define CFF_OP_WRITE_MEM 0x0000000b
71struct cff_op_write_mem {
72 unsigned char op;
73 uint addr;
74 uint value;
75} __attribute__((packed));
76
77#define CFF_OP_WRITE_MEMBUF 0x0000000c
78struct cff_op_write_membuf {
79 unsigned char op;
80 uint addr;
81 ushort count;
82 uint buffer[MEMBUF_SIZE];
83} __attribute__((packed));
84
85#define CFF_OP_EOF 0xffffffff
86struct cff_op_eof {
87 unsigned char op;
88} __attribute__((packed));
89
90
91static void b64_encodeblock(unsigned char in[3], unsigned char out[4], int len)
92{
93 static const char tob64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno"
94 "pqrstuvwxyz0123456789+/";
95
96 out[0] = tob64[in[0] >> 2];
97 out[1] = tob64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
98 out[2] = (unsigned char) (len > 1 ? tob64[((in[1] & 0x0f) << 2)
99 | ((in[2] & 0xc0) >> 6)] : '=');
100 out[3] = (unsigned char) (len > 2 ? tob64[in[2] & 0x3f] : '=');
101}
102
103static void b64_encode(const unsigned char *in_buf, int in_size,
104 unsigned char *out_buf, int out_bufsize, int *out_size)
105{
106 unsigned char in[3], out[4];
107 int i, len;
108
109 *out_size = 0;
110 while (in_size > 0) {
111 len = 0;
112 for (i = 0; i < 3; ++i) {
113 if (in_size-- > 0) {
114 in[i] = *in_buf++;
115 ++len;
116 } else
117 in[i] = 0;
118 }
119 if (len) {
120 b64_encodeblock(in, out, len);
121 if (out_bufsize < 4) {
122 pr_warn("kgsl: cffdump: %s: out of buffer\n",
123 __func__);
124 return;
125 }
126 for (i = 0; i < 4; ++i)
127 *out_buf++ = out[i];
128 *out_size += 4;
129 out_bufsize -= 4;
130 }
131 }
132}
133
134#define KLOG_TMPBUF_SIZE (1024)
135static void klog_printk(const char *fmt, ...)
136{
137 /* per-cpu klog formatting temporary buffer */
138 static char klog_buf[NR_CPUS][KLOG_TMPBUF_SIZE];
139
140 va_list args;
141 int len;
142 char *cbuf;
143 unsigned long flags;
144
145 local_irq_save(flags);
146 cbuf = klog_buf[smp_processor_id()];
147 va_start(args, fmt);
148 len = vsnprintf(cbuf, KLOG_TMPBUF_SIZE, fmt, args);
149 total_bytes += len;
150 va_end(args);
151 relay_write(chan, cbuf, len);
152 local_irq_restore(flags);
153}
154
155static struct cff_op_write_membuf cff_op_write_membuf;
156static void cffdump_membuf(int id, unsigned char *out_buf, int out_bufsize)
157{
158 void *data;
159 int len, out_size;
160 struct cff_op_write_mem cff_op_write_mem;
161
162 uint addr = cff_op_write_membuf.addr
163 - sizeof(uint)*cff_op_write_membuf.count;
164
165 if (!cff_op_write_membuf.count) {
166 pr_warn("kgsl: cffdump: membuf: count == 0, skipping");
167 return;
168 }
169
170 if (cff_op_write_membuf.count != 1) {
171 cff_op_write_membuf.op = CFF_OP_WRITE_MEMBUF;
172 cff_op_write_membuf.addr = addr;
173 len = sizeof(cff_op_write_membuf) -
174 sizeof(uint)*(MEMBUF_SIZE - cff_op_write_membuf.count);
175 data = &cff_op_write_membuf;
176 } else {
177 cff_op_write_mem.op = CFF_OP_WRITE_MEM;
178 cff_op_write_mem.addr = addr;
179 cff_op_write_mem.value = cff_op_write_membuf.buffer[0];
180 data = &cff_op_write_mem;
181 len = sizeof(cff_op_write_mem);
182 }
183 b64_encode(data, len, out_buf, out_bufsize, &out_size);
184 out_buf[out_size] = 0;
185 klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
186 cff_op_write_membuf.count = 0;
187 cff_op_write_membuf.addr = 0;
188}
189
190static void cffdump_printline(int id, uint opcode, uint op1, uint op2,
191 uint op3)
192{
193 struct cff_op_write_reg cff_op_write_reg;
194 struct cff_op_poll_reg cff_op_poll_reg;
195 struct cff_op_wait_irq cff_op_wait_irq;
196 struct cff_op_eof cff_op_eof;
197 unsigned char out_buf[sizeof(cff_op_write_membuf)/3*4 + 16];
198 void *data;
199 int len = 0, out_size;
200 long cur_secs;
201
202 spin_lock(&cffdump_lock);
203 if (opcode == CFF_OP_WRITE_MEM) {
204 if (op1 < 0x40000000 || op1 >= 0x60000000)
205 KGSL_CORE_ERR("addr out-of-range: op1=%08x", op1);
206 if ((cff_op_write_membuf.addr != op1 &&
207 cff_op_write_membuf.count)
208 || (cff_op_write_membuf.count == MEMBUF_SIZE))
209 cffdump_membuf(id, out_buf, sizeof(out_buf));
210
211 cff_op_write_membuf.buffer[cff_op_write_membuf.count++] = op2;
212 cff_op_write_membuf.addr = op1 + sizeof(uint);
213 spin_unlock(&cffdump_lock);
214 return;
215 } else if (cff_op_write_membuf.count)
216 cffdump_membuf(id, out_buf, sizeof(out_buf));
217 spin_unlock(&cffdump_lock);
218
219 switch (opcode) {
220 case CFF_OP_WRITE_REG:
221 cff_op_write_reg.op = opcode;
222 cff_op_write_reg.addr = op1;
223 cff_op_write_reg.value = op2;
224 data = &cff_op_write_reg;
225 len = sizeof(cff_op_write_reg);
226 break;
227
228 case CFF_OP_POLL_REG:
229 cff_op_poll_reg.op = opcode;
230 cff_op_poll_reg.addr = op1;
231 cff_op_poll_reg.value = op2;
232 cff_op_poll_reg.mask = op3;
233 data = &cff_op_poll_reg;
234 len = sizeof(cff_op_poll_reg);
235 break;
236
237 case CFF_OP_WAIT_IRQ:
238 cff_op_wait_irq.op = opcode;
239 data = &cff_op_wait_irq;
240 len = sizeof(cff_op_wait_irq);
241 break;
242
243 case CFF_OP_EOF:
244 cff_op_eof.op = opcode;
245 data = &cff_op_eof;
246 len = sizeof(cff_op_eof);
247 break;
248 }
249
250 if (len) {
251 b64_encode(data, len, out_buf, sizeof(out_buf), &out_size);
252 out_buf[out_size] = 0;
253 klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf);
254 } else
255 pr_warn("kgsl: cffdump: unhandled opcode: %d\n", opcode);
256
257 cur_secs = get_seconds();
258 if ((cur_secs - last_sec) > 10 || (last_sec - cur_secs) > 10) {
259 pr_info("kgsl: cffdump: total [bytes:%lu kB, syncmem:%lu kB], "
260 "seq#: %lu\n", total_bytes/1024, total_syncmem/1024,
261 serial_nr);
262 last_sec = cur_secs;
263 }
264}
265
266void kgsl_cffdump_init()
267{
268 struct dentry *debugfs_dir = kgsl_get_debugfs_dir();
269
270#ifdef ALIGN_CPU
271 cpumask_t mask;
272
273 cpumask_clear(&mask);
Lucille Sylvester57996e32011-05-26 19:12:22 -0600274 cpumask_set_cpu(0, &mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 sched_setaffinity(0, &mask);
276#endif
277 if (!debugfs_dir || IS_ERR(debugfs_dir)) {
278 KGSL_CORE_ERR("Debugfs directory is bad\n");
279 return;
280 }
281
282 kgsl_cff_dump_enable = 1;
283
284 spin_lock_init(&cffdump_lock);
285
286 dir = debugfs_create_dir("cff", debugfs_dir);
287 if (!dir) {
288 KGSL_CORE_ERR("debugfs_create_dir failed\n");
289 return;
290 }
291
292 chan = create_channel(subbuf_size, n_subbufs);
293}
294
295void kgsl_cffdump_destroy()
296{
297 if (chan)
298 relay_flush(chan);
299 destroy_channel();
300 if (dir)
301 debugfs_remove(dir);
302}
303
304void kgsl_cffdump_open(enum kgsl_deviceid device_id)
305{
306}
307
308void kgsl_cffdump_close(enum kgsl_deviceid device_id)
309{
310 cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0);
311}
312
313void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv,
314 const struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes,
315 bool clean_cache)
316{
317 const void *src;
318 uint host_size;
319 uint physaddr;
320
321 if (!kgsl_cff_dump_enable)
322 return;
323
324 total_syncmem += sizebytes;
325
326 if (memdesc == NULL) {
327 struct kgsl_mem_entry *entry;
328 spin_lock(&dev_priv->process_priv->mem_lock);
329 entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
330 gpuaddr, sizebytes);
331 spin_unlock(&dev_priv->process_priv->mem_lock);
332 if (entry == NULL) {
333 KGSL_CORE_ERR("did not find mapping "
334 "for gpuaddr: 0x%08x\n", gpuaddr);
335 return;
336 }
337 memdesc = &entry->memdesc;
338 }
339 BUG_ON(memdesc->gpuaddr == 0);
340 BUG_ON(gpuaddr == 0);
341 physaddr = kgsl_get_realaddr(memdesc) + (gpuaddr - memdesc->gpuaddr);
342
343 src = kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size);
344 if (src == NULL || host_size < sizebytes) {
345 KGSL_CORE_ERR(("did not find mapping for "
346 "gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n",
347 gpuaddr, memdesc->hostptr, memdesc->physaddr);
348 return;
349 }
350
351 if (clean_cache) {
352 /* Ensure that this memory region is not read from the
353 * cache but fetched fresh */
354
355 mb();
356
357 kgsl_cache_range_op(memdesc->hostptr, memdesc->size,
358 memdesc->type, KGSL_CACHE_OP_INV);
359 }
360
361 BUG_ON(physaddr > 0x66000000 && physaddr < 0x66ffffff);
362 while (sizebytes > 3) {
363 cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src,
364 0);
365 physaddr += 4;
366 src += 4;
367 sizebytes -= 4;
368 }
369 if (sizebytes > 0)
370 cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src,
371 0);
372}
373
374void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes)
375{
376 if (!kgsl_cff_dump_enable)
377 return;
378
379 BUG_ON(addr > 0x66000000 && addr < 0x66ffffff);
380 while (sizebytes > 3) {
381 /* Use 32bit memory writes as long as there's at least
382 * 4 bytes left */
383 cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0);
384 addr += 4;
385 sizebytes -= 4;
386 }
387 if (sizebytes > 0)
388 cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0);
389}
390
391void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr,
392 uint value)
393{
394 if (!kgsl_cff_dump_enable)
395 return;
396
397 cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value, 0);
398}
399
400void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr,
401 uint value, uint mask)
402{
403 if (!kgsl_cff_dump_enable)
404 return;
405
406 cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value, mask);
407}
408
409void kgsl_cffdump_slavewrite(uint addr, uint value)
410{
411 if (!kgsl_cff_dump_enable)
412 return;
413
414 cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0);
415}
416
417int kgsl_cffdump_waitirq(void)
418{
419 if (!kgsl_cff_dump_enable)
420 return 0;
421
422 cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0);
423
424 return 1;
425}
426EXPORT_SYMBOL(kgsl_cffdump_waitirq);
427
428#define ADDRESS_STACK_SIZE 256
429#define GET_PM4_TYPE3_OPCODE(x) ((*(x) >> 8) & 0xFF)
430static unsigned int kgsl_cffdump_addr_count;
431
432static bool kgsl_cffdump_handle_type3(struct kgsl_device_private *dev_priv,
433 uint *hostaddr, bool check_only)
434{
435 static uint addr_stack[ADDRESS_STACK_SIZE];
436 static uint size_stack[ADDRESS_STACK_SIZE];
437
438 switch (GET_PM4_TYPE3_OPCODE(hostaddr)) {
439 case PM4_INDIRECT_BUFFER_PFD:
440 case PM4_INDIRECT_BUFFER:
441 {
442 /* traverse indirect buffers */
443 int i;
444 uint ibaddr = hostaddr[1];
445 uint ibsize = hostaddr[2];
446
447 /* is this address already in encountered? */
448 for (i = 0;
449 i < kgsl_cffdump_addr_count && addr_stack[i] != ibaddr;
450 ++i)
451 ;
452
453 if (kgsl_cffdump_addr_count == i) {
454 addr_stack[kgsl_cffdump_addr_count] = ibaddr;
455 size_stack[kgsl_cffdump_addr_count++] = ibsize;
456
457 if (kgsl_cffdump_addr_count >= ADDRESS_STACK_SIZE) {
458 KGSL_CORE_ERR("stack overflow\n");
459 return false;
460 }
461
462 return kgsl_cffdump_parse_ibs(dev_priv, NULL,
463 ibaddr, ibsize, check_only);
464 } else if (size_stack[i] != ibsize) {
465 KGSL_CORE_ERR("gpuaddr: 0x%08x, "
466 "wc: %u, with size wc: %u already on the "
467 "stack\n", ibaddr, ibsize, size_stack[i]);
468 return false;
469 }
470 }
471 break;
472 }
473
474 return true;
475}
476
477/*
478 * Traverse IBs and dump them to test vector. Detect swap by inspecting
479 * register writes, keeping note of the current state, and dump
480 * framebuffer config to test vector
481 */
482bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv,
483 const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords,
484 bool check_only)
485{
486 static uint level; /* recursion level */
487 bool ret = true;
488 uint host_size;
489 uint *hostaddr, *hoststart;
490 int dwords_left = sizedwords; /* dwords left in the current command
491 buffer */
492
493 if (level == 0)
494 kgsl_cffdump_addr_count = 0;
495
496 if (memdesc == NULL) {
497 struct kgsl_mem_entry *entry;
498 spin_lock(&dev_priv->process_priv->mem_lock);
499 entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
500 gpuaddr, sizedwords * sizeof(uint));
501 spin_unlock(&dev_priv->process_priv->mem_lock);
502 if (entry == NULL) {
503 KGSL_CORE_ERR("did not find mapping "
504 "for gpuaddr: 0x%08x\n", gpuaddr);
505 return true;
506 }
507 memdesc = &entry->memdesc;
508 }
509
510 hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size);
511 if (hostaddr == NULL) {
512 KGSL_CORE_ERR("did not find mapping for "
513 "gpuaddr: 0x%08x\n", gpuaddr);
514 return true;
515 }
516
517 hoststart = hostaddr;
518
519 level++;
520
521 if (!memdesc->physaddr) {
522 KGSL_CORE_ERR("no physaddr");
523 return true;
524 } else {
525 mb();
526 kgsl_cache_range_op(memdesc->hostptr, memdesc->size,
527 memdesc->type, KGSL_CACHE_OP_INV);
528 }
529
530#ifdef DEBUG
531 pr_info("kgsl: cffdump: ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n",
532 gpuaddr, sizedwords, hostaddr);
533#endif
534
535 while (dwords_left > 0) {
536 int count = 0; /* dword count including packet header */
537 bool cur_ret = true;
538
539 switch (*hostaddr >> 30) {
540 case 0x0: /* type-0 */
541 count = (*hostaddr >> 16)+2;
542 break;
543 case 0x1: /* type-1 */
544 count = 2;
545 break;
546 case 0x3: /* type-3 */
547 count = ((*hostaddr >> 16) & 0x3fff) + 2;
548 cur_ret = kgsl_cffdump_handle_type3(dev_priv,
549 hostaddr, check_only);
550 break;
551 default:
552 pr_warn("kgsl: cffdump: parse-ib: unexpected type: "
553 "type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n",
554 *hostaddr >> 30, *hostaddr, hostaddr,
555 gpuaddr+4*(sizedwords-dwords_left));
556 cur_ret = false;
557 count = dwords_left;
558 break;
559 }
560
561#ifdef DEBUG
562 if (!cur_ret) {
563 pr_info("kgsl: cffdump: bad sub-type: #:%d/%d, v:0x%08x"
564 " @ 0x%p[gb:0x%08x], level:%d\n",
565 sizedwords-dwords_left, sizedwords, *hostaddr,
566 hostaddr, gpuaddr+4*(sizedwords-dwords_left),
567 level);
568
569 print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
570 DUMP_PREFIX_OFFSET, 32, 4, hoststart,
571 sizedwords*4, 0);
572 }
573#endif
574 ret = ret && cur_ret;
575
576 /* jump to next packet */
577 dwords_left -= count;
578 hostaddr += count;
579 cur_ret = dwords_left >= 0;
580
581#ifdef DEBUG
582 if (!cur_ret) {
583 pr_info("kgsl: cffdump: bad count: c:%d, #:%d/%d, "
584 "v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n",
585 count, sizedwords-(dwords_left+count),
586 sizedwords, *(hostaddr-count), hostaddr-count,
587 gpuaddr+4*(sizedwords-(dwords_left+count)),
588 level);
589
590 print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
591 DUMP_PREFIX_OFFSET, 32, 4, hoststart,
592 sizedwords*4, 0);
593 }
594#endif
595
596 ret = ret && cur_ret;
597 }
598
599 if (!ret)
600 pr_info("kgsl: cffdump: parsing failed: gpuaddr:0x%08x, "
601 "host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords);
602
603 if (!check_only) {
604#ifdef DEBUG
605 uint offset = gpuaddr - memdesc->gpuaddr;
606 pr_info("kgsl: cffdump: ib-dump: hostptr:%p, gpuaddr:%08x, "
607 "physaddr:%08x, offset:%d, size:%d", hoststart,
608 gpuaddr, memdesc->physaddr + offset, offset,
609 sizedwords*4);
610#endif
611 kgsl_cffdump_syncmem(dev_priv, memdesc, gpuaddr, sizedwords*4,
612 false);
613 }
614
615 level--;
616
617 return ret;
618}
619
620static int subbuf_start_handler(struct rchan_buf *buf,
621 void *subbuf, void *prev_subbuf, uint prev_padding)
622{
623 pr_debug("kgsl: cffdump: subbuf_start_handler(subbuf=%p, prev_subbuf"
624 "=%p, prev_padding=%08x)\n", subbuf, prev_subbuf, prev_padding);
625
626 if (relay_buf_full(buf)) {
627 if (!suspended) {
628 suspended = 1;
629 pr_warn("kgsl: cffdump: relay: cpu %d buffer full!!!\n",
630 smp_processor_id());
631 }
632 dropped++;
633 return 0;
634 } else if (suspended) {
635 suspended = 0;
636 pr_warn("kgsl: cffdump: relay: cpu %d buffer no longer full.\n",
637 smp_processor_id());
638 }
639
640 subbuf_start_reserve(buf, 0);
641 return 1;
642}
643
644static struct dentry *create_buf_file_handler(const char *filename,
645 struct dentry *parent, int mode, struct rchan_buf *buf,
646 int *is_global)
647{
648 return debugfs_create_file(filename, mode, parent, buf,
649 &relay_file_operations);
650}
651
652/*
653 * file_remove() default callback. Removes relay file in debugfs.
654 */
655static int remove_buf_file_handler(struct dentry *dentry)
656{
657 pr_info("kgsl: cffdump: %s()\n", __func__);
658 debugfs_remove(dentry);
659 return 0;
660}
661
662/*
663 * relay callbacks
664 */
665static struct rchan_callbacks relay_callbacks = {
666 .subbuf_start = subbuf_start_handler,
667 .create_buf_file = create_buf_file_handler,
668 .remove_buf_file = remove_buf_file_handler,
669};
670
671/**
672 * create_channel - creates channel /debug/klog/cpuXXX
673 *
674 * Creates channel along with associated produced/consumed control files
675 *
676 * Returns channel on success, NULL otherwise
677 */
678static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs)
679{
680 struct rchan *chan;
681
682 pr_info("kgsl: cffdump: relay: create_channel: subbuf_size %u, "
683 "n_subbufs %u, dir 0x%p\n", subbuf_size, n_subbufs, dir);
684
685 chan = relay_open("cpu", dir, subbuf_size,
686 n_subbufs, &relay_callbacks, NULL);
687 if (!chan) {
688 KGSL_CORE_ERR("relay_open failed\n");
689 return NULL;
690 }
691
692 suspended = 0;
693 dropped = 0;
694
695 return chan;
696}
697
698/**
699 * destroy_channel - destroys channel /debug/kgsl/cff/cpuXXX
700 *
701 * Destroys channel along with associated produced/consumed control files
702 */
703static void destroy_channel(void)
704{
705 pr_info("kgsl: cffdump: relay: destroy_channel\n");
706 if (chan) {
707 relay_close(chan);
708 chan = NULL;
709 }
710}
711