1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs * Copyright 2013 Red Hat Inc.
3c39f472eSBen Skeggs *
4c39f472eSBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs *
11c39f472eSBen Skeggs * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs *
14c39f472eSBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17c39f472eSBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs *
22c39f472eSBen Skeggs * Authors: Ben Skeggs
23c39f472eSBen Skeggs */
24878da15aSBen Skeggs #include "priv.h"
25878da15aSBen Skeggs #include "conn.h"
263c66c87dSBen Skeggs #include "dp.h"
27a1c93078SBen Skeggs #include "head.h"
2878f1ad6fSBen Skeggs #include "ior.h"
29878da15aSBen Skeggs #include "outp.h"
30c39f472eSBen Skeggs
310ce41e3cSBen Skeggs #include <core/client.h>
321c6aab75SBen Skeggs #include <core/ramht.h>
33878da15aSBen Skeggs #include <subdev/bios.h>
34878da15aSBen Skeggs #include <subdev/bios/dcb.h>
35878da15aSBen Skeggs
36c39f472eSBen Skeggs #include <nvif/class.h>
377568b106SBen Skeggs #include <nvif/cl0046.h>
38c39f472eSBen Skeggs #include <nvif/event.h>
39878da15aSBen Skeggs #include <nvif/unpack.h>
40c39f472eSBen Skeggs
4170aa8670SBen Skeggs static void
nvkm_disp_vblank_fini(struct nvkm_event * event,int type,int id)4214187b00SBen Skeggs nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int id)
4370aa8670SBen Skeggs {
4470aa8670SBen Skeggs struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
4514187b00SBen Skeggs struct nvkm_head *head = nvkm_head_find(disp, id);
4614187b00SBen Skeggs if (head)
4714187b00SBen Skeggs head->func->vblank_put(head);
4870aa8670SBen Skeggs }
4970aa8670SBen Skeggs
5070aa8670SBen Skeggs static void
nvkm_disp_vblank_init(struct nvkm_event * event,int type,int id)5114187b00SBen Skeggs nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id)
5270aa8670SBen Skeggs {
5370aa8670SBen Skeggs struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
5414187b00SBen Skeggs struct nvkm_head *head = nvkm_head_find(disp, id);
5514187b00SBen Skeggs if (head)
5614187b00SBen Skeggs head->func->vblank_get(head);
5770aa8670SBen Skeggs }
5870aa8670SBen Skeggs
5970aa8670SBen Skeggs static const struct nvkm_event_func
6070aa8670SBen Skeggs nvkm_disp_vblank_func = {
6170aa8670SBen Skeggs .init = nvkm_disp_vblank_init,
6270aa8670SBen Skeggs .fini = nvkm_disp_vblank_fini,
6370aa8670SBen Skeggs };
6470aa8670SBen Skeggs
65c39f472eSBen Skeggs void
nvkm_disp_vblank(struct nvkm_disp * disp,int head)66878da15aSBen Skeggs nvkm_disp_vblank(struct nvkm_disp *disp, int head)
67c39f472eSBen Skeggs {
68*99d0701aSBen Skeggs nvkm_event_ntfy(&disp->vblank, head, NVKM_DISP_HEAD_EVENT_VBLANK);
69c39f472eSBen Skeggs }
70c39f472eSBen Skeggs
71c39f472eSBen Skeggs static int
nvkm_disp_class_new(struct nvkm_device * device,const struct nvkm_oclass * oclass,void * data,u32 size,struct nvkm_object ** pobject)720ce41e3cSBen Skeggs nvkm_disp_class_new(struct nvkm_device *device,
730ce41e3cSBen Skeggs const struct nvkm_oclass *oclass, void *data, u32 size,
740ce41e3cSBen Skeggs struct nvkm_object **pobject)
750ce41e3cSBen Skeggs {
76168c0299SBen Skeggs return nvkm_udisp_new(oclass, data, size, pobject);
770ce41e3cSBen Skeggs }
780ce41e3cSBen Skeggs
790ce41e3cSBen Skeggs static const struct nvkm_device_oclass
800ce41e3cSBen Skeggs nvkm_disp_sclass = {
810ce41e3cSBen Skeggs .ctor = nvkm_disp_class_new,
820ce41e3cSBen Skeggs };
830ce41e3cSBen Skeggs
840ce41e3cSBen Skeggs static int
nvkm_disp_class_get(struct nvkm_oclass * oclass,int index,const struct nvkm_device_oclass ** class)850ce41e3cSBen Skeggs nvkm_disp_class_get(struct nvkm_oclass *oclass, int index,
860ce41e3cSBen Skeggs const struct nvkm_device_oclass **class)
870ce41e3cSBen Skeggs {
880ce41e3cSBen Skeggs struct nvkm_disp *disp = nvkm_disp(oclass->engine);
890ce41e3cSBen Skeggs if (index == 0) {
90168c0299SBen Skeggs oclass->base = disp->func->root;
910ce41e3cSBen Skeggs *class = &nvkm_disp_sclass;
920ce41e3cSBen Skeggs return 0;
930ce41e3cSBen Skeggs }
940ce41e3cSBen Skeggs return 1;
950ce41e3cSBen Skeggs }
960ce41e3cSBen Skeggs
9770aa8670SBen Skeggs static void
nvkm_disp_intr(struct nvkm_engine * engine)9870aa8670SBen Skeggs nvkm_disp_intr(struct nvkm_engine *engine)
99c39f472eSBen Skeggs {
10070aa8670SBen Skeggs struct nvkm_disp *disp = nvkm_disp(engine);
10170aa8670SBen Skeggs disp->func->intr(disp);
10270aa8670SBen Skeggs }
10370aa8670SBen Skeggs
10470aa8670SBen Skeggs static int
nvkm_disp_fini(struct nvkm_engine * engine,bool suspend)10570aa8670SBen Skeggs nvkm_disp_fini(struct nvkm_engine *engine, bool suspend)
10670aa8670SBen Skeggs {
10770aa8670SBen Skeggs struct nvkm_disp *disp = nvkm_disp(engine);
108981a8162SBen Skeggs struct nvkm_conn *conn;
109d7ce92e2SBen Skeggs struct nvkm_outp *outp;
110c39f472eSBen Skeggs
111bb3b0a42SBen Skeggs if (disp->func->fini)
112bb3b0a42SBen Skeggs disp->func->fini(disp);
113bb3b0a42SBen Skeggs
11492fba5d3SBen Skeggs list_for_each_entry(outp, &disp->outps, head) {
115d7ce92e2SBen Skeggs nvkm_outp_fini(outp);
116f2c906fcSBen Skeggs }
117f2c906fcSBen Skeggs
11892fba5d3SBen Skeggs list_for_each_entry(conn, &disp->conns, head) {
119981a8162SBen Skeggs nvkm_conn_fini(conn);
120c39f472eSBen Skeggs }
121c39f472eSBen Skeggs
12270aa8670SBen Skeggs return 0;
123c39f472eSBen Skeggs }
124c39f472eSBen Skeggs
12570aa8670SBen Skeggs static int
nvkm_disp_init(struct nvkm_engine * engine)12670aa8670SBen Skeggs nvkm_disp_init(struct nvkm_engine *engine)
127c39f472eSBen Skeggs {
12870aa8670SBen Skeggs struct nvkm_disp *disp = nvkm_disp(engine);
129981a8162SBen Skeggs struct nvkm_conn *conn;
130d7ce92e2SBen Skeggs struct nvkm_outp *outp;
131bb3b0a42SBen Skeggs struct nvkm_ior *ior;
132c39f472eSBen Skeggs
13392fba5d3SBen Skeggs list_for_each_entry(conn, &disp->conns, head) {
134981a8162SBen Skeggs nvkm_conn_init(conn);
135c39f472eSBen Skeggs }
136c39f472eSBen Skeggs
13792fba5d3SBen Skeggs list_for_each_entry(outp, &disp->outps, head) {
138d7ce92e2SBen Skeggs nvkm_outp_init(outp);
139c39f472eSBen Skeggs }
140c39f472eSBen Skeggs
141bb3b0a42SBen Skeggs if (disp->func->init) {
142bb3b0a42SBen Skeggs int ret = disp->func->init(disp);
143bb3b0a42SBen Skeggs if (ret)
144bb3b0a42SBen Skeggs return ret;
145bb3b0a42SBen Skeggs }
146bb3b0a42SBen Skeggs
147bb3b0a42SBen Skeggs /* Set 'normal' (ie. when it's attached to a head) state for
148bb3b0a42SBen Skeggs * each output resource to 'fully enabled'.
149bb3b0a42SBen Skeggs */
15092fba5d3SBen Skeggs list_for_each_entry(ior, &disp->iors, head) {
151bb3b0a42SBen Skeggs ior->func->power(ior, true, true, true, true, true);
152bb3b0a42SBen Skeggs }
153bb3b0a42SBen Skeggs
15470aa8670SBen Skeggs return 0;
155c39f472eSBen Skeggs }
156c39f472eSBen Skeggs
1574b2b42f8SBen Skeggs static int
nvkm_disp_oneinit(struct nvkm_engine * engine)1584b2b42f8SBen Skeggs nvkm_disp_oneinit(struct nvkm_engine *engine)
159c39f472eSBen Skeggs {
16070aa8670SBen Skeggs struct nvkm_disp *disp = nvkm_disp(engine);
1613c66c87dSBen Skeggs struct nvkm_subdev *subdev = &disp->engine.subdev;
1623c66c87dSBen Skeggs struct nvkm_bios *bios = subdev->device->bios;
163d7ce92e2SBen Skeggs struct nvkm_outp *outp, *outt, *pair;
164981a8162SBen Skeggs struct nvkm_conn *conn;
165a1c93078SBen Skeggs struct nvkm_head *head;
16653b0cc46SBen Skeggs struct nvkm_ior *ior;
167f2c906fcSBen Skeggs struct nvbios_connE connE;
168c39f472eSBen Skeggs struct dcb_output dcbE;
169c39f472eSBen Skeggs u8 hpd = 0, ver, hdr;
170c39f472eSBen Skeggs u32 data;
171c39f472eSBen Skeggs int ret, i;
172c39f472eSBen Skeggs
1734b2b42f8SBen Skeggs /* Create output path objects for each VBIOS display path. */
174c39f472eSBen Skeggs i = -1;
175c39f472eSBen Skeggs while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
176372aa73eSBen Skeggs if (ver < 0x40) /* No support for chipsets prior to NV50. */
177372aa73eSBen Skeggs break;
178c39f472eSBen Skeggs if (dcbE.type == DCB_OUTPUT_UNUSED)
179c39f472eSBen Skeggs continue;
180c39f472eSBen Skeggs if (dcbE.type == DCB_OUTPUT_EOL)
181c39f472eSBen Skeggs break;
182f2c906fcSBen Skeggs outp = NULL;
183c39f472eSBen Skeggs
184f2c906fcSBen Skeggs switch (dcbE.type) {
1853c66c87dSBen Skeggs case DCB_OUTPUT_ANALOG:
1863c66c87dSBen Skeggs case DCB_OUTPUT_TV:
1873c66c87dSBen Skeggs case DCB_OUTPUT_TMDS:
1883c66c87dSBen Skeggs case DCB_OUTPUT_LVDS:
1893c66c87dSBen Skeggs ret = nvkm_outp_new(disp, i, &dcbE, &outp);
1903c66c87dSBen Skeggs break;
1913c66c87dSBen Skeggs case DCB_OUTPUT_DP:
1923c66c87dSBen Skeggs ret = nvkm_dp_new(disp, i, &dcbE, &outp);
1933c66c87dSBen Skeggs break;
194df00d5daSRosen Penev case DCB_OUTPUT_WFD:
195df00d5daSRosen Penev /* No support for WFD yet. */
196df00d5daSRosen Penev ret = -ENODEV;
197df00d5daSRosen Penev continue;
198f2c906fcSBen Skeggs default:
1993c66c87dSBen Skeggs nvkm_warn(subdev, "dcb %d type %d unknown\n",
2003c66c87dSBen Skeggs i, dcbE.type);
201f2c906fcSBen Skeggs continue;
202f2c906fcSBen Skeggs }
203f2c906fcSBen Skeggs
204f2c906fcSBen Skeggs if (ret) {
2053c66c87dSBen Skeggs if (outp) {
2063c66c87dSBen Skeggs if (ret != -ENODEV)
2073c66c87dSBen Skeggs OUTP_ERR(outp, "ctor failed: %d", ret);
2083c66c87dSBen Skeggs else
2093c66c87dSBen Skeggs OUTP_DBG(outp, "not supported");
2103c66c87dSBen Skeggs nvkm_outp_del(&outp);
211f2c906fcSBen Skeggs continue;
212f2c906fcSBen Skeggs }
2133c66c87dSBen Skeggs nvkm_error(subdev, "failed to create outp %d\n", i);
214f2c906fcSBen Skeggs continue;
215f2c906fcSBen Skeggs }
216f2c906fcSBen Skeggs
21792fba5d3SBen Skeggs list_add_tail(&outp->head, &disp->outps);
218f2c906fcSBen Skeggs hpd = max(hpd, (u8)(dcbE.connector + 1));
219f2c906fcSBen Skeggs }
220f2c906fcSBen Skeggs
2214b2b42f8SBen Skeggs /* Create connector objects based on available output paths. */
22292fba5d3SBen Skeggs list_for_each_entry_safe(outp, outt, &disp->outps, head) {
2234b2b42f8SBen Skeggs /* VBIOS data *should* give us the most useful information. */
224f2c906fcSBen Skeggs data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr,
225f2c906fcSBen Skeggs &connE);
226f2c906fcSBen Skeggs
2274b2b42f8SBen Skeggs /* No bios connector data... */
228f2c906fcSBen Skeggs if (!data) {
2294b2b42f8SBen Skeggs /* Heuristic: anything with the same ccb index is
230f2c906fcSBen Skeggs * considered to be on the same connector, any
231f2c906fcSBen Skeggs * output path without an associated ccb entry will
2324b2b42f8SBen Skeggs * be put on its own connector.
233f2c906fcSBen Skeggs */
234f2c906fcSBen Skeggs int ccb_index = outp->info.i2c_index;
235f2c906fcSBen Skeggs if (ccb_index != 0xf) {
23692fba5d3SBen Skeggs list_for_each_entry(pair, &disp->outps, head) {
237f2c906fcSBen Skeggs if (pair->info.i2c_index == ccb_index) {
238f2c906fcSBen Skeggs outp->conn = pair->conn;
239c39f472eSBen Skeggs break;
240c39f472eSBen Skeggs }
241f2c906fcSBen Skeggs }
242c39f472eSBen Skeggs }
243c39f472eSBen Skeggs
2444b2b42f8SBen Skeggs /* Connector shared with another output path. */
245f2c906fcSBen Skeggs if (outp->conn)
246f2c906fcSBen Skeggs continue;
247f2c906fcSBen Skeggs
248f2c906fcSBen Skeggs memset(&connE, 0x00, sizeof(connE));
249f2c906fcSBen Skeggs connE.type = DCB_CONNECTOR_NONE;
250f2c906fcSBen Skeggs i = -1;
251f2c906fcSBen Skeggs } else {
252f2c906fcSBen Skeggs i = outp->info.connector;
253f2c906fcSBen Skeggs }
254f2c906fcSBen Skeggs
2554b2b42f8SBen Skeggs /* Check that we haven't already created this connector. */
25692fba5d3SBen Skeggs list_for_each_entry(conn, &disp->conns, head) {
257f2c906fcSBen Skeggs if (conn->index == outp->info.connector) {
258f2c906fcSBen Skeggs outp->conn = conn;
259f2c906fcSBen Skeggs break;
260f2c906fcSBen Skeggs }
261f2c906fcSBen Skeggs }
262f2c906fcSBen Skeggs
263f2c906fcSBen Skeggs if (outp->conn)
264f2c906fcSBen Skeggs continue;
265f2c906fcSBen Skeggs
2664b2b42f8SBen Skeggs /* Apparently we need to create a new one! */
267981a8162SBen Skeggs ret = nvkm_conn_new(disp, i, &connE, &outp->conn);
268f2c906fcSBen Skeggs if (ret) {
269f43e47c0SBen Skeggs nvkm_error(subdev, "failed to create outp %d conn: %d\n", outp->index, ret);
270981a8162SBen Skeggs nvkm_conn_del(&outp->conn);
271f2c906fcSBen Skeggs list_del(&outp->head);
272d7ce92e2SBen Skeggs nvkm_outp_del(&outp);
273f2c906fcSBen Skeggs continue;
274f2c906fcSBen Skeggs }
275f2c906fcSBen Skeggs
27692fba5d3SBen Skeggs list_add_tail(&outp->conn->head, &disp->conns);
277c39f472eSBen Skeggs }
278c39f472eSBen Skeggs
2793b9ba66aSBen Skeggs if (disp->func->oneinit) {
2803b9ba66aSBen Skeggs ret = disp->func->oneinit(disp);
2813b9ba66aSBen Skeggs if (ret)
2823b9ba66aSBen Skeggs return ret;
2833b9ba66aSBen Skeggs }
2843b9ba66aSBen Skeggs
28553b0cc46SBen Skeggs /* Enforce identity-mapped SOR assignment for panels, which have
28653b0cc46SBen Skeggs * certain bits (ie. backlight controls) wired to a specific SOR.
28753b0cc46SBen Skeggs */
28892fba5d3SBen Skeggs list_for_each_entry(outp, &disp->outps, head) {
28953b0cc46SBen Skeggs if (outp->conn->info.type == DCB_CONNECTOR_LVDS ||
29053b0cc46SBen Skeggs outp->conn->info.type == DCB_CONNECTOR_eDP) {
29153b0cc46SBen Skeggs ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1);
29253b0cc46SBen Skeggs if (!WARN_ON(!ior))
29353b0cc46SBen Skeggs ior->identity = true;
29453b0cc46SBen Skeggs outp->identity = true;
29553b0cc46SBen Skeggs }
29653b0cc46SBen Skeggs }
29753b0cc46SBen Skeggs
298a1c93078SBen Skeggs i = 0;
29992fba5d3SBen Skeggs list_for_each_entry(head, &disp->heads, head)
300a1c93078SBen Skeggs i = max(i, head->id + 1);
301a1c93078SBen Skeggs
302f43e47c0SBen Skeggs return nvkm_event_init(&nvkm_disp_vblank_func, subdev, 1, i, &disp->vblank);
3034b2b42f8SBen Skeggs }
304c39f472eSBen Skeggs
3054b2b42f8SBen Skeggs static void *
nvkm_disp_dtor(struct nvkm_engine * engine)3064b2b42f8SBen Skeggs nvkm_disp_dtor(struct nvkm_engine *engine)
3074b2b42f8SBen Skeggs {
3084b2b42f8SBen Skeggs struct nvkm_disp *disp = nvkm_disp(engine);
3094b2b42f8SBen Skeggs struct nvkm_conn *conn;
3104b2b42f8SBen Skeggs struct nvkm_outp *outp;
31192fba5d3SBen Skeggs struct nvkm_ior *ior;
31292fba5d3SBen Skeggs struct nvkm_head *head;
3134b2b42f8SBen Skeggs void *data = disp;
3144b2b42f8SBen Skeggs
3151c6aab75SBen Skeggs nvkm_ramht_del(&disp->ramht);
3161c6aab75SBen Skeggs nvkm_gpuobj_del(&disp->inst);
3171c6aab75SBen Skeggs
3181c6aab75SBen Skeggs nvkm_event_fini(&disp->uevent);
319a6fd8f93SBen Skeggs
320a6fd8f93SBen Skeggs if (disp->super.wq) {
3211c6aab75SBen Skeggs destroy_workqueue(disp->super.wq);
322a6fd8f93SBen Skeggs mutex_destroy(&disp->super.mutex);
323a6fd8f93SBen Skeggs }
3244b2b42f8SBen Skeggs
3254b2b42f8SBen Skeggs nvkm_event_fini(&disp->vblank);
3264b2b42f8SBen Skeggs
32792fba5d3SBen Skeggs while (!list_empty(&disp->conns)) {
32892fba5d3SBen Skeggs conn = list_first_entry(&disp->conns, typeof(*conn), head);
3294b2b42f8SBen Skeggs list_del(&conn->head);
3304b2b42f8SBen Skeggs nvkm_conn_del(&conn);
3314b2b42f8SBen Skeggs }
3324b2b42f8SBen Skeggs
33392fba5d3SBen Skeggs while (!list_empty(&disp->outps)) {
33492fba5d3SBen Skeggs outp = list_first_entry(&disp->outps, typeof(*outp), head);
3354b2b42f8SBen Skeggs list_del(&outp->head);
3364b2b42f8SBen Skeggs nvkm_outp_del(&outp);
3374b2b42f8SBen Skeggs }
3384b2b42f8SBen Skeggs
33992fba5d3SBen Skeggs while (!list_empty(&disp->iors)) {
34092fba5d3SBen Skeggs ior = list_first_entry(&disp->iors, typeof(*ior), head);
34178f1ad6fSBen Skeggs nvkm_ior_del(&ior);
34278f1ad6fSBen Skeggs }
34378f1ad6fSBen Skeggs
34492fba5d3SBen Skeggs while (!list_empty(&disp->heads)) {
34592fba5d3SBen Skeggs head = list_first_entry(&disp->heads, typeof(*head), head);
346a1c93078SBen Skeggs nvkm_head_del(&head);
347a1c93078SBen Skeggs }
348a1c93078SBen Skeggs
3494b2b42f8SBen Skeggs return data;
3504b2b42f8SBen Skeggs }
3514b2b42f8SBen Skeggs
3524b2b42f8SBen Skeggs static const struct nvkm_engine_func
3534b2b42f8SBen Skeggs nvkm_disp = {
3544b2b42f8SBen Skeggs .dtor = nvkm_disp_dtor,
3554b2b42f8SBen Skeggs .oneinit = nvkm_disp_oneinit,
3564b2b42f8SBen Skeggs .init = nvkm_disp_init,
3574b2b42f8SBen Skeggs .fini = nvkm_disp_fini,
3584b2b42f8SBen Skeggs .intr = nvkm_disp_intr,
3594b2b42f8SBen Skeggs .base.sclass = nvkm_disp_class_get,
3604b2b42f8SBen Skeggs };
3614b2b42f8SBen Skeggs
3624b2b42f8SBen Skeggs int
nvkm_disp_new_(const struct nvkm_disp_func * func,struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_disp ** pdisp)3631c6aab75SBen Skeggs nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device,
3641c6aab75SBen Skeggs enum nvkm_subdev_type type, int inst, struct nvkm_disp **pdisp)
3654b2b42f8SBen Skeggs {
3661c6aab75SBen Skeggs struct nvkm_disp *disp;
3671c6aab75SBen Skeggs int ret;
3681c6aab75SBen Skeggs
3691c6aab75SBen Skeggs if (!(disp = *pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL)))
3701c6aab75SBen Skeggs return -ENOMEM;
3711c6aab75SBen Skeggs
3724b2b42f8SBen Skeggs disp->func = func;
37392fba5d3SBen Skeggs INIT_LIST_HEAD(&disp->heads);
37492fba5d3SBen Skeggs INIT_LIST_HEAD(&disp->iors);
37592fba5d3SBen Skeggs INIT_LIST_HEAD(&disp->outps);
37692fba5d3SBen Skeggs INIT_LIST_HEAD(&disp->conns);
377bfa7f6a6SBen Skeggs spin_lock_init(&disp->client.lock);
3781c6aab75SBen Skeggs
3791c6aab75SBen Skeggs ret = nvkm_engine_ctor(&nvkm_disp, device, type, inst, true, &disp->engine);
3801c6aab75SBen Skeggs if (ret)
3811c6aab75SBen Skeggs return ret;
3821c6aab75SBen Skeggs
3831c6aab75SBen Skeggs if (func->super) {
3841c6aab75SBen Skeggs disp->super.wq = create_singlethread_workqueue("nvkm-disp");
3851c6aab75SBen Skeggs if (!disp->super.wq)
3861c6aab75SBen Skeggs return -ENOMEM;
3871c6aab75SBen Skeggs
3881c6aab75SBen Skeggs INIT_WORK(&disp->super.work, func->super);
389a6fd8f93SBen Skeggs mutex_init(&disp->super.mutex);
390c39f472eSBen Skeggs }
39170aa8670SBen Skeggs
392f43e47c0SBen Skeggs return nvkm_event_init(func->uevent, &disp->engine.subdev, 1, ARRAY_SIZE(disp->chan),
393f43e47c0SBen Skeggs &disp->uevent);
39470aa8670SBen Skeggs }
395