Commit | Line | Data |
---|---|---|
1d7c71a3 BS |
1 | /* |
2 | * Copyright 2013 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 | * Authors: Ben Skeggs | |
23 | */ | |
24 | ||
79ca2770 BS |
25 | #include <core/os.h> |
26 | #include <nvif/unpack.h> | |
27 | #include <nvif/event.h> | |
28 | ||
377b1f16 | 29 | #include "priv.h" |
7a014a87 BS |
30 | #include "outp.h" |
31 | #include "conn.h" | |
32 | ||
79ca2770 BS |
33 | int |
34 | nouveau_disp_vblank_ctor(void *data, u32 size, struct nvkm_notify *notify) | |
35 | { | |
36 | struct nouveau_disp *disp = | |
37 | container_of(notify->event, typeof(*disp), vblank); | |
38 | union { | |
39 | struct nvif_notify_head_req_v0 v0; | |
40 | } *req = data; | |
41 | int ret; | |
42 | ||
43 | if (nvif_unpack(req->v0, 0, 0, false)) { | |
44 | notify->size = sizeof(struct nvif_notify_head_rep_v0); | |
45 | if (ret = -ENXIO, req->v0.head <= disp->vblank.index_nr) { | |
46 | notify->types = 1; | |
47 | notify->index = req->v0.head; | |
48 | return 0; | |
49 | } | |
50 | } | |
51 | ||
52 | return ret; | |
53 | } | |
54 | ||
55 | void | |
56 | nouveau_disp_vblank(struct nouveau_disp *disp, int head) | |
57 | { | |
58 | struct nvif_notify_head_rep_v0 rep = {}; | |
59 | nvkm_event_send(&disp->vblank, 1, head, &rep, sizeof(rep)); | |
60 | } | |
61 | ||
7a014a87 | 62 | static int |
79ca2770 | 63 | nouveau_disp_hpd_ctor(void *data, u32 size, struct nvkm_notify *notify) |
7a014a87 | 64 | { |
79ca2770 BS |
65 | struct nouveau_disp *disp = |
66 | container_of(notify->event, typeof(*disp), hpd); | |
67 | union { | |
68 | struct nvif_notify_conn_req_v0 v0; | |
69 | } *req = data; | |
7a014a87 | 70 | struct nvkm_output *outp; |
79ca2770 BS |
71 | int ret; |
72 | ||
73 | if (nvif_unpack(req->v0, 0, 0, false)) { | |
74 | notify->size = sizeof(struct nvif_notify_conn_rep_v0); | |
75 | list_for_each_entry(outp, &disp->outp, head) { | |
76 | if (ret = -ENXIO, outp->conn->index == req->v0.conn) { | |
77 | if (ret = -ENODEV, outp->conn->hpd.event) { | |
78 | notify->types = req->v0.mask; | |
79 | notify->index = req->v0.conn; | |
80 | ret = 0; | |
81 | } | |
82 | break; | |
83 | } | |
7a014a87 BS |
84 | } |
85 | } | |
79ca2770 BS |
86 | |
87 | return ret; | |
7a014a87 | 88 | } |
377b1f16 | 89 | |
79ca2770 BS |
90 | static const struct nvkm_event_func |
91 | nouveau_disp_hpd_func = { | |
92 | .ctor = nouveau_disp_hpd_ctor | |
93 | }; | |
94 | ||
377b1f16 BS |
95 | int |
96 | _nouveau_disp_fini(struct nouveau_object *object, bool suspend) | |
97 | { | |
98 | struct nouveau_disp *disp = (void *)object; | |
7a014a87 BS |
99 | struct nvkm_output *outp; |
100 | int ret; | |
101 | ||
102 | list_for_each_entry(outp, &disp->outp, head) { | |
103 | ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend); | |
104 | if (ret && suspend) | |
105 | goto fail_outp; | |
106 | } | |
107 | ||
377b1f16 | 108 | return nouveau_engine_fini(&disp->base, suspend); |
7a014a87 BS |
109 | |
110 | fail_outp: | |
111 | list_for_each_entry_continue_reverse(outp, &disp->outp, head) { | |
112 | nv_ofuncs(outp)->init(nv_object(outp)); | |
113 | } | |
114 | ||
115 | return ret; | |
377b1f16 BS |
116 | } |
117 | ||
118 | int | |
119 | _nouveau_disp_init(struct nouveau_object *object) | |
120 | { | |
7a014a87 BS |
121 | struct nouveau_disp *disp = (void *)object; |
122 | struct nvkm_output *outp; | |
123 | int ret; | |
124 | ||
125 | ret = nouveau_engine_init(&disp->base); | |
126 | if (ret) | |
127 | return ret; | |
128 | ||
129 | list_for_each_entry(outp, &disp->outp, head) { | |
130 | ret = nv_ofuncs(outp)->init(nv_object(outp)); | |
131 | if (ret) | |
132 | goto fail_outp; | |
133 | } | |
134 | ||
135 | return ret; | |
136 | ||
137 | fail_outp: | |
138 | list_for_each_entry_continue_reverse(outp, &disp->outp, head) { | |
139 | nv_ofuncs(outp)->fini(nv_object(outp), false); | |
140 | } | |
141 | ||
142 | return ret; | |
377b1f16 | 143 | } |
1d7c71a3 BS |
144 | |
145 | void | |
146 | _nouveau_disp_dtor(struct nouveau_object *object) | |
147 | { | |
148 | struct nouveau_disp *disp = (void *)object; | |
7a014a87 BS |
149 | struct nvkm_output *outp, *outt; |
150 | ||
79ca2770 BS |
151 | nvkm_event_fini(&disp->vblank); |
152 | nvkm_event_fini(&disp->hpd); | |
7a014a87 | 153 | |
242a42ea ML |
154 | if (disp->outp.next) { |
155 | list_for_each_entry_safe(outp, outt, &disp->outp, head) { | |
156 | nouveau_object_ref(NULL, (struct nouveau_object **)&outp); | |
157 | } | |
7a014a87 BS |
158 | } |
159 | ||
1d7c71a3 BS |
160 | nouveau_engine_destroy(&disp->base); |
161 | } | |
162 | ||
163 | int | |
164 | nouveau_disp_create_(struct nouveau_object *parent, | |
165 | struct nouveau_object *engine, | |
166 | struct nouveau_oclass *oclass, int heads, | |
167 | const char *intname, const char *extname, | |
168 | int length, void **pobject) | |
169 | { | |
7a014a87 BS |
170 | struct nouveau_disp_impl *impl = (void *)oclass; |
171 | struct nouveau_bios *bios = nouveau_bios(parent); | |
1d7c71a3 | 172 | struct nouveau_disp *disp; |
7a014a87 BS |
173 | struct nouveau_oclass **sclass; |
174 | struct nouveau_object *object; | |
175 | struct dcb_output dcbE; | |
176 | u8 hpd = 0, ver, hdr; | |
177 | u32 data; | |
178 | int ret, i; | |
1d7c71a3 BS |
179 | |
180 | ret = nouveau_engine_create_(parent, engine, oclass, true, | |
181 | intname, extname, length, pobject); | |
182 | disp = *pobject; | |
183 | if (ret) | |
184 | return ret; | |
185 | ||
7a014a87 BS |
186 | INIT_LIST_HEAD(&disp->outp); |
187 | ||
188 | /* create output objects for each display path in the vbios */ | |
189 | i = -1; | |
190 | while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { | |
191 | if (dcbE.type == DCB_OUTPUT_UNUSED) | |
192 | continue; | |
193 | if (dcbE.type == DCB_OUTPUT_EOL) | |
194 | break; | |
195 | data = dcbE.location << 4 | dcbE.type; | |
196 | ||
197 | oclass = nvkm_output_oclass; | |
198 | sclass = impl->outp; | |
199 | while (sclass && sclass[0]) { | |
200 | if (sclass[0]->handle == data) { | |
201 | oclass = sclass[0]; | |
202 | break; | |
203 | } | |
204 | sclass++; | |
205 | } | |
206 | ||
207 | nouveau_object_ctor(*pobject, *pobject, oclass, | |
208 | &dcbE, i, &object); | |
209 | hpd = max(hpd, (u8)(dcbE.connector + 1)); | |
210 | } | |
211 | ||
79ca2770 | 212 | ret = nvkm_event_init(&nouveau_disp_hpd_func, 3, hpd, &disp->hpd); |
7a014a87 BS |
213 | if (ret) |
214 | return ret; | |
215 | ||
79ca2770 | 216 | ret = nvkm_event_init(impl->vblank, 1, heads, &disp->vblank); |
377b1f16 BS |
217 | if (ret) |
218 | return ret; | |
219 | ||
220 | return 0; | |
1d7c71a3 | 221 | } |