]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c
drm/nouveau/disp: introduce object to track per-head functions/state
[karo-tx-linux.git] / drivers / gpu / drm / nouveau / nvkm / engine / disp / base.c
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 #include "priv.h"
25 #include "conn.h"
26 #include "head.h"
27 #include "outp.h"
28
29 #include <core/client.h>
30 #include <core/notify.h>
31 #include <core/oproxy.h>
32 #include <subdev/bios.h>
33 #include <subdev/bios/dcb.h>
34
35 #include <nvif/class.h>
36 #include <nvif/cl0046.h>
37 #include <nvif/event.h>
38 #include <nvif/unpack.h>
39
40 static void
41 nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int head)
42 {
43         struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
44         disp->func->head.vblank_fini(disp, head);
45 }
46
47 static void
48 nvkm_disp_vblank_init(struct nvkm_event *event, int type, int head)
49 {
50         struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
51         disp->func->head.vblank_init(disp, head);
52 }
53
54 static int
55 nvkm_disp_vblank_ctor(struct nvkm_object *object, void *data, u32 size,
56                       struct nvkm_notify *notify)
57 {
58         struct nvkm_disp *disp =
59                 container_of(notify->event, typeof(*disp), vblank);
60         union {
61                 struct nvif_notify_head_req_v0 v0;
62         } *req = data;
63         int ret = -ENOSYS;
64
65         if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
66                 notify->size = sizeof(struct nvif_notify_head_rep_v0);
67                 if (ret = -ENXIO, req->v0.head <= disp->vblank.index_nr) {
68                         notify->types = 1;
69                         notify->index = req->v0.head;
70                         return 0;
71                 }
72         }
73
74         return ret;
75 }
76
77 static const struct nvkm_event_func
78 nvkm_disp_vblank_func = {
79         .ctor = nvkm_disp_vblank_ctor,
80         .init = nvkm_disp_vblank_init,
81         .fini = nvkm_disp_vblank_fini,
82 };
83
84 void
85 nvkm_disp_vblank(struct nvkm_disp *disp, int head)
86 {
87         struct nvif_notify_head_rep_v0 rep = {};
88         nvkm_event_send(&disp->vblank, 1, head, &rep, sizeof(rep));
89 }
90
91 static int
92 nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size,
93                    struct nvkm_notify *notify)
94 {
95         struct nvkm_disp *disp =
96                 container_of(notify->event, typeof(*disp), hpd);
97         union {
98                 struct nvif_notify_conn_req_v0 v0;
99         } *req = data;
100         struct nvkm_outp *outp;
101         int ret = -ENOSYS;
102
103         if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
104                 notify->size = sizeof(struct nvif_notify_conn_rep_v0);
105                 list_for_each_entry(outp, &disp->outp, head) {
106                         if (ret = -ENXIO, outp->conn->index == req->v0.conn) {
107                                 if (ret = -ENODEV, outp->conn->hpd.event) {
108                                         notify->types = req->v0.mask;
109                                         notify->index = req->v0.conn;
110                                         ret = 0;
111                                 }
112                                 break;
113                         }
114                 }
115         }
116
117         return ret;
118 }
119
120 static const struct nvkm_event_func
121 nvkm_disp_hpd_func = {
122         .ctor = nvkm_disp_hpd_ctor
123 };
124
125 int
126 nvkm_disp_ntfy(struct nvkm_object *object, u32 type, struct nvkm_event **event)
127 {
128         struct nvkm_disp *disp = nvkm_disp(object->engine);
129         switch (type) {
130         case NV04_DISP_NTFY_VBLANK:
131                 *event = &disp->vblank;
132                 return 0;
133         case NV04_DISP_NTFY_CONN:
134                 *event = &disp->hpd;
135                 return 0;
136         default:
137                 break;
138         }
139         return -EINVAL;
140 }
141
142 static void
143 nvkm_disp_class_del(struct nvkm_oproxy *oproxy)
144 {
145         struct nvkm_disp *disp = nvkm_disp(oproxy->base.engine);
146         mutex_lock(&disp->engine.subdev.mutex);
147         if (disp->client == oproxy)
148                 disp->client = NULL;
149         mutex_unlock(&disp->engine.subdev.mutex);
150 }
151
152 static const struct nvkm_oproxy_func
153 nvkm_disp_class = {
154         .dtor[1] = nvkm_disp_class_del,
155 };
156
157 static int
158 nvkm_disp_class_new(struct nvkm_device *device,
159                     const struct nvkm_oclass *oclass, void *data, u32 size,
160                     struct nvkm_object **pobject)
161 {
162         const struct nvkm_disp_oclass *sclass = oclass->engn;
163         struct nvkm_disp *disp = nvkm_disp(oclass->engine);
164         struct nvkm_oproxy *oproxy;
165         int ret;
166
167         ret = nvkm_oproxy_new_(&nvkm_disp_class, oclass, &oproxy);
168         if (ret)
169                 return ret;
170         *pobject = &oproxy->base;
171
172         mutex_lock(&disp->engine.subdev.mutex);
173         if (disp->client) {
174                 mutex_unlock(&disp->engine.subdev.mutex);
175                 return -EBUSY;
176         }
177         disp->client = oproxy;
178         mutex_unlock(&disp->engine.subdev.mutex);
179
180         return sclass->ctor(disp, oclass, data, size, &oproxy->object);
181 }
182
183 static const struct nvkm_device_oclass
184 nvkm_disp_sclass = {
185         .ctor = nvkm_disp_class_new,
186 };
187
188 static int
189 nvkm_disp_class_get(struct nvkm_oclass *oclass, int index,
190                     const struct nvkm_device_oclass **class)
191 {
192         struct nvkm_disp *disp = nvkm_disp(oclass->engine);
193         if (index == 0) {
194                 const struct nvkm_disp_oclass *root = disp->func->root(disp);
195                 oclass->base = root->base;
196                 oclass->engn = root;
197                 *class = &nvkm_disp_sclass;
198                 return 0;
199         }
200         return 1;
201 }
202
203 static void
204 nvkm_disp_intr(struct nvkm_engine *engine)
205 {
206         struct nvkm_disp *disp = nvkm_disp(engine);
207         disp->func->intr(disp);
208 }
209
210 static int
211 nvkm_disp_fini(struct nvkm_engine *engine, bool suspend)
212 {
213         struct nvkm_disp *disp = nvkm_disp(engine);
214         struct nvkm_conn *conn;
215         struct nvkm_outp *outp;
216
217         list_for_each_entry(outp, &disp->outp, head) {
218                 nvkm_outp_fini(outp);
219         }
220
221         list_for_each_entry(conn, &disp->conn, head) {
222                 nvkm_conn_fini(conn);
223         }
224
225         return 0;
226 }
227
228 static int
229 nvkm_disp_init(struct nvkm_engine *engine)
230 {
231         struct nvkm_disp *disp = nvkm_disp(engine);
232         struct nvkm_conn *conn;
233         struct nvkm_outp *outp;
234
235         list_for_each_entry(conn, &disp->conn, head) {
236                 nvkm_conn_init(conn);
237         }
238
239         list_for_each_entry(outp, &disp->outp, head) {
240                 nvkm_outp_init(outp);
241         }
242
243         return 0;
244 }
245
246 static int
247 nvkm_disp_oneinit(struct nvkm_engine *engine)
248 {
249         struct nvkm_disp *disp = nvkm_disp(engine);
250         struct nvkm_bios *bios = disp->engine.subdev.device->bios;
251         struct nvkm_outp *outp, *outt, *pair;
252         struct nvkm_conn *conn;
253         struct nvkm_head *head;
254         struct nvbios_connE connE;
255         struct dcb_output dcbE;
256         u8  hpd = 0, ver, hdr;
257         u32 data;
258         int ret, i;
259
260         /* Create output path objects for each VBIOS display path. */
261         i = -1;
262         while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
263                 const struct nvkm_disp_func_outp *outps;
264                 int (*ctor)(struct nvkm_disp *, int, struct dcb_output *,
265                             struct nvkm_outp **);
266
267                 if (dcbE.type == DCB_OUTPUT_UNUSED)
268                         continue;
269                 if (dcbE.type == DCB_OUTPUT_EOL)
270                         break;
271                 outp = NULL;
272
273                 switch (dcbE.location) {
274                 case 0: outps = &disp->func->outp.internal; break;
275                 case 1: outps = &disp->func->outp.external; break;
276                 default:
277                         nvkm_warn(&disp->engine.subdev,
278                                   "dcb %d locn %d unknown\n", i, dcbE.location);
279                         continue;
280                 }
281
282                 switch (dcbE.type) {
283                 case DCB_OUTPUT_ANALOG: ctor = outps->crt ; break;
284                 case DCB_OUTPUT_TV    : ctor = outps->tv  ; break;
285                 case DCB_OUTPUT_TMDS  : ctor = outps->tmds; break;
286                 case DCB_OUTPUT_LVDS  : ctor = outps->lvds; break;
287                 case DCB_OUTPUT_DP    : ctor = outps->dp  ; break;
288                 default:
289                         nvkm_warn(&disp->engine.subdev,
290                                   "dcb %d type %d unknown\n", i, dcbE.type);
291                         continue;
292                 }
293
294                 if (ctor)
295                         ret = ctor(disp, i, &dcbE, &outp);
296                 else
297                         ret = -ENODEV;
298
299                 if (ret) {
300                         if (ret == -ENODEV) {
301                                 nvkm_debug(&disp->engine.subdev,
302                                            "dcb %d %d/%d not supported\n",
303                                            i, dcbE.location, dcbE.type);
304                                 continue;
305                         }
306                         nvkm_error(&disp->engine.subdev,
307                                    "failed to create outp %d\n", i);
308                         nvkm_outp_del(&outp);
309                         continue;
310                 }
311
312                 list_add_tail(&outp->head, &disp->outp);
313                 hpd = max(hpd, (u8)(dcbE.connector + 1));
314         }
315
316         /* Create connector objects based on available output paths. */
317         list_for_each_entry_safe(outp, outt, &disp->outp, head) {
318                 /* VBIOS data *should* give us the most useful information. */
319                 data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr,
320                                      &connE);
321
322                 /* No bios connector data... */
323                 if (!data) {
324                         /* Heuristic: anything with the same ccb index is
325                          * considered to be on the same connector, any
326                          * output path without an associated ccb entry will
327                          * be put on its own connector.
328                          */
329                         int ccb_index = outp->info.i2c_index;
330                         if (ccb_index != 0xf) {
331                                 list_for_each_entry(pair, &disp->outp, head) {
332                                         if (pair->info.i2c_index == ccb_index) {
333                                                 outp->conn = pair->conn;
334                                                 break;
335                                         }
336                                 }
337                         }
338
339                         /* Connector shared with another output path. */
340                         if (outp->conn)
341                                 continue;
342
343                         memset(&connE, 0x00, sizeof(connE));
344                         connE.type = DCB_CONNECTOR_NONE;
345                         i = -1;
346                 } else {
347                         i = outp->info.connector;
348                 }
349
350                 /* Check that we haven't already created this connector. */
351                 list_for_each_entry(conn, &disp->conn, head) {
352                         if (conn->index == outp->info.connector) {
353                                 outp->conn = conn;
354                                 break;
355                         }
356                 }
357
358                 if (outp->conn)
359                         continue;
360
361                 /* Apparently we need to create a new one! */
362                 ret = nvkm_conn_new(disp, i, &connE, &outp->conn);
363                 if (ret) {
364                         nvkm_error(&disp->engine.subdev,
365                                    "failed to create outp %d conn: %d\n",
366                                    outp->index, ret);
367                         nvkm_conn_del(&outp->conn);
368                         list_del(&outp->head);
369                         nvkm_outp_del(&outp);
370                         continue;
371                 }
372
373                 list_add_tail(&outp->conn->head, &disp->conn);
374         }
375
376         ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd);
377         if (ret)
378                 return ret;
379
380         i = 0;
381         list_for_each_entry(head, &disp->head, head)
382                 i = max(i, head->id + 1);
383
384         return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank);
385 }
386
387 static void *
388 nvkm_disp_dtor(struct nvkm_engine *engine)
389 {
390         struct nvkm_disp *disp = nvkm_disp(engine);
391         struct nvkm_conn *conn;
392         struct nvkm_outp *outp;
393         void *data = disp;
394
395         if (disp->func->dtor)
396                 data = disp->func->dtor(disp);
397
398         nvkm_event_fini(&disp->vblank);
399         nvkm_event_fini(&disp->hpd);
400
401         while (!list_empty(&disp->conn)) {
402                 conn = list_first_entry(&disp->conn, typeof(*conn), head);
403                 list_del(&conn->head);
404                 nvkm_conn_del(&conn);
405         }
406
407         while (!list_empty(&disp->outp)) {
408                 outp = list_first_entry(&disp->outp, typeof(*outp), head);
409                 list_del(&outp->head);
410                 nvkm_outp_del(&outp);
411         }
412
413         while (!list_empty(&disp->head)) {
414                 struct nvkm_head *head =
415                         list_first_entry(&disp->head, typeof(*head), head);
416                 nvkm_head_del(&head);
417         }
418
419         return data;
420 }
421
422 static const struct nvkm_engine_func
423 nvkm_disp = {
424         .dtor = nvkm_disp_dtor,
425         .oneinit = nvkm_disp_oneinit,
426         .init = nvkm_disp_init,
427         .fini = nvkm_disp_fini,
428         .intr = nvkm_disp_intr,
429         .base.sclass = nvkm_disp_class_get,
430 };
431
432 int
433 nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
434                int index, struct nvkm_disp *disp)
435 {
436         disp->func = func;
437         INIT_LIST_HEAD(&disp->head);
438         INIT_LIST_HEAD(&disp->outp);
439         INIT_LIST_HEAD(&disp->conn);
440         return nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine);
441 }
442
443 int
444 nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device,
445                int index, struct nvkm_disp **pdisp)
446 {
447         if (!(*pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL)))
448                 return -ENOMEM;
449         return nvkm_disp_ctor(func, device, index, *pdisp);
450 }