1 /*
2  * Copyright 2021 Red Hat Inc.
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 COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  */
22 #define nvkm_uhead(p) container_of((p), struct nvkm_head, object)
23 #include "head.h"
24 #include <core/event.h>
25 
26 #include <nvif/if0013.h>
27 
28 #include <nvif/event.h>
29 
30 static int
31 nvkm_uhead_uevent(struct nvkm_object *object, void *argv, u32 argc, struct nvkm_uevent *uevent)
32 {
33 	struct nvkm_head *head = nvkm_uhead(object);
34 	union nvif_head_event_args *args = argv;
35 
36 	if (!uevent)
37 		return 0;
38 	if (argc != sizeof(args->vn))
39 		return -ENOSYS;
40 
41 	return nvkm_uevent_add(uevent, &head->disp->vblank, head->id,
42 			       NVKM_DISP_HEAD_EVENT_VBLANK, NULL);
43 }
44 
45 static int
46 nvkm_uhead_mthd_scanoutpos(struct nvkm_head *head, void *argv, u32 argc)
47 {
48 	union nvif_head_scanoutpos_args *args = argv;
49 
50 	if (argc != sizeof(args->v0) || args->v0.version != 0)
51 		return -ENOSYS;
52 
53 	head->func->state(head, &head->arm);
54 	args->v0.vtotal  = head->arm.vtotal;
55 	args->v0.vblanks = head->arm.vblanks;
56 	args->v0.vblanke = head->arm.vblanke;
57 	args->v0.htotal  = head->arm.htotal;
58 	args->v0.hblanks = head->arm.hblanks;
59 	args->v0.hblanke = head->arm.hblanke;
60 
61 	/* We don't support reading htotal/vtotal on pre-NV50 VGA,
62 	 * so we have to give up and trigger the timestamping
63 	 * fallback in the drm core.
64 	 */
65 	if (!args->v0.vtotal || !args->v0.htotal)
66 		return -ENOTSUPP;
67 
68 	args->v0.time[0] = ktime_to_ns(ktime_get());
69 	head->func->rgpos(head, &args->v0.hline, &args->v0.vline);
70 	args->v0.time[1] = ktime_to_ns(ktime_get());
71 	return 0;
72 }
73 
74 static int
75 nvkm_uhead_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)
76 {
77 	struct nvkm_head *head = nvkm_uhead(object);
78 
79 	switch (mthd) {
80 	case NVIF_HEAD_V0_SCANOUTPOS: return nvkm_uhead_mthd_scanoutpos(head, argv, argc);
81 	default:
82 		return -EINVAL;
83 	}
84 }
85 
86 static void *
87 nvkm_uhead_dtor(struct nvkm_object *object)
88 {
89 	struct nvkm_head *head = nvkm_uhead(object);
90 	struct nvkm_disp *disp = head->disp;
91 
92 	spin_lock(&disp->client.lock);
93 	head->object.func = NULL;
94 	spin_unlock(&disp->client.lock);
95 	return NULL;
96 }
97 
98 static const struct nvkm_object_func
99 nvkm_uhead = {
100 	.dtor = nvkm_uhead_dtor,
101 	.mthd = nvkm_uhead_mthd,
102 	.uevent = nvkm_uhead_uevent,
103 };
104 
105 int
106 nvkm_uhead_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject)
107 {
108 	struct nvkm_disp *disp = nvkm_udisp(oclass->parent);
109 	struct nvkm_head *head;
110 	union nvif_head_args *args = argv;
111 	int ret;
112 
113 	if (argc != sizeof(args->v0) || args->v0.version != 0)
114 		return -ENOSYS;
115 	if (!(head = nvkm_head_find(disp, args->v0.id)))
116 		return -EINVAL;
117 
118 	ret = -EBUSY;
119 	spin_lock(&disp->client.lock);
120 	if (!head->object.func) {
121 		nvkm_object_ctor(&nvkm_uhead, oclass, &head->object);
122 		*pobject = &head->object;
123 		ret = 0;
124 	}
125 	spin_unlock(&disp->client.lock);
126 	return ret;
127 }
128