#include <err.h>
#include <fcntl.h>
#include <ftw.h>
+#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <curses.h>
#include <libconfig.h>
+#include <inttypes.h>
+#include <xf86drm.h>
#include "drm/freedreno_drmif.h"
#include "drm/freedreno_ringbuffer.h"
static void config_save(void);
static void config_restore(void);
+static void restore_counter_groups(void);
/*
* helpers
if (ret < 0) {
free(buf);
*sz = 0;
+ close(fd);
return NULL;
} else if (ret < CHUNKSIZE) {
n += ret;
*sz = n;
+ close(fd);
return buf;
} else {
n += CHUNKSIZE;
return b - a;
}
-/*
- * TODO de-duplicate OUT_RING() and friends
- */
-
-#define CP_WAIT_FOR_IDLE 38
-#define CP_TYPE0_PKT 0x00000000
-#define CP_TYPE3_PKT 0xc0000000
-#define CP_TYPE4_PKT 0x40000000
-#define CP_TYPE7_PKT 0x70000000
-
-static inline void
-OUT_RING(struct fd_ringbuffer *ring, uint32_t data)
-{
- *(ring->cur++) = data;
-}
-
-static inline void
-OUT_PKT0(struct fd_ringbuffer *ring, uint16_t regindx, uint16_t cnt)
-{
- OUT_RING(ring, CP_TYPE0_PKT | ((cnt-1) << 16) | (regindx & 0x7FFF));
-}
-
-static inline void
-OUT_PKT3(struct fd_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
-{
- OUT_RING(ring, CP_TYPE3_PKT | ((cnt-1) << 16) | ((opcode & 0xFF) << 8));
-}
-
-
-/*
- * Starting with a5xx, pkt4/pkt7 are used instead of pkt0/pkt3
- */
-
-static inline unsigned
-_odd_parity_bit(unsigned val)
-{
- /* See: http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
- * note that we want odd parity so 0x6996 is inverted.
- */
- val ^= val >> 16;
- val ^= val >> 8;
- val ^= val >> 4;
- val &= 0xf;
- return (~0x6996 >> val) & 1;
-}
-
-static inline void
-OUT_PKT4(struct fd_ringbuffer *ring, uint16_t regindx, uint16_t cnt)
-{
- OUT_RING(ring, CP_TYPE4_PKT | cnt |
- (_odd_parity_bit(cnt) << 7) |
- ((regindx & 0x3ffff) << 8) |
- ((_odd_parity_bit(regindx) << 27)));
-}
-
-static inline void
-OUT_PKT7(struct fd_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
-{
- OUT_RING(ring, CP_TYPE7_PKT | cnt |
- (_odd_parity_bit(cnt) << 15) |
- ((opcode & 0x7f) << 16) |
- ((_odd_parity_bit(opcode) << 23)));
-}
-
/*
* code to find stuff in /proc/device-tree:
*
void *buf;
int sz;
- asprintf(&path, "%s/%s", dev.dtnode, node);
+ (void) asprintf(&path, "%s/%s", dev.dtnode, node);
buf = readfile(path, &sz);
free(path);
dev.min_freq = ~0;
dev.max_freq = 0;
- asprintf(&path, "%s/%s", dev.dtnode, "qcom,gpu-pwrlevels");
+ (void) asprintf(&path, "%s/%s", dev.dtnode, "qcom,gpu-pwrlevels");
ret = nftw(path, find_freqs_fn, 64, 0);
if (ret < 0)
free(path);
}
+static const char * compatibles[] = {
+ "qcom,adreno-3xx",
+ "qcom,kgsl-3d0",
+ "amd,imageon",
+ "qcom,adreno",
+};
+
+/**
+ * compatstrs is a list of compatible strings separated by null, ie.
+ *
+ * compatible = "qcom,adreno-630.2", "qcom,adreno";
+ *
+ * would result in "qcom,adreno-630.2\0qcom,adreno\0"
+ */
+static bool match_compatible(char *compatstrs, int sz)
+{
+ while (sz > 0) {
+ char *compatible = compatstrs;
+
+ for (unsigned i = 0; i < ARRAY_SIZE(compatibles); i++) {
+ if (strcmp(compatible, compatibles[i]) == 0) {
+ return true;
+ }
+ }
+
+ compatstrs += strlen(compatible) + 1;
+ sz -= strlen(compatible) + 1;
+ }
+ return false;
+}
+
static int
find_device_fn(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
if (strcmp(fname, "compatible") == 0) {
char *str = readfile(fpath, &sz);
- if ((strcmp(str, "qcom,adreno-3xx") == 0) ||
- (strcmp(str, "qcom,kgsl-3d0") == 0) ||
- (strstr(str, "qcom,adreno") == str)) {
+ if (match_compatible(str, sz)) {
int dlen = strlen(fpath) - strlen("/compatible");
dev.dtnode = malloc(dlen + 1);
memcpy(dev.dtnode, fpath, dlen);
if (!dev.dtnode)
errx(1, "could not find qcom,adreno-3xx node");
- fd = open("/dev/dri/card0", O_RDWR);
+ fd = drmOpenWithType("msm", NULL, DRM_NODE_RENDER);
if (fd < 0)
err(1, "could not open drm device");
free(b);
- printf("i/o region at %08lx (size: %x)\n", dev.base, dev.size);
+ printf("i/o region at %08"PRIx64" (size: %x)\n", dev.base, dev.size);
/* try MAX_FREQ first as that will work regardless of old dt
* dt bindings vs upstream bindings:
err(1, "could not open /dev/mem");
dev.io = mmap(0, dev.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, dev.base);
- if (!dev.io)
+ if (dev.io == MAP_FAILED) {
+ close(fd);
err(1, "could not map device");
+ }
}
/*
*/
struct fd_ringbuffer *ring = dev.ring;
switch (dev.chipid >> 24) {
+ case 2:
case 3:
case 4:
OUT_PKT3(ring, CP_WAIT_FOR_IDLE, 1);
redraw_counter_value_raw(WINDOW *win, float val)
{
char *str;
- asprintf(&str, "%'.2f", val);
+ (void) asprintf(&str, "%'.2f", val);
waddstr(win, str);
whline(win, ' ', w - getcurx(win));
free(str);
main_ui(void)
{
WINDOW *mainwin;
+ uint32_t last_time = gettime_us();
/* curses setup: */
mainwin = initscr();
}
resample();
redraw(mainwin);
+
+ /* restore the counters every 0.5s in case the GPU has suspended,
+ * in which case the current selected countables will have reset:
+ */
+ uint32_t t = gettime_us();
+ if (delta(last_time, t) > 500000) {
+ restore_counter_groups();
+ flush_ring();
+ last_time = t;
+ }
}
/* restore settings.. maybe we need an atexit()??*/
refresh();
}
+static void
+restore_counter_groups(void)
+{
+ for (unsigned i = 0; i < dev.ngroups; i++) {
+ struct counter_group *group = &dev.groups[i];
+ unsigned j = 0;
+
+ /* NOTE skip CP the first CP counter */
+ if (i == 0)
+ j++;
+
+ for (; j < group->group->num_counters; j++) {
+ select_counter(group, j, group->counter[j].select_val);
+ }
+ }
+}
+
static void
setup_counter_groups(const struct fd_perfcntr_group *groups)
{
group->counter[j].val_hi = dev.io + (group->counter[j].counter->counter_reg_hi * 4);
group->counter[j].val_lo = dev.io + (group->counter[j].counter->counter_reg_lo * 4);
- select_counter(group, j, j);
+ group->counter[j].select_val = j;
}
for (unsigned j = 0; j < group->group->num_countables; j++) {
config_setting_t *root = config_root_setting(&cfg);
/* per device settings: */
- asprintf(&str, "a%dxx", dev.chipid >> 24);
+ (void) asprintf(&str, "a%dxx", dev.chipid >> 24);
setting = config_setting_get_member(root, str);
if (!setting)
setting = config_setting_add(root, str, CONFIG_TYPE_GROUP);
dev.groups = calloc(dev.ngroups, sizeof(struct counter_group));
+ setlocale(LC_NUMERIC, "en_US.UTF-8");
+
setup_counter_groups(groups);
+ restore_counter_groups();
config_restore();
flush_ring();