mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 09:04:39 +02:00
drm/nouveau/kms/nv50-: create outputs based on nvkm info
- preparation for GSP-RM Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Acked-by: Danilo Krummrich <me@dakr.org> Signed-off-by: Lyude Paul <lyude@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20230919220442.202488-44-lyude@redhat.com
This commit is contained in:
parent
8b7d92cad9
commit
0a4410a79f
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user