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

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

- preparation for GSP-RM
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
Reviewed-by: default avatarLyude Paul <lyude@redhat.com>
Acked-by: default avatarDanilo Krummrich <me@dakr.org>
Signed-off-by: default avatarLyude Paul <lyude@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230919220442.202488-44-lyude@redhat.com
parent 8b7d92ca
......@@ -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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment