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