1/*
2 * Copyright (C) 2017 Etnaviv Project
3 * Copyright (C) 2017 Zodiac Inflight Innovations
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * Authors:
25 *    Christian Gmeiner <christian.gmeiner@gmail.com>
26 */
27
28#include "etnaviv_priv.h"
29
30static int etna_perfmon_query_signals(struct etna_perfmon *pm, struct etna_perfmon_domain *dom)
31{
32	struct etna_device *dev = pm->pipe->gpu->dev;
33	struct drm_etnaviv_pm_signal req = {
34		.pipe = pm->pipe->id,
35		.domain = dom->id
36	};
37
38	do {
39		struct etna_perfmon_signal *sig;
40		int ret;
41
42		ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_PM_QUERY_SIG, &req, sizeof(req));
43		if (ret)
44			break;
45
46		sig = calloc(1, sizeof(*sig));
47		if (!sig)
48			return -ENOMEM;
49
50		INFO_MSG("perfmon signal:");
51		INFO_MSG("id         = %d", req.id);
52		INFO_MSG("name       = %s", req.name);
53
54		sig->domain = dom;
55		sig->signal = req.id;
56		strncpy(sig->name, req.name, sizeof(sig->name));
57		list_addtail(&sig->head, &dom->signals);
58	} while (req.iter != 0xffff);
59
60	return 0;
61}
62
63static int etna_perfmon_query_domains(struct etna_perfmon *pm)
64{
65	struct etna_device *dev = pm->pipe->gpu->dev;
66	struct drm_etnaviv_pm_domain req = {
67		.pipe = pm->pipe->id
68	};
69
70	do {
71		struct etna_perfmon_domain *dom;
72		int ret;
73
74		ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_PM_QUERY_DOM, &req, sizeof(req));
75		if (ret)
76			break;
77
78		dom = calloc(1, sizeof(*dom));
79		if (!dom)
80			return -ENOMEM;
81
82		list_inithead(&dom->signals);
83		dom->id = req.id;
84		strncpy(dom->name, req.name, sizeof(dom->name));
85		list_addtail(&dom->head, &pm->domains);
86
87		INFO_MSG("perfmon domain:");
88		INFO_MSG("id         = %d", req.id);
89		INFO_MSG("name       = %s", req.name);
90		INFO_MSG("nr_signals = %d", req.nr_signals);
91
92		/* Query all available signals for this domain. */
93		if (req.nr_signals > 0) {
94			ret = etna_perfmon_query_signals(pm, dom);
95			if (ret)
96				return ret;
97		}
98	} while (req.iter != 0xff);
99
100	return 0;
101}
102
103static void etna_perfmon_free_signals(struct etna_perfmon_domain *dom)
104{
105	struct etna_perfmon_signal *sig, *next;
106
107	LIST_FOR_EACH_ENTRY_SAFE(sig, next, &dom->signals, head) {
108		list_del(&sig->head);
109		free(sig);
110	}
111}
112
113static void etna_perfmon_free_domains(struct etna_perfmon *pm)
114{
115	struct etna_perfmon_domain *dom, *next;
116
117	LIST_FOR_EACH_ENTRY_SAFE(dom, next, &pm->domains, head) {
118		etna_perfmon_free_signals(dom);
119		list_del(&dom->head);
120		free(dom);
121	}
122}
123
124drm_public struct etna_perfmon *etna_perfmon_create(struct etna_pipe *pipe)
125{
126	struct etna_perfmon *pm;
127	int ret;
128
129	pm = calloc(1, sizeof(*pm));
130	if (!pm) {
131		ERROR_MSG("allocation failed");
132		return NULL;
133	}
134
135	list_inithead(&pm->domains);
136	pm->pipe = pipe;
137
138	/* query all available domains and sources for this device */
139	ret = etna_perfmon_query_domains(pm);
140	if (ret)
141		goto fail;
142
143	return pm;
144
145fail:
146	etna_perfmon_del(pm);
147	return NULL;
148}
149
150drm_public void etna_perfmon_del(struct etna_perfmon *pm)
151{
152	if (!pm)
153		return;
154
155	etna_perfmon_free_domains(pm);
156	free(pm);
157}
158
159drm_public struct etna_perfmon_domain *etna_perfmon_get_dom_by_name(struct etna_perfmon *pm, const char *name)
160{
161	struct etna_perfmon_domain *dom;
162
163	if (pm) {
164		LIST_FOR_EACH_ENTRY(dom, &pm->domains, head) {
165			if (!strcmp(dom->name, name))
166				return dom;
167		}
168	}
169
170	return NULL;
171}
172
173drm_public struct etna_perfmon_signal *etna_perfmon_get_sig_by_name(struct etna_perfmon_domain *dom, const char *name)
174{
175	struct etna_perfmon_signal *signal;
176
177	if (dom) {
178		LIST_FOR_EACH_ENTRY(signal, &dom->signals, head) {
179			if (!strcmp(signal->name, name))
180				return signal;
181		}
182	}
183
184	return NULL;
185}
186