Commit 0a4410a7 authored by Ben Skeggs's avatar Ben Skeggs Committed by Lyude Paul
Browse files

drm/nouveau/kms/nv50-: create outputs based on nvkm info

parent 8b7d92ca
Loading
Loading
Loading
Loading
+52 −45
Original line number Diff line number Diff line
@@ -66,8 +66,6 @@
#include "nouveau_fence.h"
#include "nv50_display.h"

#include <subdev/bios/dp.h>

/******************************************************************************
 * EVO channel
 *****************************************************************************/
@@ -1704,7 +1702,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta
	}

	if (head->func->display_id)
		head->func->display_id(head, BIT(nv_encoder->dcb->id));
		head->func->display_id(head, BIT(nv_encoder->outp.id));

	nv_encoder->update(nv_encoder, nv_crtc->index, asyh, proto, depth);
}
@@ -1736,16 +1734,6 @@ nv50_sor_func = {
	.destroy = nv50_sor_destroy,
};

bool nv50_has_mst(struct nouveau_drm *drm)
{
	struct nvkm_bios *bios = nvxx_bios(&drm->client.device);
	u32 data;
	u8 ver, hdr, cnt, len;

	data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len);
	return data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04);
}

static int
nv50_sor_create(struct nouveau_encoder *nv_encoder)
{
@@ -1798,15 +1786,15 @@ nv50_sor_create(struct nouveau_encoder *nv_encoder)
			nv_encoder->i2c = &nv_connector->aux.ddc;
		}

		if (nv_connector->type != DCB_CONNECTOR_eDP &&
		    nv50_has_mst(drm)) {
		if (nv_connector->type != DCB_CONNECTOR_eDP && nv_encoder->outp.info.dp.mst) {
			ret = nv50_mstm_new(nv_encoder, &nv_connector->aux,
					    16, nv_connector->base.base.id,
					    &nv_encoder->dp.mstm);
			if (ret)
				return ret;
		}
	} else {
	} else
	if (nv_encoder->outp.info.ddc != NVIF_OUTP_DDC_INVALID) {
		struct nvkm_i2c_bus *bus =
			nvkm_i2c_bus_find(i2c, dcbe->i2c_index);
		if (bus)
@@ -1927,12 +1915,12 @@ nv50_pior_create(struct nouveau_encoder *nv_encoder)

	switch (dcbe->type) {
	case DCB_OUTPUT_TMDS:
		bus  = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_EXT(dcbe->extdev));
		bus  = nvkm_i2c_bus_find(i2c, nv_encoder->outp.info.ddc);
		ddc  = bus ? &bus->i2c : NULL;
		type = DRM_MODE_ENCODER_TMDS;
		break;
	case DCB_OUTPUT_DP:
		aux  = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbe->extdev));
		aux  = nvkm_i2c_aux_find(i2c, nv_encoder->outp.info.dp.aux);
		ddc  = aux ? &aux->i2c : NULL;
		type = DRM_MODE_ENCODER_TMDS;
		break;
@@ -2693,12 +2681,10 @@ int
nv50_display_create(struct drm_device *dev)
{
	struct nouveau_drm *drm = nouveau_drm(dev);
	struct dcb_table *dcb = &drm->vbios.dcb;
	struct drm_connector *connector, *tmp;
	struct nv50_disp *disp;
	struct dcb_output *dcbe;
	int ret, i;
	bool has_mst = nv50_has_mst(drm);
	bool has_mst = false;

	disp = kzalloc(sizeof(*disp), GFP_KERNEL);
	if (!disp)
@@ -2775,54 +2761,75 @@ nv50_display_create(struct drm_device *dev)
	}

	/* create encoder/connector objects based on VBIOS DCB table */
	for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) {
	for_each_set_bit(i, &disp->disp->outp_mask, sizeof(disp->disp->outp_mask) * 8) {
		struct nouveau_encoder *outp;

		outp = kzalloc(sizeof(*outp), GFP_KERNEL);
		if (!outp)
			break;

		ret = nvif_outp_ctor(disp->disp, "kmsOutp", dcbe->id, &outp->outp);
		ret = nvif_outp_ctor(disp->disp, "kmsOutp", i, &outp->outp);
		if (ret) {
			kfree(outp);
			continue;
		}

		connector = nouveau_connector_create(dev, dcbe->connector);
		connector = nouveau_connector_create(dev, outp->outp.info.conn);
		if (IS_ERR(connector)) {
			nvif_outp_dtor(&outp->outp);
			kfree(outp);
			continue;
		}

		outp->base.base.possible_crtcs = dcbe->heads;
		outp->base.base.possible_crtcs = outp->outp.info.heads;
		outp->base.base.possible_clones = 0;
		outp->dcb = dcbe;
		outp->conn = nouveau_connector(connector);

		if (dcbe->location == DCB_LOC_ON_CHIP) {
			switch (dcbe->type) {
			case DCB_OUTPUT_TMDS:
			case DCB_OUTPUT_LVDS:
			case DCB_OUTPUT_DP:
				ret = nv50_sor_create(outp);
		outp->dcb = kzalloc(sizeof(*outp->dcb), GFP_KERNEL);
		if (!outp->dcb)
			break;
			case DCB_OUTPUT_ANALOG:
				ret = nv50_dac_create(outp);

		switch (outp->outp.info.proto) {
		case NVIF_OUTP_RGB_CRT:
			outp->dcb->type = DCB_OUTPUT_ANALOG;
			outp->dcb->crtconf.maxfreq = outp->outp.info.rgb_crt.freq_max;
			break;
			default:
				ret = -ENODEV;
		case NVIF_OUTP_TMDS:
			outp->dcb->type = DCB_OUTPUT_TMDS;
			outp->dcb->duallink_possible = outp->outp.info.tmds.dual;
			break;
		case NVIF_OUTP_LVDS:
			outp->dcb->type = DCB_OUTPUT_LVDS;
			outp->dcb->lvdsconf.use_acpi_for_edid = outp->outp.info.lvds.acpi_edid;
			break;
		case NVIF_OUTP_DP:
			outp->dcb->type = DCB_OUTPUT_DP;
			outp->dcb->dpconf.link_nr = outp->outp.info.dp.link_nr;
			outp->dcb->dpconf.link_bw = outp->outp.info.dp.link_bw;
			if (outp->outp.info.dp.mst)
				has_mst = true;
			break;
		default:
			WARN_ON(1);
			continue;
		}
		} else {
			ret = nv50_pior_create(outp);

		outp->dcb->heads = outp->outp.info.heads;
		outp->dcb->connector = outp->outp.info.conn;
		outp->dcb->i2c_index = outp->outp.info.ddc;

		switch (outp->outp.info.type) {
		case NVIF_OUTP_DAC : ret = nv50_dac_create(outp); break;
		case NVIF_OUTP_SOR : ret = nv50_sor_create(outp); break;
		case NVIF_OUTP_PIOR: ret = nv50_pior_create(outp); break;
		default:
			WARN_ON(1);
			continue;
		}

		if (ret) {
			NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n",
				     dcbe->location, dcbe->type,
				     ffs(dcbe->or) - 1, ret);
			ret = 0;
				i, outp->outp.info.type, outp->outp.info.proto, ret);
		}
	}

+0 −2
Original line number Diff line number Diff line
@@ -108,8 +108,6 @@ void nv50_dmac_destroy(struct nv50_dmac *);
 */
struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder);

bool nv50_has_mst(struct nouveau_drm *drm);

u32 *evo_wait(struct nv50_dmac *, int nr);
void evo_kick(u32 *, struct nv50_dmac *);

+30 −1
Original line number Diff line number Diff line
@@ -8,7 +8,36 @@ union nvif_outp_args {
	struct nvif_outp_v0 {
		__u8 version;
		__u8 id;	/* DCB device index. */
		__u8 pad02[6];
#define NVIF_OUTP_V0_TYPE_DAC  0x00
#define NVIF_OUTP_V0_TYPE_SOR  0x01
#define NVIF_OUTP_V0_TYPE_PIOR 0x02
		__u8 type;
#define NVIF_OUTP_V0_PROTO_RGB_CRT 0x00
#define NVIF_OUTP_V0_PROTO_TMDS    0x01
#define NVIF_OUTP_V0_PROTO_LVDS    0x02
#define NVIF_OUTP_V0_PROTO_DP      0x03
	        __u8 proto;
	        __u8 heads;
	        __u8 ddc;
	        __u8 conn;
		union {
			struct {
				__u32 freq_max;
			} rgb_crt;
			struct {
				__u8  dual;
			} tmds;
			struct {
				__u8  acpi_edid;
			} lvds;
			struct {
				__u8  aux;
				__u8  mst;
				__u8  increased_wm;
				__u8  link_nr;
				__u32 link_bw;
			} dp;
		};
	} v0;
};

+40 −0
Original line number Diff line number Diff line
@@ -8,6 +8,46 @@ struct nvif_disp;

struct nvif_outp {
	struct nvif_object object;
	u32 id;

	struct {
		enum {
			NVIF_OUTP_DAC,
			NVIF_OUTP_SOR,
			NVIF_OUTP_PIOR,
		} type;

		enum {
			NVIF_OUTP_RGB_CRT,
			NVIF_OUTP_TMDS,
			NVIF_OUTP_LVDS,
			NVIF_OUTP_DP,
		} proto;

		u8 heads;
#define NVIF_OUTP_DDC_INVALID 0xff
		u8 ddc;
		u8 conn;

		union {
			struct {
				u32 freq_max;
			} rgb_crt;
			struct {
				bool dual;
			} tmds;
			struct {
				bool acpi_edid;
			} lvds;
			struct {
				u8   aux;
				bool mst;
				bool increased_wm;
				u8   link_nr;
				u32  link_bw;
			} dp;
		};
	} info;

	struct {
		int id;
+1 −1
Original line number Diff line number Diff line
@@ -1135,7 +1135,7 @@ nouveau_connector_atomic_check(struct drm_connector *connector, struct drm_atomi
	struct drm_connector_state *conn_state =
		drm_atomic_get_new_connector_state(state, connector);

	if (!nv_conn->dp_encoder || !nv50_has_mst(nouveau_drm(connector->dev)))
	if (!nv_conn->dp_encoder || !nv_conn->dp_encoder->dp.mstm)
		return 0;

	return drm_dp_mst_root_conn_atomic_check(conn_state, &nv_conn->dp_encoder->dp.mstm->mgr);
Loading