xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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