@@ -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);
- break;
- case DCB_OUTPUT_ANALOG:
- ret = nv50_dac_create(outp);
- break;
- default:
- ret = -ENODEV;
- break;
- }
- } else {
- ret = nv50_pior_create(outp);
+ outp->dcb = kzalloc(sizeof(*outp->dcb), GFP_KERNEL);
+ if (!outp->dcb)
+ break;
+
+ 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;
+ 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;
+ }
+
+ 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);
}
}
@@ -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 *);
@@ -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;
};
@@ -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;
@@ -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);
@@ -511,6 +511,50 @@ nvif_outp_ctor(struct nvif_disp *disp, const char *name, int id, struct nvif_out
if (ret)
return ret;
+ outp->id = args.id;
+
+ switch (args.type) {
+ case NVIF_OUTP_V0_TYPE_DAC : outp->info.type = NVIF_OUTP_DAC; break;
+ case NVIF_OUTP_V0_TYPE_SOR : outp->info.type = NVIF_OUTP_SOR; break;
+ case NVIF_OUTP_V0_TYPE_PIOR: outp->info.type = NVIF_OUTP_PIOR; break;
+ break;
+ default:
+ WARN_ON(1);
+ nvif_outp_dtor(outp);
+ return -EINVAL;
+ }
+
+ switch (args.proto) {
+ case NVIF_OUTP_V0_PROTO_RGB_CRT:
+ outp->info.proto = NVIF_OUTP_RGB_CRT;
+ outp->info.rgb_crt.freq_max = args.rgb_crt.freq_max;
+ break;
+ case NVIF_OUTP_V0_PROTO_TMDS:
+ outp->info.proto = NVIF_OUTP_TMDS;
+ outp->info.tmds.dual = args.tmds.dual;
+ break;
+ case NVIF_OUTP_V0_PROTO_LVDS:
+ outp->info.proto = NVIF_OUTP_LVDS;
+ outp->info.lvds.acpi_edid = args.lvds.acpi_edid;
+ break;
+ case NVIF_OUTP_V0_PROTO_DP:
+ outp->info.proto = NVIF_OUTP_DP;
+ outp->info.dp.aux = args.dp.aux;
+ outp->info.dp.mst = args.dp.mst;
+ outp->info.dp.increased_wm = args.dp.increased_wm;
+ outp->info.dp.link_nr = args.dp.link_nr;
+ outp->info.dp.link_bw = args.dp.link_bw;
+ break;
+ default:
+ WARN_ON(1);
+ nvif_outp_dtor(outp);
+ return -EINVAL;
+ }
+
+ outp->info.heads = args.heads;
+ outp->info.ddc = args.ddc;
+ outp->info.conn = args.conn;
+
outp->or.id = -1;
return 0;
}
@@ -639,7 +639,7 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n
struct nvkm_bios *bios = device->bios;
struct nvkm_i2c *i2c = device->i2c;
struct nvkm_outp *outp;
- u8 hdr, cnt, len;
+ u8 ver, hdr, cnt, len;
u32 data;
int ret;
@@ -667,6 +667,9 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n
OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len);
+ data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len);
+ outp->dp.mst = data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04);
+
mutex_init(&outp->dp.mutex);
return 0;
}
@@ -35,6 +35,8 @@ struct nvkm_outp {
struct {
struct nvbios_dpout info;
u8 version;
+ bool mst;
+ bool increased_wm;
struct nvkm_i2c_aux *aux;
@@ -606,10 +606,60 @@ nvkm_uoutp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv
ret = -EBUSY;
spin_lock(&disp->client.lock);
if (!outp->object.func) {
+ switch (outp->info.type) {
+ case DCB_OUTPUT_ANALOG:
+ args->v0.type = NVIF_OUTP_V0_TYPE_DAC;
+ args->v0.proto = NVIF_OUTP_V0_PROTO_RGB_CRT;
+ args->v0.rgb_crt.freq_max = outp->info.crtconf.maxfreq;
+ break;
+ case DCB_OUTPUT_TMDS:
+ if (!outp->info.location) {
+ args->v0.type = NVIF_OUTP_V0_TYPE_SOR;
+ args->v0.tmds.dual = (outp->info.tmdsconf.sor.link == 3);
+ } else {
+ args->v0.type = NVIF_OUTP_V0_TYPE_PIOR;
+ args->v0.tmds.dual = 0;
+ }
+ args->v0.proto = NVIF_OUTP_V0_PROTO_TMDS;
+ break;
+ case DCB_OUTPUT_LVDS:
+ args->v0.type = NVIF_OUTP_V0_TYPE_SOR;
+ args->v0.proto = NVIF_OUTP_V0_PROTO_LVDS;
+ args->v0.lvds.acpi_edid = outp->info.lvdsconf.use_acpi_for_edid;
+ break;
+ case DCB_OUTPUT_DP:
+ if (!outp->info.location) {
+ args->v0.type = NVIF_OUTP_V0_TYPE_SOR;
+ args->v0.dp.aux = outp->info.i2c_index;
+ } else {
+ args->v0.type = NVIF_OUTP_V0_TYPE_PIOR;
+ args->v0.dp.aux = NVKM_I2C_AUX_EXT(outp->info.extdev);
+ }
+ args->v0.proto = NVIF_OUTP_V0_PROTO_DP;
+ args->v0.dp.mst = outp->dp.mst;
+ args->v0.dp.increased_wm = outp->dp.increased_wm;
+ args->v0.dp.link_nr = outp->info.dpconf.link_nr;
+ args->v0.dp.link_bw = outp->info.dpconf.link_bw * 27000;
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (outp->info.location)
+ args->v0.ddc = NVKM_I2C_BUS_EXT(outp->info.extdev);
+ else
+ args->v0.ddc = outp->info.i2c_index;
+ args->v0.heads = outp->info.heads;
+ args->v0.conn = outp->info.connector;
+
nvkm_object_ctor(&nvkm_uoutp, oclass, &outp->object);
*pobject = &outp->object;
ret = 0;
}
+
+done:
spin_unlock(&disp->client.lock);
return ret;
}