1/*
2 * Copyright 2007 Nouveau Project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#include <stdlib.h>
24#include <errno.h>
25#include <sys/time.h>
26
27#include "nouveau_private.h"
28
29#define NOTIFIER(__v)                                                          \
30	struct nouveau_notifier_priv *nvnotify = nouveau_notifier(notifier);   \
31	volatile uint32_t *__v = (uint32_t *)((char *)nvnotify->map + (id * 32))
32
33int
34nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
35		       int count, struct nouveau_notifier **notifier)
36{
37	struct nouveau_notifier_priv *nvnotify;
38	int ret;
39
40	if (!chan || !notifier || *notifier)
41		return -EINVAL;
42
43	nvnotify = calloc(1, sizeof(struct nouveau_notifier_priv));
44	if (!nvnotify)
45		return -ENOMEM;
46	nvnotify->base.channel = chan;
47	nvnotify->base.handle  = handle;
48
49	nvnotify->drm.channel = chan->id;
50	nvnotify->drm.handle  = handle;
51	nvnotify->drm.count   = count;
52	if ((ret = drmCommandWriteRead(nouveau_device(chan->device)->fd,
53				       DRM_NOUVEAU_NOTIFIEROBJ_ALLOC,
54				       &nvnotify->drm,
55				       sizeof(nvnotify->drm)))) {
56		nouveau_notifier_free((void *)&nvnotify);
57		return ret;
58	}
59
60	nvnotify->map = (char *)nouveau_channel(chan)->notifier_block +
61				nvnotify->drm.offset;
62	*notifier = &nvnotify->base;
63	return 0;
64}
65
66void
67nouveau_notifier_free(struct nouveau_notifier **notifier)
68{
69
70	struct nouveau_notifier_priv *nvnotify;
71	struct nouveau_channel_priv *nvchan;
72	struct nouveau_device_priv *nvdev;
73	struct drm_nouveau_gpuobj_free f;
74
75	if (!notifier || !*notifier)
76		return;
77	nvnotify = nouveau_notifier(*notifier);
78	*notifier = NULL;
79
80	nvchan = nouveau_channel(nvnotify->base.channel);
81	nvdev   = nouveau_device(nvchan->base.device);
82
83	f.channel = nvchan->drm.channel;
84	f.handle  = nvnotify->base.handle;
85	drmCommandWrite(nvdev->fd, DRM_NOUVEAU_GPUOBJ_FREE, &f, sizeof(f));
86	free(nvnotify);
87}
88
89void
90nouveau_notifier_reset(struct nouveau_notifier *notifier, int id)
91{
92	NOTIFIER(n);
93
94	n[NV_NOTIFY_TIME_0      /4] = 0x00000000;
95	n[NV_NOTIFY_TIME_1      /4] = 0x00000000;
96	n[NV_NOTIFY_RETURN_VALUE/4] = 0x00000000;
97	n[NV_NOTIFY_STATE       /4] = (NV_NOTIFY_STATE_STATUS_IN_PROCESS <<
98				       NV_NOTIFY_STATE_STATUS_SHIFT);
99}
100
101uint32_t
102nouveau_notifier_status(struct nouveau_notifier *notifier, int id)
103{
104	NOTIFIER(n);
105
106	return n[NV_NOTIFY_STATE/4] >> NV_NOTIFY_STATE_STATUS_SHIFT;
107}
108
109uint32_t
110nouveau_notifier_return_val(struct nouveau_notifier *notifier, int id)
111{
112	NOTIFIER(n);
113
114	return n[NV_NOTIFY_RETURN_VALUE/4];
115}
116
117static inline double
118gettime(void)
119{
120	struct timeval tv;
121
122	gettimeofday(&tv, NULL);
123	return (double)tv.tv_sec + tv.tv_usec / 1000000.0;
124}
125
126int
127nouveau_notifier_wait_status(struct nouveau_notifier *notifier, int id,
128			     uint32_t status, double timeout)
129{
130	NOTIFIER(n);
131	double time = 0, t_start = gettime();
132
133	while (time <= timeout) {
134		uint32_t v;
135
136		v = n[NV_NOTIFY_STATE/4] >> NV_NOTIFY_STATE_STATUS_SHIFT;
137		if (v == status)
138			return 0;
139
140		if (timeout)
141			time = gettime() - t_start;
142	}
143
144	return -EBUSY;
145}
146
147