Home | History | Annotate | Line # | Download | only in disp
      1 /*	$NetBSD: nouveau_nvkm_engine_disp_outp.c,v 1.3 2021/12/18 23:45:35 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2014 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_outp.c,v 1.3 2021/12/18 23:45:35 riastradh Exp $");
     28 
     29 #include "outp.h"
     30 #include "ior.h"
     31 
     32 #include <subdev/bios.h>
     33 #include <subdev/bios/dcb.h>
     34 #include <subdev/i2c.h>
     35 
     36 void
     37 nvkm_outp_route(struct nvkm_disp *disp)
     38 {
     39 	struct nvkm_outp *outp;
     40 	struct nvkm_ior *ior;
     41 
     42 	list_for_each_entry(ior, &disp->ior, head) {
     43 		if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) {
     44 			OUTP_DBG(outp, "release %s", ior->name);
     45 			if (ior->func->route.set)
     46 				ior->func->route.set(outp, NULL);
     47 			ior->arm.outp = NULL;
     48 		}
     49 	}
     50 
     51 	list_for_each_entry(ior, &disp->ior, head) {
     52 		if ((outp = ior->asy.outp)) {
     53 			OUTP_DBG(outp, "acquire %s", ior->name);
     54 			if (ior->asy.outp != ior->arm.outp) {
     55 				if (ior->func->route.set)
     56 					ior->func->route.set(outp, ior);
     57 				ior->arm.outp = ior->asy.outp;
     58 			}
     59 		}
     60 	}
     61 }
     62 
     63 static enum nvkm_ior_proto
     64 nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type)
     65 {
     66 	switch (outp->info.location) {
     67 	case 0:
     68 		switch (outp->info.type) {
     69 		case DCB_OUTPUT_ANALOG: *type = DAC; return  CRT;
     70 		case DCB_OUTPUT_TV    : *type = DAC; return   TV;
     71 		case DCB_OUTPUT_TMDS  : *type = SOR; return TMDS;
     72 		case DCB_OUTPUT_LVDS  : *type = SOR; return LVDS;
     73 		case DCB_OUTPUT_DP    : *type = SOR; return   DP;
     74 		default:
     75 			break;
     76 		}
     77 		break;
     78 	case 1:
     79 		switch (outp->info.type) {
     80 		case DCB_OUTPUT_TMDS: *type = PIOR; return TMDS;
     81 		case DCB_OUTPUT_DP  : *type = PIOR; return TMDS; /* not a bug */
     82 		default:
     83 			break;
     84 		}
     85 		break;
     86 	default:
     87 		break;
     88 	}
     89 	WARN_ON(1);
     90 	return UNKNOWN;
     91 }
     92 
     93 void
     94 nvkm_outp_release(struct nvkm_outp *outp, u8 user)
     95 {
     96 	struct nvkm_ior *ior = outp->ior;
     97 	OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior);
     98 	if (ior) {
     99 		outp->acquired &= ~user;
    100 		if (!outp->acquired) {
    101 			if (outp->func->release && outp->ior)
    102 				outp->func->release(outp);
    103 			outp->ior->asy.outp = NULL;
    104 			outp->ior = NULL;
    105 		}
    106 	}
    107 }
    108 
    109 static inline int
    110 nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior)
    111 {
    112 	outp->ior = ior;
    113 	outp->ior->asy.outp = outp;
    114 	outp->ior->asy.link = outp->info.sorconf.link;
    115 	outp->acquired |= user;
    116 	return 0;
    117 }
    118 
    119 int
    120 nvkm_outp_acquire(struct nvkm_outp *outp, u8 user)
    121 {
    122 	struct nvkm_ior *ior = outp->ior;
    123 	enum nvkm_ior_proto proto;
    124 	enum nvkm_ior_type type;
    125 
    126 	OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior);
    127 	if (ior) {
    128 		outp->acquired |= user;
    129 		return 0;
    130 	}
    131 
    132 	/* Lookup a compatible, and unused, OR to assign to the device. */
    133 	proto = nvkm_outp_xlat(outp, &type);
    134 	if (proto == UNKNOWN)
    135 		return -ENOSYS;
    136 
    137 	/* Deal with panels requiring identity-mapped SOR assignment. */
    138 	if (outp->identity) {
    139 		ior = nvkm_ior_find(outp->disp, SOR, ffs(outp->info.or) - 1);
    140 		if (WARN_ON(!ior))
    141 			return -ENOSPC;
    142 		return nvkm_outp_acquire_ior(outp, user, ior);
    143 	}
    144 
    145 	/* First preference is to reuse the OR that is currently armed
    146 	 * on HW, if any, in order to prevent unnecessary switching.
    147 	 */
    148 	list_for_each_entry(ior, &outp->disp->ior, head) {
    149 		if (!ior->identity && !ior->asy.outp && ior->arm.outp == outp)
    150 			return nvkm_outp_acquire_ior(outp, user, ior);
    151 	}
    152 
    153 	/* Failing that, a completely unused OR is the next best thing. */
    154 	list_for_each_entry(ior, &outp->disp->ior, head) {
    155 		if (!ior->identity &&
    156 		    !ior->asy.outp && ior->type == type && !ior->arm.outp &&
    157 		    (ior->func->route.set || ior->id == __ffs(outp->info.or)))
    158 			return nvkm_outp_acquire_ior(outp, user, ior);
    159 	}
    160 
    161 	/* Last resort is to assign an OR that's already active on HW,
    162 	 * but will be released during the next modeset.
    163 	 */
    164 	list_for_each_entry(ior, &outp->disp->ior, head) {
    165 		if (!ior->identity && !ior->asy.outp && ior->type == type &&
    166 		    (ior->func->route.set || ior->id == __ffs(outp->info.or)))
    167 			return nvkm_outp_acquire_ior(outp, user, ior);
    168 	}
    169 
    170 	return -ENOSPC;
    171 }
    172 
    173 void
    174 nvkm_outp_fini(struct nvkm_outp *outp)
    175 {
    176 	if (outp->func->fini)
    177 		outp->func->fini(outp);
    178 }
    179 
    180 static void
    181 nvkm_outp_init_route(struct nvkm_outp *outp)
    182 {
    183 	struct nvkm_disp *disp = outp->disp;
    184 	enum nvkm_ior_proto proto;
    185 	enum nvkm_ior_type type;
    186 	struct nvkm_ior *ior;
    187 	int id, link;
    188 
    189 	/* Find any OR from the class that is able to support this device. */
    190 	proto = nvkm_outp_xlat(outp, &type);
    191 	if (proto == UNKNOWN)
    192 		return;
    193 
    194 	ior = nvkm_ior_find(disp, type, -1);
    195 	if (!ior) {
    196 		WARN_ON(1);
    197 		return;
    198 	}
    199 
    200 	/* Determine the specific OR, if any, this device is attached to. */
    201 	if (ior->func->route.get) {
    202 		id = ior->func->route.get(outp, &link);
    203 		if (id < 0) {
    204 			OUTP_DBG(outp, "no route");
    205 			return;
    206 		}
    207 	} else {
    208 		/* Prior to DCB 4.1, this is hardwired like so. */
    209 		id   = ffs(outp->info.or) - 1;
    210 		link = (ior->type == SOR) ? outp->info.sorconf.link : 0;
    211 	}
    212 
    213 	ior = nvkm_ior_find(disp, type, id);
    214 	if (!ior) {
    215 		WARN_ON(1);
    216 		return;
    217 	}
    218 
    219 	/* Determine if the OR is already configured for this device. */
    220 	ior->func->state(ior, &ior->arm);
    221 	if (!ior->arm.head || ior->arm.proto != proto) {
    222 		OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head,
    223 			 ior->arm.proto, proto);
    224 		return;
    225 	}
    226 
    227 	OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link);
    228 	ior->arm.outp = outp;
    229 }
    230 
    231 void
    232 nvkm_outp_init(struct nvkm_outp *outp)
    233 {
    234 	nvkm_outp_init_route(outp);
    235 	if (outp->func->init)
    236 		outp->func->init(outp);
    237 }
    238 
    239 void
    240 nvkm_outp_del(struct nvkm_outp **poutp)
    241 {
    242 	struct nvkm_outp *outp = *poutp;
    243 	if (outp && !WARN_ON(!outp->func)) {
    244 		if (outp->func->dtor)
    245 			*poutp = outp->func->dtor(outp);
    246 		kfree(*poutp);
    247 		*poutp = NULL;
    248 	}
    249 }
    250 
    251 int
    252 nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp,
    253 	       int index, struct dcb_output *dcbE, struct nvkm_outp *outp)
    254 {
    255 	struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
    256 	enum nvkm_ior_proto proto;
    257 	enum nvkm_ior_type type;
    258 
    259 	outp->func = func;
    260 	outp->disp = disp;
    261 	outp->index = index;
    262 	outp->info = *dcbE;
    263 	outp->i2c = nvkm_i2c_bus_find(i2c, dcbE->i2c_index);
    264 
    265 	OUTP_DBG(outp, "type %02x loc %d or %d link %d con %x "
    266 		       "edid %x bus %d head %x",
    267 		 outp->info.type, outp->info.location, outp->info.or,
    268 		 outp->info.type >= 2 ? outp->info.sorconf.link : 0,
    269 		 outp->info.connector, outp->info.i2c_index,
    270 		 outp->info.bus, outp->info.heads);
    271 
    272 	/* Cull output paths we can't map to an output resource. */
    273 	proto = nvkm_outp_xlat(outp, &type);
    274 	if (proto == UNKNOWN)
    275 		return -ENODEV;
    276 
    277 	return 0;
    278 }
    279 
    280 static const struct nvkm_outp_func
    281 nvkm_outp = {
    282 };
    283 
    284 int
    285 nvkm_outp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
    286 	      struct nvkm_outp **poutp)
    287 {
    288 	if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL)))
    289 		return -ENOMEM;
    290 	return nvkm_outp_ctor(&nvkm_outp, disp, index, dcbE, *poutp);
    291 }
    292