brintos

brintos / linux-shallow public Read only

0
0
Text · 17.7 KiB · 377d0e0 Raw
666 lines · c
1/*2 * Copyright 2021 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 */22#define nvkm_uoutp(p) container_of((p), struct nvkm_outp, object)23#include "outp.h"24#include "dp.h"25#include "head.h"26#include "ior.h"27 28#include <subdev/i2c.h>29 30#include <nvif/if0012.h>31 32static int33nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp *outp, void *argv, u32 argc)34{35	struct nvkm_ior *ior = outp->ior;36	union nvif_outp_dp_mst_vcpi_args *args = argv;37 38	if (argc != sizeof(args->v0) || args->v0.version != 0)39		return -ENOSYS;40	if (!ior->func->dp || !ior->func->dp->vcpi || !nvkm_head_find(outp->disp, args->v0.head))41		return -EINVAL;42 43	ior->func->dp->vcpi(ior, args->v0.head, args->v0.start_slot, args->v0.num_slots,44				 args->v0.pbn, args->v0.aligned_pbn);45	return 0;46}47 48static int49nvkm_uoutp_mthd_dp_mst_id_put(struct nvkm_outp *outp, void *argv, u32 argc)50{51	union nvif_outp_dp_mst_id_put_args *args = argv;52 53	if (argc != sizeof(args->v0) || args->v0.version != 0)54	        return -ENOSYS;55	if (!outp->func->dp.mst_id_put)56	        return -EINVAL;57 58	return outp->func->dp.mst_id_put(outp, args->v0.id);59}60 61static int62nvkm_uoutp_mthd_dp_mst_id_get(struct nvkm_outp *outp, void *argv, u32 argc)63{64	union nvif_outp_dp_mst_id_get_args *args = argv;65 66	if (argc != sizeof(args->v0) || args->v0.version != 0)67	        return -ENOSYS;68	if (!outp->func->dp.mst_id_get)69	        return -EINVAL;70 71	return outp->func->dp.mst_id_get(outp, &args->v0.id);72}73 74static int75nvkm_uoutp_mthd_dp_sst(struct nvkm_outp *outp, void *argv, u32 argc)76{77	union nvif_outp_dp_sst_args *args = argv;78	struct nvkm_disp *disp = outp->disp;79	struct nvkm_ior *ior = outp->ior;80 81	if (argc != sizeof(args->v0) || args->v0.version != 0)82		return -ENOSYS;83 84	if (!ior->func->dp || !nvkm_head_find(disp, args->v0.head))85		return -EINVAL;86	if (!ior->func->dp->sst)87		return 0;88 89	return ior->func->dp->sst(ior, args->v0.head,90				  outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP,91				  args->v0.watermark, args->v0.hblanksym, args->v0.vblanksym);92}93 94static int95nvkm_uoutp_mthd_dp_drive(struct nvkm_outp *outp, void *argv, u32 argc)96{97	union nvif_outp_dp_drive_args *args = argv;98 99	if (argc != sizeof(args->v0) || args->v0.version != 0)100		return -ENOSYS;101	if (!outp->func->dp.drive)102		return -EINVAL;103 104	return outp->func->dp.drive(outp, args->v0.lanes, args->v0.pe, args->v0.vs);105}106 107static int108nvkm_uoutp_mthd_dp_train(struct nvkm_outp *outp, void *argv, u32 argc)109{110	union nvif_outp_dp_train_args *args = argv;111 112	if (argc != sizeof(args->v0) || args->v0.version != 0)113		return -ENOSYS;114	if (!outp->func->dp.train)115		return -EINVAL;116 117	if (!args->v0.retrain) {118		memcpy(outp->dp.dpcd, args->v0.dpcd, sizeof(outp->dp.dpcd));119		outp->dp.lttprs = args->v0.lttprs;120		outp->dp.lt.nr = args->v0.link_nr;121		outp->dp.lt.bw = args->v0.link_bw / 27000;122		outp->dp.lt.mst = args->v0.mst;123		outp->dp.lt.post_adj = args->v0.post_lt_adj;124	}125 126	return outp->func->dp.train(outp, args->v0.retrain);127}128 129static int130nvkm_uoutp_mthd_dp_rates(struct nvkm_outp *outp, void *argv, u32 argc)131{132	union nvif_outp_dp_rates_args *args = argv;133 134	if (argc != sizeof(args->v0) || args->v0.version != 0)135		return -ENOSYS;136	if (args->v0.rates > ARRAY_SIZE(outp->dp.rate))137		return -EINVAL;138 139	for (int i = 0; i < args->v0.rates; i++) {140		outp->dp.rate[i].dpcd = args->v0.rate[i].dpcd;141		outp->dp.rate[i].rate = args->v0.rate[i].rate;142	}143 144	outp->dp.rates = args->v0.rates;145 146	if (outp->func->dp.rates)147		outp->func->dp.rates(outp);148 149	return 0;150}151 152static int153nvkm_uoutp_mthd_dp_aux_xfer(struct nvkm_outp *outp, void *argv, u32 argc)154{155	union nvif_outp_dp_aux_xfer_args *args = argv;156 157	if (argc != sizeof(args->v0) || args->v0.version != 0)158		return -ENOSYS;159	if (!outp->func->dp.aux_xfer)160		return -EINVAL;161 162	return outp->func->dp.aux_xfer(outp, args->v0.type, args->v0.addr,163					     args->v0.data, &args->v0.size);164}165 166static int167nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp *outp, void *argv, u32 argc)168{169	union nvif_outp_dp_aux_pwr_args *args = argv;170 171	if (argc != sizeof(args->v0) || args->v0.version != 0)172		return -ENOSYS;173	if (!outp->func->dp.aux_pwr)174		return -EINVAL;175 176	return outp->func->dp.aux_pwr(outp, !!args->v0.state);177}178 179static int180nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc)181{182	struct nvkm_ior *ior = outp->ior;183	union nvif_outp_hda_eld_args *args = argv;184 185	if (argc < sizeof(args->v0) || args->v0.version != 0)186		return -ENOSYS;187	argc -= sizeof(args->v0);188 189	if (!ior->hda || !nvkm_head_find(outp->disp, args->v0.head))190		return -EINVAL;191	if (argc > 0x60)192		return -E2BIG;193 194	if (argc && args->v0.data[0]) {195		if (outp->info.type == DCB_OUTPUT_DP)196			ior->func->dp->audio(ior, args->v0.head, true);197		else198		if (ior->func->hdmi->audio)199			ior->func->hdmi->audio(ior, args->v0.head, true);200 201		ior->func->hda->hpd(ior, args->v0.head, true);202		ior->func->hda->eld(ior, args->v0.head, args->v0.data, argc);203	} else {204		ior->func->hda->hpd(ior, args->v0.head, false);205 206		if (outp->info.type == DCB_OUTPUT_DP)207			ior->func->dp->audio(ior, args->v0.head, false);208		else209		if (ior->func->hdmi->audio)210			ior->func->hdmi->audio(ior, args->v0.head, false);211	}212 213	return 0;214}215 216static int217nvkm_uoutp_mthd_infoframe(struct nvkm_outp *outp, void *argv, u32 argc)218{219	struct nvkm_ior *ior = outp->ior;220	union nvif_outp_infoframe_args *args = argv;221	ssize_t size = argc - sizeof(*args);222 223	if (argc < sizeof(args->v0) || args->v0.version != 0)224		return -ENOSYS;225	if (!nvkm_head_find(outp->disp, args->v0.head))226		return -EINVAL;227 228	switch (ior->func->hdmi ? args->v0.type : 0xff) {229	case NVIF_OUTP_INFOFRAME_V0_AVI:230		ior->func->hdmi->infoframe_avi(ior, args->v0.head, &args->v0.data, size);231		return 0;232	case NVIF_OUTP_INFOFRAME_V0_VSI:233		ior->func->hdmi->infoframe_vsi(ior, args->v0.head, &args->v0.data, size);234		return 0;235	default:236		break;237	}238 239	return -EINVAL;240}241 242static int243nvkm_uoutp_mthd_hdmi(struct nvkm_outp *outp, void *argv, u32 argc)244{245	union nvif_outp_hdmi_args *args = argv;246	struct nvkm_ior *ior = outp->ior;247 248	if (argc != sizeof(args->v0) || args->v0.version != 0)249		return -ENOSYS;250 251	if (!(outp->asy.head = nvkm_head_find(outp->disp, args->v0.head)))252		return -EINVAL;253 254	if (!ior->func->hdmi ||255	    args->v0.max_ac_packet > 0x1f ||256	    args->v0.rekey > 0x7f ||257	    (args->v0.scdc && !ior->func->hdmi->scdc))258		return -EINVAL;259 260	if (!args->v0.enable) {261		ior->func->hdmi->infoframe_avi(ior, args->v0.head, NULL, 0);262		ior->func->hdmi->infoframe_vsi(ior, args->v0.head, NULL, 0);263		ior->func->hdmi->ctrl(ior, args->v0.head, false, 0, 0);264		return 0;265	}266 267	ior->func->hdmi->ctrl(ior, args->v0.head, args->v0.enable,268			      args->v0.max_ac_packet, args->v0.rekey);269	if (ior->func->hdmi->scdc)270		ior->func->hdmi->scdc(ior, args->v0.khz, args->v0.scdc, args->v0.scdc_scrambling,271				      args->v0.scdc_low_rates);272 273	return 0;274}275 276static int277nvkm_uoutp_mthd_lvds(struct nvkm_outp *outp, void *argv, u32 argc)278{279	union nvif_outp_lvds_args *args = argv;280 281	if (argc != sizeof(args->v0) || args->v0.version != 0)282		return -ENOSYS;283	if (outp->info.type != DCB_OUTPUT_LVDS)284		return -EINVAL;285 286	outp->lvds.dual = !!args->v0.dual;287	outp->lvds.bpc8 = !!args->v0.bpc8;288	return 0;289}290 291static int292nvkm_uoutp_mthd_bl_set(struct nvkm_outp *outp, void *argv, u32 argc)293{294	union nvif_outp_bl_get_args *args = argv;295	int ret;296 297	if (argc != sizeof(args->v0) || args->v0.version != 0)298		return -ENOSYS;299 300	if (outp->func->bl.set)301		ret = outp->func->bl.set(outp, args->v0.level);302	else303		ret = -EINVAL;304 305	return ret;306}307 308static int309nvkm_uoutp_mthd_bl_get(struct nvkm_outp *outp, void *argv, u32 argc)310{311	union nvif_outp_bl_get_args *args = argv;312	int ret;313 314	if (argc != sizeof(args->v0) || args->v0.version != 0)315		return -ENOSYS;316 317	if (outp->func->bl.get) {318		ret = outp->func->bl.get(outp);319		if (ret >= 0) {320			args->v0.level = ret;321			ret = 0;322		}323	} else {324		ret = -EINVAL;325	}326 327	return ret;328}329 330static int331nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc)332{333	union nvif_outp_release_args *args = argv;334 335	if (argc != sizeof(args->vn))336		return -ENOSYS;337	if (!outp->ior)338		return -EINVAL;339 340	outp->func->release(outp);341	return 0;342}343 344static int345nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)346{347	union nvif_outp_acquire_args *args = argv;348	int ret;349 350	if (argc != sizeof(args->v0) || args->v0.version != 0)351		return -ENOSYS;352	if (outp->ior && args->v0.type <= NVIF_OUTP_ACQUIRE_V0_PIOR)353		return -EBUSY;354 355	switch (args->v0.type) {356	case NVIF_OUTP_ACQUIRE_V0_DAC:357	case NVIF_OUTP_ACQUIRE_V0_PIOR:358		ret = outp->func->acquire(outp, false);359		break;360	case NVIF_OUTP_ACQUIRE_V0_SOR:361		ret = outp->func->acquire(outp, args->v0.sor.hda);362		break;363	default:364		ret = -EINVAL;365		break;366	}367 368	if (ret)369		return ret;370 371	args->v0.or = outp->ior->id;372	args->v0.link = outp->ior->asy.link;373	return 0;374}375 376static int377nvkm_uoutp_mthd_inherit(struct nvkm_outp *outp, void *argv, u32 argc)378{379	union nvif_outp_inherit_args *args = argv;380	struct nvkm_ior *ior;381	int ret = 0;382 383	if (argc != sizeof(args->v0) || args->v0.version != 0)384		return -ENOSYS;385 386	/* Ensure an ior is hooked up to this outp already */387	ior = outp->func->inherit(outp);388	if (!ior || !ior->arm.head)389		return -ENODEV;390 391	/* With iors, there will be a separate output path for each type of connector - and all of392	 * them will appear to be hooked up. Figure out which one is actually the one we're using393	 * based on the protocol we were given over nvif394	 */395	switch (args->v0.proto) {396	case NVIF_OUTP_INHERIT_V0_TMDS:397		if (ior->arm.proto != TMDS)398			return -ENODEV;399		break;400	case NVIF_OUTP_INHERIT_V0_DP:401		if (ior->arm.proto != DP)402			return -ENODEV;403		break;404	case NVIF_OUTP_INHERIT_V0_LVDS:405		if (ior->arm.proto != LVDS)406			return -ENODEV;407		break;408	case NVIF_OUTP_INHERIT_V0_TV:409		if (ior->arm.proto != TV)410			return -ENODEV;411		break;412	case NVIF_OUTP_INHERIT_V0_RGB_CRT:413		if (ior->arm.proto != CRT)414			return -ENODEV;415		break;416	default:417		ret = -EINVAL;418		break;419	}420 421	/* Make sure that userspace hasn't already acquired this */422	if (outp->acquired) {423		OUTP_ERR(outp, "cannot inherit an already acquired (%02x) outp", outp->acquired);424		return -EBUSY;425	}426 427	/* Mark the outp acquired by userspace now that we've confirmed it's already active */428	OUTP_TRACE(outp, "inherit %02x |= %02x %p", outp->acquired, NVKM_OUTP_USER, ior);429	nvkm_outp_acquire_ior(outp, NVKM_OUTP_USER, ior);430 431	args->v0.or = ior->id;432	args->v0.link = ior->arm.link;433	args->v0.head = ffs(ior->arm.head) - 1;434	args->v0.proto = ior->arm.proto_evo;435 436	return ret;437}438 439static int440nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc)441{442	union nvif_outp_load_detect_args *args = argv;443	int ret;444 445	if (argc != sizeof(args->v0) || args->v0.version != 0)446		return -ENOSYS;447 448	ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false);449	if (ret == 0) {450		if (outp->ior->func->sense) {451			ret = outp->ior->func->sense(outp->ior, args->v0.data);452			args->v0.load = ret < 0 ? 0 : ret;453		} else {454			ret = -EINVAL;455		}456		nvkm_outp_release_or(outp, NVKM_OUTP_PRIV);457	}458 459	return ret;460}461 462static int463nvkm_uoutp_mthd_edid_get(struct nvkm_outp *outp, void *argv, u32 argc)464{465	union nvif_outp_edid_get_args *args = argv;466 467	if (argc != sizeof(args->v0) || args->v0.version != 0)468		return -ENOSYS;469	if (!outp->func->edid_get)470		return -EINVAL;471 472	args->v0.size = ARRAY_SIZE(args->v0.data);473	return outp->func->edid_get(outp, args->v0.data, &args->v0.size);474}475 476static int477nvkm_uoutp_mthd_detect(struct nvkm_outp *outp, void *argv, u32 argc)478{479	union nvif_outp_detect_args *args = argv;480	int ret;481 482	if (argc != sizeof(args->v0) || args->v0.version != 0)483		return -ENOSYS;484	if (!outp->func->detect)485		return -EINVAL;486 487	ret = outp->func->detect(outp);488	switch (ret) {489	case 0: args->v0.status = NVIF_OUTP_DETECT_V0_NOT_PRESENT; break;490	case 1: args->v0.status = NVIF_OUTP_DETECT_V0_PRESENT; break;491	default:492		args->v0.status = NVIF_OUTP_DETECT_V0_UNKNOWN;493		break;494	}495 496	return 0;497}498 499static int500nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc)501{502	switch (mthd) {503	case NVIF_OUTP_V0_RELEASE      : return nvkm_uoutp_mthd_release      (outp, argv, argc);504	case NVIF_OUTP_V0_LVDS         : return nvkm_uoutp_mthd_lvds         (outp, argv, argc);505	case NVIF_OUTP_V0_HDMI         : return nvkm_uoutp_mthd_hdmi         (outp, argv, argc);506	case NVIF_OUTP_V0_INFOFRAME    : return nvkm_uoutp_mthd_infoframe    (outp, argv, argc);507	case NVIF_OUTP_V0_HDA_ELD      : return nvkm_uoutp_mthd_hda_eld      (outp, argv, argc);508	case NVIF_OUTP_V0_DP_TRAIN     : return nvkm_uoutp_mthd_dp_train     (outp, argv, argc);509	case NVIF_OUTP_V0_DP_DRIVE     : return nvkm_uoutp_mthd_dp_drive     (outp, argv, argc);510	case NVIF_OUTP_V0_DP_SST       : return nvkm_uoutp_mthd_dp_sst       (outp, argv, argc);511	case NVIF_OUTP_V0_DP_MST_ID_GET: return nvkm_uoutp_mthd_dp_mst_id_get(outp, argv, argc);512	case NVIF_OUTP_V0_DP_MST_ID_PUT: return nvkm_uoutp_mthd_dp_mst_id_put(outp, argv, argc);513	case NVIF_OUTP_V0_DP_MST_VCPI  : return nvkm_uoutp_mthd_dp_mst_vcpi  (outp, argv, argc);514	default:515		break;516	}517 518	return -EINVAL;519}520 521static int522nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc, bool *invalid)523{524	switch (mthd) {525	case NVIF_OUTP_V0_DETECT     : return nvkm_uoutp_mthd_detect     (outp, argv, argc);526	case NVIF_OUTP_V0_EDID_GET   : return nvkm_uoutp_mthd_edid_get   (outp, argv, argc);527	case NVIF_OUTP_V0_INHERIT    : return nvkm_uoutp_mthd_inherit    (outp, argv, argc);528	case NVIF_OUTP_V0_ACQUIRE    : return nvkm_uoutp_mthd_acquire    (outp, argv, argc);529	case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc);530	case NVIF_OUTP_V0_BL_GET     : return nvkm_uoutp_mthd_bl_get     (outp, argv, argc);531	case NVIF_OUTP_V0_BL_SET     : return nvkm_uoutp_mthd_bl_set     (outp, argv, argc);532	case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc);533	case NVIF_OUTP_V0_DP_AUX_XFER: return nvkm_uoutp_mthd_dp_aux_xfer(outp, argv, argc);534	case NVIF_OUTP_V0_DP_RATES   : return nvkm_uoutp_mthd_dp_rates   (outp, argv, argc);535	default:536		break;537	}538 539	*invalid = true;540	return 0;541}542 543static int544nvkm_uoutp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc)545{546	struct nvkm_outp *outp = nvkm_uoutp(object);547	struct nvkm_disp *disp = outp->disp;548	bool invalid = false;549	int ret;550 551	mutex_lock(&disp->super.mutex);552 553	ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc, &invalid);554	if (!invalid)555		goto done;556 557	if (outp->ior)558		ret = nvkm_uoutp_mthd_acquired(outp, mthd, argv, argc);559	else560		ret = -EIO;561 562done:563	mutex_unlock(&disp->super.mutex);564	return ret;565}566 567static void *568nvkm_uoutp_dtor(struct nvkm_object *object)569{570	struct nvkm_outp *outp = nvkm_uoutp(object);571	struct nvkm_disp *disp = outp->disp;572 573	spin_lock(&disp->client.lock);574	outp->object.func = NULL;575	spin_unlock(&disp->client.lock);576	return NULL;577}578 579static const struct nvkm_object_func580nvkm_uoutp = {581	.dtor = nvkm_uoutp_dtor,582	.mthd = nvkm_uoutp_mthd,583};584 585int586nvkm_uoutp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nvkm_object **pobject)587{588	struct nvkm_disp *disp = nvkm_udisp(oclass->parent);589	struct nvkm_outp *outt, *outp = NULL;590	union nvif_outp_args *args = argv;591	int ret;592 593	if (argc != sizeof(args->v0) || args->v0.version != 0)594		return -ENOSYS;595 596	list_for_each_entry(outt, &disp->outps, head) {597		if (outt->index == args->v0.id) {598			outp = outt;599			break;600		}601	}602 603	if (!outp)604		return -EINVAL;605 606	ret = -EBUSY;607	spin_lock(&disp->client.lock);608	if (!outp->object.func) {609		switch (outp->info.type) {610		case DCB_OUTPUT_ANALOG:611			args->v0.type = NVIF_OUTP_V0_TYPE_DAC;612			args->v0.proto = NVIF_OUTP_V0_PROTO_RGB_CRT;613			args->v0.rgb_crt.freq_max = outp->info.crtconf.maxfreq;614			break;615		case DCB_OUTPUT_TMDS:616			if (!outp->info.location) {617				args->v0.type = NVIF_OUTP_V0_TYPE_SOR;618				args->v0.tmds.dual = (outp->info.tmdsconf.sor.link == 3);619			} else {620				args->v0.type = NVIF_OUTP_V0_TYPE_PIOR;621				args->v0.tmds.dual = 0;622			}623			args->v0.proto = NVIF_OUTP_V0_PROTO_TMDS;624			break;625		case DCB_OUTPUT_LVDS:626			args->v0.type = NVIF_OUTP_V0_TYPE_SOR;627			args->v0.proto = NVIF_OUTP_V0_PROTO_LVDS;628			args->v0.lvds.acpi_edid = outp->info.lvdsconf.use_acpi_for_edid;629			break;630		case DCB_OUTPUT_DP:631			if (!outp->info.location) {632				args->v0.type = NVIF_OUTP_V0_TYPE_SOR;633				args->v0.dp.aux = outp->info.i2c_index;634			} else {635				args->v0.type = NVIF_OUTP_V0_TYPE_PIOR;636				args->v0.dp.aux = NVKM_I2C_AUX_EXT(outp->info.extdev);637			}638			args->v0.proto = NVIF_OUTP_V0_PROTO_DP;639			args->v0.dp.mst = outp->dp.mst;640			args->v0.dp.increased_wm = outp->dp.increased_wm;641			args->v0.dp.link_nr = outp->info.dpconf.link_nr;642			args->v0.dp.link_bw = outp->info.dpconf.link_bw * 27000;643			break;644		default:645			WARN_ON(1);646			ret = -EINVAL;647			goto done;648		}649 650		if (outp->info.location)651			args->v0.ddc = NVKM_I2C_BUS_EXT(outp->info.extdev);652		else653			args->v0.ddc = outp->info.i2c_index;654		args->v0.heads = outp->info.heads;655		args->v0.conn = outp->info.connector;656 657		nvkm_object_ctor(&nvkm_uoutp, oclass, &outp->object);658		*pobject = &outp->object;659		ret = 0;660	}661 662done:663	spin_unlock(&disp->client.lock);664	return ret;665}666