Home | History | Annotate | Line # | Download | only in disp
      1 /*	$NetBSD: nouveau_nvkm_engine_disp_base.c,v 1.3 2021/12/18 23:45:35 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2013 Red Hat Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: Ben Skeggs
     25  */
     26 #include <sys/cdefs.h>
     27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_engine_disp_base.c,v 1.3 2021/12/18 23:45:35 riastradh Exp $");
     28 
     29 #include "priv.h"
     30 #include "conn.h"
     31 #include "dp.h"
     32 #include "head.h"
     33 #include "ior.h"
     34 #include "outp.h"
     35 
     36 #include <core/client.h>
     37 #include <core/notify.h>
     38 #include <core/oproxy.h>
     39 #include <subdev/bios.h>
     40 #include <subdev/bios/dcb.h>
     41 
     42 #include <nvif/class.h>
     43 #include <nvif/cl0046.h>
     44 #include <nvif/event.h>
     45 #include <nvif/unpack.h>
     46 
     47 static void
     48 nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int id)
     49 {
     50 	struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
     51 	struct nvkm_head *head = nvkm_head_find(disp, id);
     52 	if (head)
     53 		head->func->vblank_put(head);
     54 }
     55 
     56 static void
     57 nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id)
     58 {
     59 	struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
     60 	struct nvkm_head *head = nvkm_head_find(disp, id);
     61 	if (head)
     62 		head->func->vblank_get(head);
     63 }
     64 
     65 static int
     66 nvkm_disp_vblank_ctor(struct nvkm_object *object, void *data, u32 size,
     67 		      struct nvkm_notify *notify)
     68 {
     69 	struct nvkm_disp *disp =
     70 		container_of(notify->event, typeof(*disp), vblank);
     71 	union {
     72 		struct nvif_notify_head_req_v0 v0;
     73 	} *req = data;
     74 	int ret = -ENOSYS;
     75 
     76 	if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
     77 		notify->size = sizeof(struct nvif_notify_head_rep_v0);
     78 		if (ret = -ENXIO, req->v0.head <= disp->vblank.index_nr) {
     79 			notify->types = 1;
     80 			notify->index = req->v0.head;
     81 			return 0;
     82 		}
     83 	}
     84 
     85 	return ret;
     86 }
     87 
     88 static const struct nvkm_event_func
     89 nvkm_disp_vblank_func = {
     90 	.ctor = nvkm_disp_vblank_ctor,
     91 	.init = nvkm_disp_vblank_init,
     92 	.fini = nvkm_disp_vblank_fini,
     93 };
     94 
     95 void
     96 nvkm_disp_vblank(struct nvkm_disp *disp, int head)
     97 {
     98 	struct nvif_notify_head_rep_v0 rep = {};
     99 	nvkm_event_send(&disp->vblank, 1, head, &rep, sizeof(rep));
    100 }
    101 
    102 static int
    103 nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size,
    104 		   struct nvkm_notify *notify)
    105 {
    106 	struct nvkm_disp *disp =
    107 		container_of(notify->event, typeof(*disp), hpd);
    108 	union {
    109 		struct nvif_notify_conn_req_v0 v0;
    110 	} *req = data;
    111 	struct nvkm_outp *outp;
    112 	int ret = -ENOSYS;
    113 
    114 	if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
    115 		notify->size = sizeof(struct nvif_notify_conn_rep_v0);
    116 		list_for_each_entry(outp, &disp->outp, head) {
    117 			if (ret = -ENXIO, outp->conn->index == req->v0.conn) {
    118 				if (ret = -ENODEV, outp->conn->hpd.event) {
    119 					notify->types = req->v0.mask;
    120 					notify->index = req->v0.conn;
    121 					ret = 0;
    122 				}
    123 				break;
    124 			}
    125 		}
    126 	}
    127 
    128 	return ret;
    129 }
    130 
    131 static const struct nvkm_event_func
    132 nvkm_disp_hpd_func = {
    133 	.ctor = nvkm_disp_hpd_ctor
    134 };
    135 
    136 int
    137 nvkm_disp_ntfy(struct nvkm_object *object, u32 type, struct nvkm_event **event)
    138 {
    139 	struct nvkm_disp *disp = nvkm_disp(object->engine);
    140 	switch (type) {
    141 	case NV04_DISP_NTFY_VBLANK:
    142 		*event = &disp->vblank;
    143 		return 0;
    144 	case NV04_DISP_NTFY_CONN:
    145 		*event = &disp->hpd;
    146 		return 0;
    147 	default:
    148 		break;
    149 	}
    150 	return -EINVAL;
    151 }
    152 
    153 static void
    154 nvkm_disp_class_del(struct nvkm_oproxy *oproxy)
    155 {
    156 	struct nvkm_disp *disp = nvkm_disp(oproxy->base.engine);
    157 	mutex_lock(&disp->engine.subdev.mutex);
    158 	if (disp->client == oproxy)
    159 		disp->client = NULL;
    160 	mutex_unlock(&disp->engine.subdev.mutex);
    161 }
    162 
    163 static const struct nvkm_oproxy_func
    164 nvkm_disp_class = {
    165 	.dtor[1] = nvkm_disp_class_del,
    166 };
    167 
    168 static int
    169 nvkm_disp_class_new(struct nvkm_device *device,
    170 		    const struct nvkm_oclass *oclass, void *data, u32 size,
    171 		    struct nvkm_object **pobject)
    172 {
    173 	const struct nvkm_disp_oclass *sclass = oclass->engn;
    174 	struct nvkm_disp *disp = nvkm_disp(oclass->engine);
    175 	struct nvkm_oproxy *oproxy;
    176 	int ret;
    177 
    178 	ret = nvkm_oproxy_new_(&nvkm_disp_class, oclass, &oproxy);
    179 	if (ret)
    180 		return ret;
    181 	*pobject = &oproxy->base;
    182 
    183 	mutex_lock(&disp->engine.subdev.mutex);
    184 	if (disp->client) {
    185 		mutex_unlock(&disp->engine.subdev.mutex);
    186 		return -EBUSY;
    187 	}
    188 	disp->client = oproxy;
    189 	mutex_unlock(&disp->engine.subdev.mutex);
    190 
    191 	return sclass->ctor(disp, oclass, data, size, &oproxy->object);
    192 }
    193 
    194 static const struct nvkm_device_oclass
    195 nvkm_disp_sclass = {
    196 	.ctor = nvkm_disp_class_new,
    197 };
    198 
    199 static int
    200 nvkm_disp_class_get(struct nvkm_oclass *oclass, int index,
    201 		    const struct nvkm_device_oclass **class)
    202 {
    203 	struct nvkm_disp *disp = nvkm_disp(oclass->engine);
    204 	if (index == 0) {
    205 		const struct nvkm_disp_oclass *root = disp->func->root(disp);
    206 		oclass->base = root->base;
    207 		oclass->engn = root;
    208 		*class = &nvkm_disp_sclass;
    209 		return 0;
    210 	}
    211 	return 1;
    212 }
    213 
    214 static void
    215 nvkm_disp_intr(struct nvkm_engine *engine)
    216 {
    217 	struct nvkm_disp *disp = nvkm_disp(engine);
    218 	disp->func->intr(disp);
    219 }
    220 
    221 static int
    222 nvkm_disp_fini(struct nvkm_engine *engine, bool suspend)
    223 {
    224 	struct nvkm_disp *disp = nvkm_disp(engine);
    225 	struct nvkm_conn *conn;
    226 	struct nvkm_outp *outp;
    227 
    228 	if (disp->func->fini)
    229 		disp->func->fini(disp);
    230 
    231 	list_for_each_entry(outp, &disp->outp, head) {
    232 		nvkm_outp_fini(outp);
    233 	}
    234 
    235 	list_for_each_entry(conn, &disp->conn, head) {
    236 		nvkm_conn_fini(conn);
    237 	}
    238 
    239 	return 0;
    240 }
    241 
    242 static int
    243 nvkm_disp_init(struct nvkm_engine *engine)
    244 {
    245 	struct nvkm_disp *disp = nvkm_disp(engine);
    246 	struct nvkm_conn *conn;
    247 	struct nvkm_outp *outp;
    248 	struct nvkm_ior *ior;
    249 
    250 	list_for_each_entry(conn, &disp->conn, head) {
    251 		nvkm_conn_init(conn);
    252 	}
    253 
    254 	list_for_each_entry(outp, &disp->outp, head) {
    255 		nvkm_outp_init(outp);
    256 	}
    257 
    258 	if (disp->func->init) {
    259 		int ret = disp->func->init(disp);
    260 		if (ret)
    261 			return ret;
    262 	}
    263 
    264 	/* Set 'normal' (ie. when it's attached to a head) state for
    265 	 * each output resource to 'fully enabled'.
    266 	 */
    267 	list_for_each_entry(ior, &disp->ior, head) {
    268 		ior->func->power(ior, true, true, true, true, true);
    269 	}
    270 
    271 	return 0;
    272 }
    273 
    274 static int
    275 nvkm_disp_oneinit(struct nvkm_engine *engine)
    276 {
    277 	struct nvkm_disp *disp = nvkm_disp(engine);
    278 	struct nvkm_subdev *subdev = &disp->engine.subdev;
    279 	struct nvkm_bios *bios = subdev->device->bios;
    280 	struct nvkm_outp *outp, *outt, *pair;
    281 	struct nvkm_conn *conn;
    282 	struct nvkm_head *head;
    283 	struct nvkm_ior *ior;
    284 	struct nvbios_connE connE;
    285 	struct dcb_output dcbE;
    286 	u8  hpd = 0, ver, hdr;
    287 	u32 data;
    288 	int ret, i;
    289 
    290 	/* Create output path objects for each VBIOS display path. */
    291 	i = -1;
    292 	while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
    293 		if (ver < 0x40) /* No support for chipsets prior to NV50. */
    294 			break;
    295 		if (dcbE.type == DCB_OUTPUT_UNUSED)
    296 			continue;
    297 		if (dcbE.type == DCB_OUTPUT_EOL)
    298 			break;
    299 		outp = NULL;
    300 
    301 		switch (dcbE.type) {
    302 		case DCB_OUTPUT_ANALOG:
    303 		case DCB_OUTPUT_TV:
    304 		case DCB_OUTPUT_TMDS:
    305 		case DCB_OUTPUT_LVDS:
    306 			ret = nvkm_outp_new(disp, i, &dcbE, &outp);
    307 			break;
    308 		case DCB_OUTPUT_DP:
    309 			ret = nvkm_dp_new(disp, i, &dcbE, &outp);
    310 			break;
    311 		case DCB_OUTPUT_WFD:
    312 			/* No support for WFD yet. */
    313 			ret = -ENODEV;
    314 			continue;
    315 		default:
    316 			nvkm_warn(subdev, "dcb %d type %d unknown\n",
    317 				  i, dcbE.type);
    318 			continue;
    319 		}
    320 
    321 		if (ret) {
    322 			if (outp) {
    323 				if (ret != -ENODEV)
    324 					OUTP_ERR(outp, "ctor failed: %d", ret);
    325 				else
    326 					OUTP_DBG(outp, "not supported");
    327 				nvkm_outp_del(&outp);
    328 				continue;
    329 			}
    330 			nvkm_error(subdev, "failed to create outp %d\n", i);
    331 			continue;
    332 		}
    333 
    334 		list_add_tail(&outp->head, &disp->outp);
    335 		hpd = max(hpd, (u8)(dcbE.connector + 1));
    336 	}
    337 
    338 	/* Create connector objects based on available output paths. */
    339 	list_for_each_entry_safe(outp, outt, &disp->outp, head) {
    340 		/* VBIOS data *should* give us the most useful information. */
    341 		data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr,
    342 				     &connE);
    343 
    344 		/* No bios connector data... */
    345 		if (!data) {
    346 			/* Heuristic: anything with the same ccb index is
    347 			 * considered to be on the same connector, any
    348 			 * output path without an associated ccb entry will
    349 			 * be put on its own connector.
    350 			 */
    351 			int ccb_index = outp->info.i2c_index;
    352 			if (ccb_index != 0xf) {
    353 				list_for_each_entry(pair, &disp->outp, head) {
    354 					if (pair->info.i2c_index == ccb_index) {
    355 						outp->conn = pair->conn;
    356 						break;
    357 					}
    358 				}
    359 			}
    360 
    361 			/* Connector shared with another output path. */
    362 			if (outp->conn)
    363 				continue;
    364 
    365 			memset(&connE, 0x00, sizeof(connE));
    366 			connE.type = DCB_CONNECTOR_NONE;
    367 			i = -1;
    368 		} else {
    369 			i = outp->info.connector;
    370 		}
    371 
    372 		/* Check that we haven't already created this connector. */
    373 		list_for_each_entry(conn, &disp->conn, head) {
    374 			if (conn->index == outp->info.connector) {
    375 				outp->conn = conn;
    376 				break;
    377 			}
    378 		}
    379 
    380 		if (outp->conn)
    381 			continue;
    382 
    383 		/* Apparently we need to create a new one! */
    384 		ret = nvkm_conn_new(disp, i, &connE, &outp->conn);
    385 		if (ret) {
    386 			nvkm_error(&disp->engine.subdev,
    387 				   "failed to create outp %d conn: %d\n",
    388 				   outp->index, ret);
    389 			nvkm_conn_del(&outp->conn);
    390 			list_del(&outp->head);
    391 			nvkm_outp_del(&outp);
    392 			continue;
    393 		}
    394 
    395 		list_add_tail(&outp->conn->head, &disp->conn);
    396 	}
    397 
    398 	ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd);
    399 	if (ret)
    400 		return ret;
    401 
    402 	if (disp->func->oneinit) {
    403 		ret = disp->func->oneinit(disp);
    404 		if (ret)
    405 			return ret;
    406 	}
    407 
    408 	/* Enforce identity-mapped SOR assignment for panels, which have
    409 	 * certain bits (ie. backlight controls) wired to a specific SOR.
    410 	 */
    411 	list_for_each_entry(outp, &disp->outp, head) {
    412 		if (outp->conn->info.type == DCB_CONNECTOR_LVDS ||
    413 		    outp->conn->info.type == DCB_CONNECTOR_eDP) {
    414 			ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1);
    415 			if (!WARN_ON(!ior))
    416 				ior->identity = true;
    417 			outp->identity = true;
    418 		}
    419 	}
    420 
    421 	i = 0;
    422 	list_for_each_entry(head, &disp->head, head)
    423 		i = max(i, head->id + 1);
    424 
    425 	return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank);
    426 }
    427 
    428 static void *
    429 nvkm_disp_dtor(struct nvkm_engine *engine)
    430 {
    431 	struct nvkm_disp *disp = nvkm_disp(engine);
    432 	struct nvkm_conn *conn;
    433 	struct nvkm_outp *outp;
    434 	void *data = disp;
    435 
    436 	if (disp->func->dtor)
    437 		data = disp->func->dtor(disp);
    438 
    439 	nvkm_event_fini(&disp->vblank);
    440 	nvkm_event_fini(&disp->hpd);
    441 
    442 	while (!list_empty(&disp->conn)) {
    443 		conn = list_first_entry(&disp->conn, typeof(*conn), head);
    444 		list_del(&conn->head);
    445 		nvkm_conn_del(&conn);
    446 	}
    447 
    448 	while (!list_empty(&disp->outp)) {
    449 		outp = list_first_entry(&disp->outp, typeof(*outp), head);
    450 		list_del(&outp->head);
    451 		nvkm_outp_del(&outp);
    452 	}
    453 
    454 	while (!list_empty(&disp->ior)) {
    455 		struct nvkm_ior *ior =
    456 			list_first_entry(&disp->ior, typeof(*ior), head);
    457 		nvkm_ior_del(&ior);
    458 	}
    459 
    460 	while (!list_empty(&disp->head)) {
    461 		struct nvkm_head *head =
    462 			list_first_entry(&disp->head, typeof(*head), head);
    463 		nvkm_head_del(&head);
    464 	}
    465 
    466 	return data;
    467 }
    468 
    469 static const struct nvkm_engine_func
    470 nvkm_disp = {
    471 	.dtor = nvkm_disp_dtor,
    472 	.oneinit = nvkm_disp_oneinit,
    473 	.init = nvkm_disp_init,
    474 	.fini = nvkm_disp_fini,
    475 	.intr = nvkm_disp_intr,
    476 	.base.sclass = nvkm_disp_class_get,
    477 };
    478 
    479 int
    480 nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
    481 	       int index, struct nvkm_disp *disp)
    482 {
    483 	disp->func = func;
    484 	INIT_LIST_HEAD(&disp->head);
    485 	INIT_LIST_HEAD(&disp->ior);
    486 	INIT_LIST_HEAD(&disp->outp);
    487 	INIT_LIST_HEAD(&disp->conn);
    488 	return nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine);
    489 }
    490 
    491 int
    492 nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device,
    493 	       int index, struct nvkm_disp **pdisp)
    494 {
    495 	if (!(*pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL)))
    496 		return -ENOMEM;
    497 	return nvkm_disp_ctor(func, device, index, *pdisp);
    498 }
    499