forked from Minki/linux
drm/nouveau/disp: transition outp/conn away from being based on nvkm_object
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
2aa5eac516
commit
f2c906fc0c
@ -7,6 +7,7 @@ struct nvkm_disp {
|
|||||||
struct nvkm_engine engine;
|
struct nvkm_engine engine;
|
||||||
|
|
||||||
struct list_head outp;
|
struct list_head outp;
|
||||||
|
struct list_head conn;
|
||||||
|
|
||||||
struct nvkm_event hpd;
|
struct nvkm_event hpd;
|
||||||
struct nvkm_event vblank;
|
struct nvkm_event vblank;
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
nvkm-y += nvkm/engine/disp/base.o
|
nvkm-y += nvkm/engine/disp/base.o
|
||||||
nvkm-y += nvkm/engine/disp/conn.o
|
|
||||||
nvkm-y += nvkm/engine/disp/outp.o
|
|
||||||
nvkm-y += nvkm/engine/disp/outpdp.o
|
|
||||||
nvkm-y += nvkm/engine/disp/nv04.o
|
nvkm-y += nvkm/engine/disp/nv04.o
|
||||||
nvkm-y += nvkm/engine/disp/nv50.o
|
nvkm-y += nvkm/engine/disp/nv50.o
|
||||||
nvkm-y += nvkm/engine/disp/g84.o
|
nvkm-y += nvkm/engine/disp/g84.o
|
||||||
@ -13,17 +10,25 @@ nvkm-y += nvkm/engine/disp/gk104.o
|
|||||||
nvkm-y += nvkm/engine/disp/gk110.o
|
nvkm-y += nvkm/engine/disp/gk110.o
|
||||||
nvkm-y += nvkm/engine/disp/gm107.o
|
nvkm-y += nvkm/engine/disp/gm107.o
|
||||||
nvkm-y += nvkm/engine/disp/gm204.o
|
nvkm-y += nvkm/engine/disp/gm204.o
|
||||||
|
|
||||||
|
nvkm-y += nvkm/engine/disp/outp.o
|
||||||
|
nvkm-y += nvkm/engine/disp/outpdp.o
|
||||||
nvkm-y += nvkm/engine/disp/dacnv50.o
|
nvkm-y += nvkm/engine/disp/dacnv50.o
|
||||||
nvkm-y += nvkm/engine/disp/dport.o
|
|
||||||
nvkm-y += nvkm/engine/disp/hdagt215.o
|
|
||||||
nvkm-y += nvkm/engine/disp/hdagf110.o
|
|
||||||
nvkm-y += nvkm/engine/disp/hdmig84.o
|
|
||||||
nvkm-y += nvkm/engine/disp/hdmigt215.o
|
|
||||||
nvkm-y += nvkm/engine/disp/hdmigf110.o
|
|
||||||
nvkm-y += nvkm/engine/disp/hdmigk104.o
|
|
||||||
nvkm-y += nvkm/engine/disp/piornv50.o
|
nvkm-y += nvkm/engine/disp/piornv50.o
|
||||||
nvkm-y += nvkm/engine/disp/sornv50.o
|
nvkm-y += nvkm/engine/disp/sornv50.o
|
||||||
nvkm-y += nvkm/engine/disp/sorg94.o
|
nvkm-y += nvkm/engine/disp/sorg94.o
|
||||||
nvkm-y += nvkm/engine/disp/sorgf110.o
|
nvkm-y += nvkm/engine/disp/sorgf110.o
|
||||||
nvkm-y += nvkm/engine/disp/sorgm204.o
|
nvkm-y += nvkm/engine/disp/sorgm204.o
|
||||||
|
nvkm-y += nvkm/engine/disp/dport.o
|
||||||
|
|
||||||
|
nvkm-y += nvkm/engine/disp/conn.o
|
||||||
|
|
||||||
|
nvkm-y += nvkm/engine/disp/hdagt215.o
|
||||||
|
nvkm-y += nvkm/engine/disp/hdagf110.o
|
||||||
|
|
||||||
|
nvkm-y += nvkm/engine/disp/hdmig84.o
|
||||||
|
nvkm-y += nvkm/engine/disp/hdmigt215.o
|
||||||
|
nvkm-y += nvkm/engine/disp/hdmigf110.o
|
||||||
|
nvkm-y += nvkm/engine/disp/hdmigk104.o
|
||||||
|
|
||||||
nvkm-y += nvkm/engine/disp/vga.o
|
nvkm-y += nvkm/engine/disp/vga.o
|
||||||
|
@ -118,29 +118,25 @@ int
|
|||||||
_nvkm_disp_fini(struct nvkm_object *object, bool suspend)
|
_nvkm_disp_fini(struct nvkm_object *object, bool suspend)
|
||||||
{
|
{
|
||||||
struct nvkm_disp *disp = (void *)object;
|
struct nvkm_disp *disp = (void *)object;
|
||||||
|
struct nvkm_connector *conn;
|
||||||
struct nvkm_output *outp;
|
struct nvkm_output *outp;
|
||||||
int ret;
|
|
||||||
|
|
||||||
list_for_each_entry(outp, &disp->outp, head) {
|
list_for_each_entry(outp, &disp->outp, head) {
|
||||||
ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend);
|
nvkm_output_fini(outp);
|
||||||
if (ret && suspend)
|
}
|
||||||
goto fail_outp;
|
|
||||||
|
list_for_each_entry(conn, &disp->conn, head) {
|
||||||
|
nvkm_connector_fini(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nvkm_engine_fini(&disp->engine, suspend);
|
return nvkm_engine_fini(&disp->engine, suspend);
|
||||||
|
|
||||||
fail_outp:
|
|
||||||
list_for_each_entry_continue_reverse(outp, &disp->outp, head) {
|
|
||||||
nv_ofuncs(outp)->init(nv_object(outp));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_nvkm_disp_init(struct nvkm_object *object)
|
_nvkm_disp_init(struct nvkm_object *object)
|
||||||
{
|
{
|
||||||
struct nvkm_disp *disp = (void *)object;
|
struct nvkm_disp *disp = (void *)object;
|
||||||
|
struct nvkm_connector *conn;
|
||||||
struct nvkm_output *outp;
|
struct nvkm_output *outp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -148,17 +144,12 @@ _nvkm_disp_init(struct nvkm_object *object)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
list_for_each_entry(outp, &disp->outp, head) {
|
list_for_each_entry(conn, &disp->conn, head) {
|
||||||
ret = nv_ofuncs(outp)->init(nv_object(outp));
|
nvkm_connector_init(conn);
|
||||||
if (ret)
|
|
||||||
goto fail_outp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
list_for_each_entry(outp, &disp->outp, head) {
|
||||||
|
nvkm_output_init(outp);
|
||||||
fail_outp:
|
|
||||||
list_for_each_entry_continue_reverse(outp, &disp->outp, head) {
|
|
||||||
nv_ofuncs(outp)->fini(nv_object(outp), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -168,15 +159,22 @@ void
|
|||||||
_nvkm_disp_dtor(struct nvkm_object *object)
|
_nvkm_disp_dtor(struct nvkm_object *object)
|
||||||
{
|
{
|
||||||
struct nvkm_disp *disp = (void *)object;
|
struct nvkm_disp *disp = (void *)object;
|
||||||
struct nvkm_output *outp, *outt;
|
struct nvkm_connector *conn;
|
||||||
|
struct nvkm_output *outp;
|
||||||
|
|
||||||
nvkm_event_fini(&disp->vblank);
|
nvkm_event_fini(&disp->vblank);
|
||||||
nvkm_event_fini(&disp->hpd);
|
nvkm_event_fini(&disp->hpd);
|
||||||
|
|
||||||
if (disp->outp.next) {
|
while (!list_empty(&disp->outp)) {
|
||||||
list_for_each_entry_safe(outp, outt, &disp->outp, head) {
|
outp = list_first_entry(&disp->outp, typeof(*outp), head);
|
||||||
nvkm_object_ref(NULL, (struct nvkm_object **)&outp);
|
list_del(&outp->head);
|
||||||
}
|
nvkm_output_del(&outp);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!list_empty(&disp->conn)) {
|
||||||
|
conn = list_first_entry(&disp->conn, typeof(*conn), head);
|
||||||
|
list_del(&conn->head);
|
||||||
|
nvkm_connector_del(&conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
nvkm_engine_destroy(&disp->engine);
|
nvkm_engine_destroy(&disp->engine);
|
||||||
@ -188,10 +186,12 @@ nvkm_disp_create_(struct nvkm_object *parent, struct nvkm_object *engine,
|
|||||||
const char *extname, int length, void **pobject)
|
const char *extname, int length, void **pobject)
|
||||||
{
|
{
|
||||||
struct nvkm_disp_impl *impl = (void *)oclass;
|
struct nvkm_disp_impl *impl = (void *)oclass;
|
||||||
struct nvkm_bios *bios = nvkm_bios(parent);
|
struct nvkm_device *device = (void *)parent;
|
||||||
|
struct nvkm_bios *bios = device->bios;
|
||||||
struct nvkm_disp *disp;
|
struct nvkm_disp *disp;
|
||||||
struct nvkm_oclass **sclass;
|
struct nvkm_connector *conn;
|
||||||
struct nvkm_object *object;
|
struct nvkm_output *outp, *outt, *pair;
|
||||||
|
struct nvbios_connE connE;
|
||||||
struct dcb_output dcbE;
|
struct dcb_output dcbE;
|
||||||
u8 hpd = 0, ver, hdr;
|
u8 hpd = 0, ver, hdr;
|
||||||
u32 data;
|
u32 data;
|
||||||
@ -204,30 +204,124 @@ nvkm_disp_create_(struct nvkm_object *parent, struct nvkm_object *engine,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&disp->outp);
|
INIT_LIST_HEAD(&disp->outp);
|
||||||
|
INIT_LIST_HEAD(&disp->conn);
|
||||||
|
|
||||||
/* create output objects for each display path in the vbios */
|
/* create output objects for each display path in the vbios */
|
||||||
i = -1;
|
i = -1;
|
||||||
while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
|
while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
|
||||||
|
const struct nvkm_disp_func_outp *outps;
|
||||||
|
int (*ctor)(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
|
||||||
if (dcbE.type == DCB_OUTPUT_UNUSED)
|
if (dcbE.type == DCB_OUTPUT_UNUSED)
|
||||||
continue;
|
continue;
|
||||||
if (dcbE.type == DCB_OUTPUT_EOL)
|
if (dcbE.type == DCB_OUTPUT_EOL)
|
||||||
break;
|
break;
|
||||||
data = dcbE.location << 4 | dcbE.type;
|
outp = NULL;
|
||||||
|
|
||||||
oclass = nvkm_output_oclass;
|
switch (dcbE.location) {
|
||||||
sclass = impl->outp;
|
case 0: outps = &impl->outp.internal; break;
|
||||||
while (sclass && sclass[0]) {
|
case 1: outps = &impl->outp.external; break;
|
||||||
if (sclass[0]->handle == data) {
|
default:
|
||||||
oclass = sclass[0];
|
nvkm_warn(&disp->engine.subdev,
|
||||||
break;
|
"dcb %d locn %d unknown\n", i, dcbE.location);
|
||||||
}
|
continue;
|
||||||
sclass++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nvkm_object_ctor(*pobject, NULL, oclass, &dcbE, i, &object);
|
switch (dcbE.type) {
|
||||||
|
case DCB_OUTPUT_ANALOG: ctor = outps->crt ; break;
|
||||||
|
case DCB_OUTPUT_TV : ctor = outps->tv ; break;
|
||||||
|
case DCB_OUTPUT_TMDS : ctor = outps->tmds; break;
|
||||||
|
case DCB_OUTPUT_LVDS : ctor = outps->lvds; break;
|
||||||
|
case DCB_OUTPUT_DP : ctor = outps->dp ; break;
|
||||||
|
default:
|
||||||
|
nvkm_warn(&disp->engine.subdev,
|
||||||
|
"dcb %d type %d unknown\n", i, dcbE.type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctor)
|
||||||
|
ret = ctor(disp, i, &dcbE, &outp);
|
||||||
|
else
|
||||||
|
ret = -ENODEV;
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
if (ret == -ENODEV) {
|
||||||
|
nvkm_debug(&disp->engine.subdev,
|
||||||
|
"dcb %d %d/%d not supported\n",
|
||||||
|
i, dcbE.location, dcbE.type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nvkm_error(&disp->engine.subdev,
|
||||||
|
"failed to create output %d\n", i);
|
||||||
|
nvkm_output_del(&outp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&outp->head, &disp->outp);
|
||||||
hpd = max(hpd, (u8)(dcbE.connector + 1));
|
hpd = max(hpd, (u8)(dcbE.connector + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* create connector objects based on the outputs we support */
|
||||||
|
list_for_each_entry_safe(outp, outt, &disp->outp, head) {
|
||||||
|
/* bios data *should* give us the most useful information */
|
||||||
|
data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr,
|
||||||
|
&connE);
|
||||||
|
|
||||||
|
/* no bios connector data... */
|
||||||
|
if (!data) {
|
||||||
|
/* heuristic: anything with the same ccb index is
|
||||||
|
* considered to be on the same connector, any
|
||||||
|
* output path without an associated ccb entry will
|
||||||
|
* be put on its own connector
|
||||||
|
*/
|
||||||
|
int ccb_index = outp->info.i2c_index;
|
||||||
|
if (ccb_index != 0xf) {
|
||||||
|
list_for_each_entry(pair, &disp->outp, head) {
|
||||||
|
if (pair->info.i2c_index == ccb_index) {
|
||||||
|
outp->conn = pair->conn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* connector shared with another output path */
|
||||||
|
if (outp->conn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
memset(&connE, 0x00, sizeof(connE));
|
||||||
|
connE.type = DCB_CONNECTOR_NONE;
|
||||||
|
i = -1;
|
||||||
|
} else {
|
||||||
|
i = outp->info.connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that we haven't already created this connector */
|
||||||
|
list_for_each_entry(conn, &disp->conn, head) {
|
||||||
|
if (conn->index == outp->info.connector) {
|
||||||
|
outp->conn = conn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outp->conn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* apparently we need to create a new one! */
|
||||||
|
ret = nvkm_connector_new(disp, i, &connE, &outp->conn);
|
||||||
|
if (ret) {
|
||||||
|
nvkm_error(&disp->engine.subdev,
|
||||||
|
"failed to create output %d conn: %d\n",
|
||||||
|
outp->index, ret);
|
||||||
|
nvkm_connector_del(&outp->conn);
|
||||||
|
list_del(&outp->head);
|
||||||
|
nvkm_output_del(&outp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&outp->conn->head, &disp->conn);
|
||||||
|
}
|
||||||
|
|
||||||
ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd);
|
ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -33,13 +33,13 @@ static int
|
|||||||
nvkm_connector_hpd(struct nvkm_notify *notify)
|
nvkm_connector_hpd(struct nvkm_notify *notify)
|
||||||
{
|
{
|
||||||
struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
|
struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
|
||||||
struct nvkm_disp *disp = nvkm_disp(conn);
|
struct nvkm_disp *disp = conn->disp;
|
||||||
struct nvkm_gpio *gpio = nvkm_gpio(conn);
|
struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio;
|
||||||
const struct nvkm_gpio_ntfy_rep *line = notify->data;
|
const struct nvkm_gpio_ntfy_rep *line = notify->data;
|
||||||
struct nvif_notify_conn_rep_v0 rep;
|
struct nvif_notify_conn_rep_v0 rep;
|
||||||
int index = conn->index;
|
int index = conn->index;
|
||||||
|
|
||||||
DBG("HPD: %d\n", line->mask);
|
CONN_DBG(conn, "HPD: %d", line->mask);
|
||||||
|
|
||||||
if (!gpio->get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.index))
|
if (!gpio->get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.index))
|
||||||
rep.mask = NVIF_NOTIFY_CONN_V0_UNPLUG;
|
rep.mask = NVIF_NOTIFY_CONN_V0_UNPLUG;
|
||||||
@ -51,78 +51,58 @@ nvkm_connector_hpd(struct nvkm_notify *notify)
|
|||||||
return NVKM_NOTIFY_KEEP;
|
return NVKM_NOTIFY_KEEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
void
|
||||||
_nvkm_connector_fini(struct nvkm_object *object, bool suspend)
|
nvkm_connector_fini(struct nvkm_connector *conn)
|
||||||
{
|
{
|
||||||
struct nvkm_connector *conn = (void *)object;
|
|
||||||
nvkm_notify_put(&conn->hpd);
|
nvkm_notify_put(&conn->hpd);
|
||||||
return nvkm_object_fini(&conn->base, suspend);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
_nvkm_connector_init(struct nvkm_object *object)
|
|
||||||
{
|
|
||||||
struct nvkm_connector *conn = (void *)object;
|
|
||||||
int ret = nvkm_object_init(&conn->base);
|
|
||||||
if (ret == 0)
|
|
||||||
nvkm_notify_get(&conn->hpd);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_nvkm_connector_dtor(struct nvkm_object *object)
|
nvkm_connector_init(struct nvkm_connector *conn)
|
||||||
{
|
{
|
||||||
struct nvkm_connector *conn = (void *)object;
|
nvkm_notify_get(&conn->hpd);
|
||||||
nvkm_notify_fini(&conn->hpd);
|
|
||||||
nvkm_object_destroy(&conn->base);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
void
|
||||||
nvkm_connector_create_(struct nvkm_object *parent,
|
nvkm_connector_del(struct nvkm_connector **pconn)
|
||||||
struct nvkm_object *engine,
|
{
|
||||||
struct nvkm_oclass *oclass,
|
struct nvkm_connector *conn = *pconn;
|
||||||
struct nvbios_connE *info, int index,
|
if (conn) {
|
||||||
int length, void **pobject)
|
nvkm_notify_fini(&conn->hpd);
|
||||||
|
kfree(*pconn);
|
||||||
|
*pconn = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nvkm_connector_ctor(struct nvkm_disp *disp, int index,
|
||||||
|
struct nvbios_connE *info, struct nvkm_connector *conn)
|
||||||
{
|
{
|
||||||
static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 };
|
static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 };
|
||||||
struct nvkm_disp *disp = nvkm_disp(parent);
|
struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio;
|
||||||
struct nvkm_gpio *gpio = nvkm_gpio(parent);
|
|
||||||
struct nvkm_connector *conn;
|
|
||||||
struct nvkm_output *outp;
|
|
||||||
struct dcb_gpio_func func;
|
struct dcb_gpio_func func;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
list_for_each_entry(outp, &disp->outp, head) {
|
conn->disp = disp;
|
||||||
if (outp->conn && outp->conn->index == index) {
|
|
||||||
atomic_inc(&nv_object(outp->conn)->refcount);
|
|
||||||
*pobject = outp->conn;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = nvkm_object_create_(parent, engine, oclass, 0, length, pobject);
|
|
||||||
conn = *pobject;
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
conn->info = *info;
|
|
||||||
conn->index = index;
|
conn->index = index;
|
||||||
|
conn->info = *info;
|
||||||
|
|
||||||
DBG("type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x\n",
|
CONN_DBG(conn, "type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x",
|
||||||
info->type, info->location, info->hpd, info->dp,
|
info->type, info->location, info->hpd, info->dp,
|
||||||
info->di, info->sr, info->lcdid);
|
info->di, info->sr, info->lcdid);
|
||||||
|
|
||||||
if ((info->hpd = ffs(info->hpd))) {
|
if ((info->hpd = ffs(info->hpd))) {
|
||||||
if (--info->hpd >= ARRAY_SIZE(hpd)) {
|
if (--info->hpd >= ARRAY_SIZE(hpd)) {
|
||||||
ERR("hpd %02x unknown\n", info->hpd);
|
CONN_ERR(conn, "hpd %02x unknown", info->hpd);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
info->hpd = hpd[info->hpd];
|
info->hpd = hpd[info->hpd];
|
||||||
|
|
||||||
ret = gpio->find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func);
|
ret = gpio->find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ERR("func %02x lookup failed, %d\n", info->hpd, ret);
|
CONN_ERR(conn, "func %02x lookup failed, %d",
|
||||||
return 0;
|
info->hpd, ret);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd,
|
ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd,
|
||||||
@ -134,41 +114,19 @@ nvkm_connector_create_(struct nvkm_object *parent,
|
|||||||
sizeof(struct nvkm_gpio_ntfy_rep),
|
sizeof(struct nvkm_gpio_ntfy_rep),
|
||||||
&conn->hpd);
|
&conn->hpd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ERR("func %02x failed, %d\n", info->hpd, ret);
|
CONN_ERR(conn, "func %02x failed, %d", info->hpd, ret);
|
||||||
} else {
|
} else {
|
||||||
DBG("func %02x (HPD)\n", info->hpd);
|
CONN_DBG(conn, "func %02x (HPD)", info->hpd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_nvkm_connector_ctor(struct nvkm_object *parent,
|
nvkm_connector_new(struct nvkm_disp *disp, int index,
|
||||||
struct nvkm_object *engine,
|
struct nvbios_connE *info, struct nvkm_connector **pconn)
|
||||||
struct nvkm_oclass *oclass, void *info, u32 index,
|
|
||||||
struct nvkm_object **pobject)
|
|
||||||
{
|
{
|
||||||
struct nvkm_connector *conn;
|
if (!(*pconn = kzalloc(sizeof(**pconn), GFP_KERNEL)))
|
||||||
int ret;
|
return -ENOMEM;
|
||||||
|
nvkm_connector_ctor(disp, index, info, *pconn);
|
||||||
ret = nvkm_connector_create(parent, engine, oclass, info, index, &conn);
|
|
||||||
*pobject = nv_object(conn);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_oclass *
|
|
||||||
nvkm_connector_oclass = &(struct nvkm_connector_impl) {
|
|
||||||
.base = {
|
|
||||||
.handle = 0,
|
|
||||||
.ofuncs = &(struct nvkm_ofuncs) {
|
|
||||||
.ctor = _nvkm_connector_ctor,
|
|
||||||
.dtor = _nvkm_connector_dtor,
|
|
||||||
.init = _nvkm_connector_init,
|
|
||||||
.fini = _nvkm_connector_fini,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}.base;
|
|
||||||
|
@ -1,58 +1,33 @@
|
|||||||
#ifndef __NVKM_DISP_CONN_H__
|
#ifndef __NVKM_DISP_CONN_H__
|
||||||
#define __NVKM_DISP_CONN_H__
|
#define __NVKM_DISP_CONN_H__
|
||||||
#include <core/object.h>
|
#include <engine/disp.h>
|
||||||
#include <core/notify.h>
|
|
||||||
|
|
||||||
|
#include <core/notify.h>
|
||||||
#include <subdev/bios.h>
|
#include <subdev/bios.h>
|
||||||
#include <subdev/bios/conn.h>
|
#include <subdev/bios/conn.h>
|
||||||
|
|
||||||
struct nvkm_connector {
|
struct nvkm_connector {
|
||||||
struct nvkm_object base;
|
struct nvkm_disp *disp;
|
||||||
struct list_head head;
|
|
||||||
|
|
||||||
struct nvbios_connE info;
|
|
||||||
int index;
|
int index;
|
||||||
|
struct nvbios_connE info;
|
||||||
|
|
||||||
struct nvkm_notify hpd;
|
struct nvkm_notify hpd;
|
||||||
|
|
||||||
|
struct list_head head;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define nvkm_connector_create(p,e,c,b,i,d) \
|
int nvkm_connector_new(struct nvkm_disp *, int index, struct nvbios_connE *,
|
||||||
nvkm_connector_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
|
struct nvkm_connector **);
|
||||||
#define nvkm_connector_destroy(d) ({ \
|
void nvkm_connector_del(struct nvkm_connector **);
|
||||||
struct nvkm_connector *disp = (d); \
|
void nvkm_connector_init(struct nvkm_connector *);
|
||||||
_nvkm_connector_dtor(nv_object(disp)); \
|
void nvkm_connector_fini(struct nvkm_connector *);
|
||||||
})
|
|
||||||
#define nvkm_connector_init(d) ({ \
|
|
||||||
struct nvkm_connector *disp = (d); \
|
|
||||||
_nvkm_connector_init(nv_object(disp)); \
|
|
||||||
})
|
|
||||||
#define nvkm_connector_fini(d,s) ({ \
|
|
||||||
struct nvkm_connector *disp = (d); \
|
|
||||||
_nvkm_connector_fini(nv_object(disp), (s)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
int nvkm_connector_create_(struct nvkm_object *, struct nvkm_object *,
|
#define CONN_MSG(c,l,f,a...) do { \
|
||||||
struct nvkm_oclass *, struct nvbios_connE *,
|
struct nvkm_connector *_conn = (c); \
|
||||||
int, int, void **);
|
nvkm_##l(&_conn->disp->engine.subdev, "conn %02x:%02x%02x: "f"\n", \
|
||||||
|
_conn->index, _conn->info.location, _conn->info.type, ##a); \
|
||||||
int _nvkm_connector_ctor(struct nvkm_object *, struct nvkm_object *,
|
|
||||||
struct nvkm_oclass *, void *, u32,
|
|
||||||
struct nvkm_object **);
|
|
||||||
void _nvkm_connector_dtor(struct nvkm_object *);
|
|
||||||
int _nvkm_connector_init(struct nvkm_object *);
|
|
||||||
int _nvkm_connector_fini(struct nvkm_object *, bool);
|
|
||||||
|
|
||||||
struct nvkm_connector_impl {
|
|
||||||
struct nvkm_oclass base;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifndef MSG
|
|
||||||
#define MSG(l,f,a...) do { \
|
|
||||||
struct nvkm_connector *_conn = (void *)conn; \
|
|
||||||
nvkm_##l(&nvkm_disp(_conn)->engine.subdev, "%02x:%02x%02x: "f, _conn->index, \
|
|
||||||
_conn->info.location, _conn->info.type, ##a); \
|
|
||||||
} while(0)
|
} while(0)
|
||||||
#define DBG(f,a...) MSG(debug, f, ##a)
|
#define CONN_ERR(c,f,a...) CONN_MSG((c), error, f, ##a)
|
||||||
#define ERR(f,a...) MSG(error, f, ##a)
|
#define CONN_DBG(c,f,a...) CONN_MSG((c), debug, f, ##a)
|
||||||
#endif
|
#define CONN_TRACE(c,f,a...) CONN_MSG((c), trace, f, ##a)
|
||||||
#endif
|
#endif
|
||||||
|
@ -112,3 +112,15 @@ nv50_dac_sense(NV50_DISP_MTHD_V1)
|
|||||||
args->v0.load = (loadval & 0x38000000) >> 27;
|
args->v0.load = (loadval & 0x38000000) >> 27;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct nvkm_output_func
|
||||||
|
nv50_dac_output_func = {
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
nv50_dac_output_new(struct nvkm_disp *disp, int index,
|
||||||
|
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
return nvkm_output_new_(&nv50_dac_output_func, disp,
|
||||||
|
index, dcbE, poutp);
|
||||||
|
}
|
||||||
|
@ -48,12 +48,12 @@ struct dp_state {
|
|||||||
static int
|
static int
|
||||||
dp_set_link_config(struct dp_state *dp)
|
dp_set_link_config(struct dp_state *dp)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
|
|
||||||
struct nvkm_output_dp *outp = dp->outp;
|
struct nvkm_output_dp *outp = dp->outp;
|
||||||
struct nvkm_disp *disp = nvkm_disp(outp);
|
struct nvkm_disp *disp = outp->base.disp;
|
||||||
struct nvkm_bios *bios = nvkm_bios(disp);
|
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||||
|
struct nvkm_bios *bios = subdev->device->bios;
|
||||||
struct nvbios_init init = {
|
struct nvbios_init init = {
|
||||||
.subdev = nv_subdev(disp),
|
.subdev = subdev,
|
||||||
.bios = bios,
|
.bios = bios,
|
||||||
.offset = 0x0000,
|
.offset = 0x0000,
|
||||||
.outp = &outp->base.info,
|
.outp = &outp->base.info,
|
||||||
@ -64,7 +64,7 @@ dp_set_link_config(struct dp_state *dp)
|
|||||||
u8 sink[2];
|
u8 sink[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
|
OUTP_DBG(&outp->base, "%d lanes at %d KB/s", dp->link_nr, dp->link_bw);
|
||||||
|
|
||||||
/* set desired link configuration on the source */
|
/* set desired link configuration on the source */
|
||||||
if ((lnkcmp = dp->outp->info.lnkcmp)) {
|
if ((lnkcmp = dp->outp->info.lnkcmp)) {
|
||||||
@ -81,16 +81,16 @@ dp_set_link_config(struct dp_state *dp)
|
|||||||
nvbios_exec(&init);
|
nvbios_exec(&init);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
|
ret = outp->func->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
|
||||||
outp->dpcd[DPCD_RC02] &
|
outp->dpcd[DPCD_RC02] &
|
||||||
DPCD_RC02_ENHANCED_FRAME_CAP);
|
DPCD_RC02_ENHANCED_FRAME_CAP);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
ERR("lnk_ctl failed with %d\n", ret);
|
OUTP_ERR(&outp->base, "lnk_ctl failed with %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl->lnk_pwr(outp, dp->link_nr);
|
outp->func->lnk_pwr(outp, dp->link_nr);
|
||||||
|
|
||||||
/* set desired link configuration on the sink */
|
/* set desired link configuration on the sink */
|
||||||
sink[0] = dp->link_bw / 27000;
|
sink[0] = dp->link_bw / 27000;
|
||||||
@ -104,12 +104,11 @@ dp_set_link_config(struct dp_state *dp)
|
|||||||
static void
|
static void
|
||||||
dp_set_training_pattern(struct dp_state *dp, u8 pattern)
|
dp_set_training_pattern(struct dp_state *dp, u8 pattern)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
|
|
||||||
struct nvkm_output_dp *outp = dp->outp;
|
struct nvkm_output_dp *outp = dp->outp;
|
||||||
u8 sink_tp;
|
u8 sink_tp;
|
||||||
|
|
||||||
DBG("training pattern %d\n", pattern);
|
OUTP_DBG(&outp->base, "training pattern %d", pattern);
|
||||||
impl->pattern(outp, pattern);
|
outp->func->pattern(outp, pattern);
|
||||||
|
|
||||||
nvkm_rdaux(outp->aux, DPCD_LC02, &sink_tp, 1);
|
nvkm_rdaux(outp->aux, DPCD_LC02, &sink_tp, 1);
|
||||||
sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
|
sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
|
||||||
@ -120,7 +119,6 @@ dp_set_training_pattern(struct dp_state *dp, u8 pattern)
|
|||||||
static int
|
static int
|
||||||
dp_link_train_commit(struct dp_state *dp, bool pc)
|
dp_link_train_commit(struct dp_state *dp, bool pc)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
|
|
||||||
struct nvkm_output_dp *outp = dp->outp;
|
struct nvkm_output_dp *outp = dp->outp;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
@ -146,8 +144,9 @@ dp_link_train_commit(struct dp_state *dp, bool pc)
|
|||||||
dp->conf[i] = (lpre << 3) | lvsw;
|
dp->conf[i] = (lpre << 3) | lvsw;
|
||||||
dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
|
dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
|
||||||
|
|
||||||
DBG("config lane %d %02x %02x\n", i, dp->conf[i], lpc2);
|
OUTP_DBG(&outp->base, "config lane %d %02x %02x",
|
||||||
impl->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3);
|
i, dp->conf[i], lpc2);
|
||||||
|
outp->func->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nvkm_wraux(outp->aux, DPCD_LC03(0), dp->conf, 4);
|
ret = nvkm_wraux(outp->aux, DPCD_LC03(0), dp->conf, 4);
|
||||||
@ -182,9 +181,10 @@ dp_link_train_update(struct dp_state *dp, bool pc, u32 delay)
|
|||||||
ret = nvkm_rdaux(outp->aux, DPCD_LS0C, &dp->pc2stat, 1);
|
ret = nvkm_rdaux(outp->aux, DPCD_LS0C, &dp->pc2stat, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
dp->pc2stat = 0x00;
|
dp->pc2stat = 0x00;
|
||||||
DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat);
|
OUTP_DBG(&outp->base, "status %6ph pc2 %02x",
|
||||||
|
dp->stat, dp->pc2stat);
|
||||||
} else {
|
} else {
|
||||||
DBG("status %6ph\n", dp->stat);
|
OUTP_DBG(&outp->base, "status %6ph", dp->stat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -260,11 +260,11 @@ static void
|
|||||||
dp_link_train_init(struct dp_state *dp, bool spread)
|
dp_link_train_init(struct dp_state *dp, bool spread)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = dp->outp;
|
struct nvkm_output_dp *outp = dp->outp;
|
||||||
struct nvkm_disp *disp = nvkm_disp(outp);
|
struct nvkm_disp *disp = outp->base.disp;
|
||||||
struct nvkm_bios *bios = nvkm_bios(disp);
|
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||||
struct nvbios_init init = {
|
struct nvbios_init init = {
|
||||||
.subdev = nv_subdev(disp),
|
.subdev = subdev,
|
||||||
.bios = bios,
|
.bios = subdev->device->bios,
|
||||||
.outp = &outp->base.info,
|
.outp = &outp->base.info,
|
||||||
.crtc = -1,
|
.crtc = -1,
|
||||||
.execute = 1,
|
.execute = 1,
|
||||||
@ -286,11 +286,11 @@ static void
|
|||||||
dp_link_train_fini(struct dp_state *dp)
|
dp_link_train_fini(struct dp_state *dp)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = dp->outp;
|
struct nvkm_output_dp *outp = dp->outp;
|
||||||
struct nvkm_disp *disp = nvkm_disp(outp);
|
struct nvkm_disp *disp = outp->base.disp;
|
||||||
struct nvkm_bios *bios = nvkm_bios(disp);
|
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||||
struct nvbios_init init = {
|
struct nvbios_init init = {
|
||||||
.subdev = nv_subdev(disp),
|
.subdev = subdev,
|
||||||
.bios = bios,
|
.bios = subdev->device->bios,
|
||||||
.outp = &outp->base.info,
|
.outp = &outp->base.info,
|
||||||
.crtc = -1,
|
.crtc = -1,
|
||||||
.execute = 1,
|
.execute = 1,
|
||||||
@ -322,7 +322,7 @@ void
|
|||||||
nvkm_dp_train(struct work_struct *w)
|
nvkm_dp_train(struct work_struct *w)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = container_of(w, typeof(*outp), lt.work);
|
struct nvkm_output_dp *outp = container_of(w, typeof(*outp), lt.work);
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nv50_disp *disp = (void *)outp->base.disp;
|
||||||
const struct dp_rates *cfg = nvkm_dp_rates;
|
const struct dp_rates *cfg = nvkm_dp_rates;
|
||||||
struct dp_state _dp = {
|
struct dp_state _dp = {
|
||||||
.outp = outp,
|
.outp = outp,
|
||||||
@ -334,7 +334,7 @@ nvkm_dp_train(struct work_struct *w)
|
|||||||
disp->sor.magic(&outp->base);
|
disp->sor.magic(&outp->base);
|
||||||
|
|
||||||
/* bring capabilities within encoder limits */
|
/* bring capabilities within encoder limits */
|
||||||
if (nv_mclass(disp) < GF110_DISP)
|
if (disp->base.engine.subdev.device->chipset < 0xd0)
|
||||||
outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
|
outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
|
||||||
if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
|
if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
|
||||||
outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
|
outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
|
||||||
@ -386,12 +386,12 @@ nvkm_dp_train(struct work_struct *w)
|
|||||||
/* finish link training and execute post-train script from vbios */
|
/* finish link training and execute post-train script from vbios */
|
||||||
dp_set_training_pattern(dp, 0);
|
dp_set_training_pattern(dp, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
ERR("link training failed\n");
|
OUTP_ERR(&outp->base, "link training failed");
|
||||||
|
|
||||||
dp_link_train_fini(dp);
|
dp_link_train_fini(dp);
|
||||||
|
|
||||||
/* signal completion and enable link interrupt handling */
|
/* signal completion and enable link interrupt handling */
|
||||||
DBG("training complete\n");
|
OUTP_DBG(&outp->base, "training complete");
|
||||||
atomic_set(&outp->lt.done, 1);
|
atomic_set(&outp->lt.done, 1);
|
||||||
wake_up(&outp->lt.wait);
|
wake_up(&outp->lt.wait);
|
||||||
nvkm_notify_get(&outp->irq);
|
nvkm_notify_get(&outp->irq);
|
||||||
|
@ -262,8 +262,12 @@ g84_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.external.tmds = nv50_pior_output_new,
|
||||||
|
.base.outp.external.dp = nv50_pior_dp_new,
|
||||||
.base.vblank = &nv50_disp_vblank_func,
|
.base.vblank = &nv50_disp_vblank_func,
|
||||||
.base.outp = nv50_disp_outp_sclass,
|
|
||||||
.mthd.core = &g84_disp_core_mthd_chan,
|
.mthd.core = &g84_disp_core_mthd_chan,
|
||||||
.mthd.base = &g84_disp_base_mthd_chan,
|
.mthd.base = &g84_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &g84_disp_ovly_mthd_chan,
|
.mthd.ovly = &g84_disp_ovly_mthd_chan,
|
||||||
|
@ -113,13 +113,6 @@ g94_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_oclass *
|
|
||||||
g94_disp_outp_sclass[] = {
|
|
||||||
&nv50_pior_dp_impl.base.base,
|
|
||||||
&g94_sor_dp_impl.base.base,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nvkm_oclass *
|
struct nvkm_oclass *
|
||||||
g94_disp_oclass = &(struct nv50_disp_impl) {
|
g94_disp_oclass = &(struct nv50_disp_impl) {
|
||||||
.base.base.handle = NV_ENGINE(DISP, 0x88),
|
.base.base.handle = NV_ENGINE(DISP, 0x88),
|
||||||
@ -129,8 +122,13 @@ g94_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.dp = g94_sor_dp_new,
|
||||||
|
.base.outp.external.lvds = nv50_pior_output_new,
|
||||||
|
.base.outp.external.dp = nv50_pior_dp_new,
|
||||||
.base.vblank = &nv50_disp_vblank_func,
|
.base.vblank = &nv50_disp_vblank_func,
|
||||||
.base.outp = g94_disp_outp_sclass,
|
|
||||||
.mthd.core = &g94_disp_core_mthd_chan,
|
.mthd.core = &g94_disp_core_mthd_chan,
|
||||||
.mthd.base = &g84_disp_base_mthd_chan,
|
.mthd.base = &g84_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &g84_disp_ovly_mthd_chan,
|
.mthd.ovly = &g84_disp_ovly_mthd_chan,
|
||||||
|
@ -988,7 +988,7 @@ gf110_disp_intr_unk2_0(struct nv50_disp *disp, int head)
|
|||||||
|
|
||||||
/* see note in nv50_disp_intr_unk20_0() */
|
/* see note in nv50_disp_intr_unk20_0() */
|
||||||
if (outp && outp->info.type == DCB_OUTPUT_DP) {
|
if (outp && outp->info.type == DCB_OUTPUT_DP) {
|
||||||
struct nvkm_output_dp *outpdp = (void *)outp;
|
struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
|
||||||
struct nvbios_init init = {
|
struct nvbios_init init = {
|
||||||
.subdev = nv_subdev(disp),
|
.subdev = nv_subdev(disp),
|
||||||
.bios = nvkm_bios(disp),
|
.bios = nvkm_bios(disp),
|
||||||
@ -1101,7 +1101,7 @@ gf110_disp_intr_unk2_2(struct nv50_disp *disp, int head)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nvkm_output_dp_train(outp, pclk, true))
|
if (nvkm_output_dp_train(outp, pclk, true))
|
||||||
ERR("link not trained before attach\n");
|
OUTP_ERR(outp, "link not trained before attach");
|
||||||
} else {
|
} else {
|
||||||
if (disp->sor.magic)
|
if (disp->sor.magic)
|
||||||
disp->sor.magic(outp);
|
disp->sor.magic(outp);
|
||||||
@ -1339,12 +1339,6 @@ gf110_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_oclass *
|
|
||||||
gf110_disp_outp_sclass[] = {
|
|
||||||
&gf110_sor_dp_impl.base.base,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nvkm_oclass *
|
struct nvkm_oclass *
|
||||||
gf110_disp_oclass = &(struct nv50_disp_impl) {
|
gf110_disp_oclass = &(struct nv50_disp_impl) {
|
||||||
.base.base.handle = NV_ENGINE(DISP, 0x90),
|
.base.base.handle = NV_ENGINE(DISP, 0x90),
|
||||||
@ -1354,8 +1348,11 @@ gf110_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.dp = gf110_sor_dp_new,
|
||||||
.base.vblank = &gf110_disp_vblank_func,
|
.base.vblank = &gf110_disp_vblank_func,
|
||||||
.base.outp = gf110_disp_outp_sclass,
|
|
||||||
.mthd.core = &gf110_disp_core_mthd_chan,
|
.mthd.core = &gf110_disp_core_mthd_chan,
|
||||||
.mthd.base = &gf110_disp_base_mthd_chan,
|
.mthd.base = &gf110_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &gf110_disp_ovly_mthd_chan,
|
.mthd.ovly = &gf110_disp_ovly_mthd_chan,
|
||||||
|
@ -259,8 +259,11 @@ gk104_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.dp = gf110_sor_dp_new,
|
||||||
.base.vblank = &gf110_disp_vblank_func,
|
.base.vblank = &gf110_disp_vblank_func,
|
||||||
.base.outp = gf110_disp_outp_sclass,
|
|
||||||
.mthd.core = &gk104_disp_core_mthd_chan,
|
.mthd.core = &gk104_disp_core_mthd_chan,
|
||||||
.mthd.base = &gf110_disp_base_mthd_chan,
|
.mthd.base = &gf110_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
||||||
|
@ -94,8 +94,11 @@ gk110_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.dp = gf110_sor_dp_new,
|
||||||
.base.vblank = &gf110_disp_vblank_func,
|
.base.vblank = &gf110_disp_vblank_func,
|
||||||
.base.outp = gf110_disp_outp_sclass,
|
|
||||||
.mthd.core = &gk104_disp_core_mthd_chan,
|
.mthd.core = &gk104_disp_core_mthd_chan,
|
||||||
.mthd.base = &gf110_disp_base_mthd_chan,
|
.mthd.base = &gf110_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
||||||
|
@ -94,8 +94,11 @@ gm107_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.dp = gf110_sor_dp_new,
|
||||||
.base.vblank = &gf110_disp_vblank_func,
|
.base.vblank = &gf110_disp_vblank_func,
|
||||||
.base.outp = gf110_disp_outp_sclass,
|
|
||||||
.mthd.core = &gk104_disp_core_mthd_chan,
|
.mthd.core = &gk104_disp_core_mthd_chan,
|
||||||
.mthd.base = &gf110_disp_base_mthd_chan,
|
.mthd.base = &gf110_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
||||||
|
@ -87,12 +87,6 @@ gm204_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_oclass *
|
|
||||||
gm204_disp_outp_sclass[] = {
|
|
||||||
&gm204_sor_dp_impl.base.base,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nvkm_oclass *
|
struct nvkm_oclass *
|
||||||
gm204_disp_oclass = &(struct nv50_disp_impl) {
|
gm204_disp_oclass = &(struct nv50_disp_impl) {
|
||||||
.base.base.handle = NV_ENGINE(DISP, 0x07),
|
.base.base.handle = NV_ENGINE(DISP, 0x07),
|
||||||
@ -102,8 +96,11 @@ gm204_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.dp = gm204_sor_dp_new,
|
||||||
.base.vblank = &gf110_disp_vblank_func,
|
.base.vblank = &gf110_disp_vblank_func,
|
||||||
.base.outp = gm204_disp_outp_sclass,
|
|
||||||
.mthd.core = &gk104_disp_core_mthd_chan,
|
.mthd.core = &gk104_disp_core_mthd_chan,
|
||||||
.mthd.base = &gf110_disp_base_mthd_chan,
|
.mthd.base = &gf110_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
.mthd.ovly = &gk104_disp_ovly_mthd_chan,
|
||||||
|
@ -138,8 +138,12 @@ gt200_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.external.tmds = nv50_pior_output_new,
|
||||||
|
.base.outp.external.dp = nv50_pior_dp_new,
|
||||||
.base.vblank = &nv50_disp_vblank_func,
|
.base.vblank = &nv50_disp_vblank_func,
|
||||||
.base.outp = nv50_disp_outp_sclass,
|
|
||||||
.mthd.core = &g84_disp_core_mthd_chan,
|
.mthd.core = &g84_disp_core_mthd_chan,
|
||||||
.mthd.base = &g84_disp_base_mthd_chan,
|
.mthd.base = &g84_disp_base_mthd_chan,
|
||||||
.mthd.ovly = >200_disp_ovly_mthd_chan,
|
.mthd.ovly = >200_disp_ovly_mthd_chan,
|
||||||
|
@ -94,8 +94,13 @@ gt215_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.dp = g94_sor_dp_new,
|
||||||
|
.base.outp.external.lvds = nv50_pior_output_new,
|
||||||
|
.base.outp.external.dp = nv50_pior_dp_new,
|
||||||
.base.vblank = &nv50_disp_vblank_func,
|
.base.vblank = &nv50_disp_vblank_func,
|
||||||
.base.outp = g94_disp_outp_sclass,
|
|
||||||
.mthd.core = &g94_disp_core_mthd_chan,
|
.mthd.core = &g94_disp_core_mthd_chan,
|
||||||
.mthd.base = &g84_disp_base_mthd_chan,
|
.mthd.base = &g84_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &g84_disp_ovly_mthd_chan,
|
.mthd.ovly = &g84_disp_ovly_mthd_chan,
|
||||||
|
@ -1109,7 +1109,7 @@ nv50_disp_main_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NV50_DISP_MTHD_V1_SOR_DP_PWR: {
|
case NV50_DISP_MTHD_V1_SOR_DP_PWR: {
|
||||||
struct nvkm_output_dp *outpdp = (void *)outp;
|
struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
|
||||||
union {
|
union {
|
||||||
struct nv50_disp_sor_dp_pwr_v0 v0;
|
struct nv50_disp_sor_dp_pwr_v0 v0;
|
||||||
} *args = data;
|
} *args = data;
|
||||||
@ -1119,8 +1119,7 @@ nv50_disp_main_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||||||
args->v0.version, args->v0.state);
|
args->v0.version, args->v0.state);
|
||||||
if (args->v0.state == 0) {
|
if (args->v0.state == 0) {
|
||||||
nvkm_notify_put(&outpdp->irq);
|
nvkm_notify_put(&outpdp->irq);
|
||||||
((struct nvkm_output_dp_impl *)nv_oclass(outp))
|
outpdp->func->lnk_pwr(outpdp, 0);
|
||||||
->lnk_pwr(outpdp, 0);
|
|
||||||
atomic_set(&outpdp->lt.done, 0);
|
atomic_set(&outpdp->lt.done, 0);
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
@ -1655,7 +1654,7 @@ nv50_disp_intr_unk20_0(struct nv50_disp *disp, int head)
|
|||||||
* in a blank screen (SOR_PWR off/on can restore it)
|
* in a blank screen (SOR_PWR off/on can restore it)
|
||||||
*/
|
*/
|
||||||
if (outp && outp->info.type == DCB_OUTPUT_DP) {
|
if (outp && outp->info.type == DCB_OUTPUT_DP) {
|
||||||
struct nvkm_output_dp *outpdp = (void *)outp;
|
struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
|
||||||
struct nvbios_init init = {
|
struct nvbios_init init = {
|
||||||
.subdev = nv_subdev(disp),
|
.subdev = nv_subdev(disp),
|
||||||
.bios = nvkm_bios(disp),
|
.bios = nvkm_bios(disp),
|
||||||
@ -1855,7 +1854,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nvkm_output_dp_train(outp, datarate / soff, true))
|
if (nvkm_output_dp_train(outp, datarate / soff, true))
|
||||||
ERR("link not trained before attach\n");
|
OUTP_ERR(outp, "link not trained before attach");
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_clkcmp(disp, head, 0, pclk, &conf);
|
exec_clkcmp(disp, head, 0, pclk, &conf);
|
||||||
@ -2047,12 +2046,6 @@ nv50_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_oclass *
|
|
||||||
nv50_disp_outp_sclass[] = {
|
|
||||||
&nv50_pior_dp_impl.base.base,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nvkm_oclass *
|
struct nvkm_oclass *
|
||||||
nv50_disp_oclass = &(struct nv50_disp_impl) {
|
nv50_disp_oclass = &(struct nv50_disp_impl) {
|
||||||
.base.base.handle = NV_ENGINE(DISP, 0x50),
|
.base.base.handle = NV_ENGINE(DISP, 0x50),
|
||||||
@ -2062,8 +2055,12 @@ nv50_disp_oclass = &(struct nv50_disp_impl) {
|
|||||||
.init = _nvkm_disp_init,
|
.init = _nvkm_disp_init,
|
||||||
.fini = _nvkm_disp_fini,
|
.fini = _nvkm_disp_fini,
|
||||||
},
|
},
|
||||||
|
.base.outp.internal.crt = nv50_dac_output_new,
|
||||||
|
.base.outp.internal.tmds = nv50_sor_output_new,
|
||||||
|
.base.outp.internal.lvds = nv50_sor_output_new,
|
||||||
|
.base.outp.external.tmds = nv50_pior_output_new,
|
||||||
|
.base.outp.external.dp = nv50_pior_dp_new,
|
||||||
.base.vblank = &nv50_disp_vblank_func,
|
.base.vblank = &nv50_disp_vblank_func,
|
||||||
.base.outp = nv50_disp_outp_sclass,
|
|
||||||
.mthd.core = &nv50_disp_core_mthd_chan,
|
.mthd.core = &nv50_disp_core_mthd_chan,
|
||||||
.mthd.base = &nv50_disp_base_mthd_chan,
|
.mthd.base = &nv50_disp_base_mthd_chan,
|
||||||
.mthd.ovly = &nv50_disp_ovly_mthd_chan,
|
.mthd.ovly = &nv50_disp_ovly_mthd_chan,
|
||||||
|
@ -214,7 +214,7 @@ extern struct nvkm_output_dp_impl nv50_pior_dp_impl;
|
|||||||
extern struct nvkm_oclass *nv50_disp_outp_sclass[];
|
extern struct nvkm_oclass *nv50_disp_outp_sclass[];
|
||||||
|
|
||||||
extern struct nvkm_output_dp_impl g94_sor_dp_impl;
|
extern struct nvkm_output_dp_impl g94_sor_dp_impl;
|
||||||
u32 g94_sor_dp_lane_map(struct nv50_disp *, u8 lane);
|
u32 g94_sor_dp_lane_map(struct nvkm_device *, u8 lane);
|
||||||
int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
|
int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
|
||||||
extern struct nvkm_oclass *g94_disp_outp_sclass[];
|
extern struct nvkm_oclass *g94_disp_outp_sclass[];
|
||||||
|
|
||||||
|
@ -22,117 +22,66 @@
|
|||||||
* Authors: Ben Skeggs
|
* Authors: Ben Skeggs
|
||||||
*/
|
*/
|
||||||
#include "outp.h"
|
#include "outp.h"
|
||||||
#include "priv.h"
|
|
||||||
|
|
||||||
#include <subdev/bios.h>
|
#include <subdev/bios.h>
|
||||||
#include <subdev/bios/conn.h>
|
|
||||||
#include <subdev/bios/dcb.h>
|
#include <subdev/bios/dcb.h>
|
||||||
#include <subdev/i2c.h>
|
#include <subdev/i2c.h>
|
||||||
|
|
||||||
int
|
void
|
||||||
_nvkm_output_fini(struct nvkm_object *object, bool suspend)
|
nvkm_output_fini(struct nvkm_output *outp)
|
||||||
{
|
{
|
||||||
struct nvkm_output *outp = (void *)object;
|
if (outp->func->fini)
|
||||||
nv_ofuncs(outp->conn)->fini(nv_object(outp->conn), suspend);
|
outp->func->fini(outp);
|
||||||
return nvkm_object_fini(&outp->base, suspend);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
_nvkm_output_init(struct nvkm_object *object)
|
|
||||||
{
|
|
||||||
struct nvkm_output *outp = (void *)object;
|
|
||||||
int ret = nvkm_object_init(&outp->base);
|
|
||||||
if (ret == 0)
|
|
||||||
nv_ofuncs(outp->conn)->init(nv_object(outp->conn));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_nvkm_output_dtor(struct nvkm_object *object)
|
nvkm_output_init(struct nvkm_output *outp)
|
||||||
{
|
{
|
||||||
struct nvkm_output *outp = (void *)object;
|
if (outp->func->init)
|
||||||
list_del(&outp->head);
|
outp->func->init(outp);
|
||||||
nvkm_object_ref(NULL, (void *)&outp->conn);
|
|
||||||
nvkm_object_destroy(&outp->base);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
void
|
||||||
nvkm_output_create_(struct nvkm_object *parent,
|
nvkm_output_del(struct nvkm_output **poutp)
|
||||||
struct nvkm_object *engine,
|
|
||||||
struct nvkm_oclass *oclass,
|
|
||||||
struct dcb_output *dcbE, int index,
|
|
||||||
int length, void **pobject)
|
|
||||||
{
|
{
|
||||||
struct nvkm_disp *disp = nvkm_disp(parent);
|
struct nvkm_output *outp = *poutp;
|
||||||
struct nvkm_bios *bios = nvkm_bios(parent);
|
if (outp && !WARN_ON(!outp->func)) {
|
||||||
struct nvkm_i2c *i2c = nvkm_i2c(parent);
|
if (outp->func->dtor)
|
||||||
struct nvbios_connE connE;
|
*poutp = outp->func->dtor(outp);
|
||||||
struct nvkm_output *outp;
|
kfree(*poutp);
|
||||||
u8 ver, hdr;
|
*poutp = NULL;
|
||||||
u32 data;
|
}
|
||||||
int ret;
|
}
|
||||||
|
|
||||||
ret = nvkm_object_create_(parent, engine, oclass, 0, length, pobject);
|
void
|
||||||
outp = *pobject;
|
nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp,
|
||||||
if (ret)
|
int index, struct dcb_output *dcbE, struct nvkm_output *outp)
|
||||||
return ret;
|
{
|
||||||
|
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
|
||||||
|
|
||||||
outp->info = *dcbE;
|
outp->func = func;
|
||||||
|
outp->disp = disp;
|
||||||
outp->index = index;
|
outp->index = index;
|
||||||
|
outp->info = *dcbE;
|
||||||
|
outp->i2c = nvkm_i2c_bus_find(i2c, dcbE->i2c_index);
|
||||||
outp->or = ffs(outp->info.or) - 1;
|
outp->or = ffs(outp->info.or) - 1;
|
||||||
|
|
||||||
DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n",
|
OUTP_DBG(outp, "type %02x loc %d or %d link %d con %x "
|
||||||
dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ?
|
"edid %x bus %d head %x",
|
||||||
dcbE->sorconf.link : 0, dcbE->connector, dcbE->i2c_index,
|
outp->info.type, outp->info.location, outp->info.or,
|
||||||
dcbE->bus, dcbE->heads);
|
outp->info.type >= 2 ? outp->info.sorconf.link : 0,
|
||||||
|
outp->info.connector, outp->info.i2c_index,
|
||||||
outp->i2c = nvkm_i2c_bus_find(i2c, outp->info.i2c_index);
|
outp->info.bus, outp->info.heads);
|
||||||
|
|
||||||
data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, &connE);
|
|
||||||
if (!data) {
|
|
||||||
DBG("vbios connector data not found\n");
|
|
||||||
memset(&connE, 0x00, sizeof(connE));
|
|
||||||
connE.type = DCB_CONNECTOR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = nvkm_object_ctor(parent, NULL, nvkm_connector_oclass,
|
|
||||||
&connE, outp->info.connector,
|
|
||||||
(struct nvkm_object **)&outp->conn);
|
|
||||||
if (ret < 0) {
|
|
||||||
ERR("error %d creating connector, disabling\n", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_add_tail(&outp->head, &disp->outp);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_nvkm_output_ctor(struct nvkm_object *parent,
|
nvkm_output_new_(const struct nvkm_output_func *func,
|
||||||
struct nvkm_object *engine,
|
struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||||
struct nvkm_oclass *oclass, void *dcbE, u32 index,
|
struct nvkm_output **poutp)
|
||||||
struct nvkm_object **pobject)
|
|
||||||
{
|
{
|
||||||
struct nvkm_output *outp;
|
if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL)))
|
||||||
int ret;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = nvkm_output_create(parent, engine, oclass, dcbE, index, &outp);
|
|
||||||
*pobject = nv_object(outp);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
|
nvkm_output_ctor(func, disp, index, dcbE, *poutp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_oclass *
|
|
||||||
nvkm_output_oclass = &(struct nvkm_output_impl) {
|
|
||||||
.base = {
|
|
||||||
.handle = 0,
|
|
||||||
.ofuncs = &(struct nvkm_ofuncs) {
|
|
||||||
.ctor = _nvkm_output_ctor,
|
|
||||||
.dtor = _nvkm_output_dtor,
|
|
||||||
.init = _nvkm_output_init,
|
|
||||||
.fini = _nvkm_output_fini,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}.base;
|
|
||||||
|
@ -1,61 +1,51 @@
|
|||||||
#ifndef __NVKM_DISP_OUTP_H__
|
#ifndef __NVKM_DISP_OUTP_H__
|
||||||
#define __NVKM_DISP_OUTP_H__
|
#define __NVKM_DISP_OUTP_H__
|
||||||
#include <core/object.h>
|
#include <engine/disp.h>
|
||||||
|
|
||||||
#include <subdev/bios.h>
|
#include <subdev/bios.h>
|
||||||
#include <subdev/bios/dcb.h>
|
#include <subdev/bios/dcb.h>
|
||||||
|
|
||||||
struct nvkm_output {
|
struct nvkm_output {
|
||||||
struct nvkm_object base;
|
const struct nvkm_output_func *func;
|
||||||
struct list_head head;
|
struct nvkm_disp *disp;
|
||||||
|
|
||||||
struct dcb_output info;
|
|
||||||
int index;
|
int index;
|
||||||
int or;
|
struct dcb_output info;
|
||||||
|
|
||||||
// whatever (if anything) is pointed at by the dcb device entry
|
// whatever (if anything) is pointed at by the dcb device entry
|
||||||
struct nvkm_i2c_bus *i2c;
|
struct nvkm_i2c_bus *i2c;
|
||||||
|
int or;
|
||||||
|
|
||||||
|
struct list_head head;
|
||||||
struct nvkm_connector *conn;
|
struct nvkm_connector *conn;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define nvkm_output_create(p,e,c,b,i,d) \
|
struct nvkm_output_func {
|
||||||
nvkm_output_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
|
void *(*dtor)(struct nvkm_output *);
|
||||||
#define nvkm_output_destroy(d) ({ \
|
void (*init)(struct nvkm_output *);
|
||||||
struct nvkm_output *_outp = (d); \
|
void (*fini)(struct nvkm_output *);
|
||||||
_nvkm_output_dtor(nv_object(_outp)); \
|
|
||||||
})
|
|
||||||
#define nvkm_output_init(d) ({ \
|
|
||||||
struct nvkm_output *_outp = (d); \
|
|
||||||
_nvkm_output_init(nv_object(_outp)); \
|
|
||||||
})
|
|
||||||
#define nvkm_output_fini(d,s) ({ \
|
|
||||||
struct nvkm_output *_outp = (d); \
|
|
||||||
_nvkm_output_fini(nv_object(_outp), (s)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
int nvkm_output_create_(struct nvkm_object *, struct nvkm_object *,
|
|
||||||
struct nvkm_oclass *, struct dcb_output *,
|
|
||||||
int, int, void **);
|
|
||||||
|
|
||||||
int _nvkm_output_ctor(struct nvkm_object *, struct nvkm_object *,
|
|
||||||
struct nvkm_oclass *, void *, u32,
|
|
||||||
struct nvkm_object **);
|
|
||||||
void _nvkm_output_dtor(struct nvkm_object *);
|
|
||||||
int _nvkm_output_init(struct nvkm_object *);
|
|
||||||
int _nvkm_output_fini(struct nvkm_object *, bool);
|
|
||||||
|
|
||||||
struct nvkm_output_impl {
|
|
||||||
struct nvkm_oclass base;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef MSG
|
void nvkm_output_ctor(const struct nvkm_output_func *, struct nvkm_disp *,
|
||||||
#define MSG(l,f,a...) do { \
|
int index, struct dcb_output *, struct nvkm_output *);
|
||||||
struct nvkm_output *_outp = (void *)outp; \
|
int nvkm_output_new_(const struct nvkm_output_func *, struct nvkm_disp *,
|
||||||
nvkm_##l(&nvkm_disp(_outp)->engine.subdev, "%02x:%04x:%04x: "f, _outp->index, \
|
int index, struct dcb_output *, struct nvkm_output **);
|
||||||
_outp->info.hasht, _outp->info.hashm, ##a); \
|
void nvkm_output_del(struct nvkm_output **);
|
||||||
|
void nvkm_output_init(struct nvkm_output *);
|
||||||
|
void nvkm_output_fini(struct nvkm_output *);
|
||||||
|
|
||||||
|
int nv50_dac_output_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
int nv50_sor_output_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
int nv50_pior_output_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
|
||||||
|
#define OUTP_MSG(o,l,f,a...) do { \
|
||||||
|
struct nvkm_output *_outp = (o); \
|
||||||
|
nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \
|
||||||
|
_outp->index, _outp->info.hasht, _outp->info.hashm, ##a); \
|
||||||
} while(0)
|
} while(0)
|
||||||
#define DBG(f,a...) MSG(debug, f, ##a)
|
#define OUTP_ERR(o,f,a...) OUTP_MSG((o), error, f, ##a)
|
||||||
#define ERR(f,a...) MSG(error, f, ##a)
|
#define OUTP_DBG(o,f,a...) OUTP_MSG((o), debug, f, ##a)
|
||||||
#endif
|
#define OUTP_TRACE(o,f,a...) OUTP_MSG((o), trace, f, ##a)
|
||||||
#endif
|
#endif
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
int
|
int
|
||||||
nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
|
nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = (void *)base;
|
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||||
bool retrain = true;
|
bool retrain = true;
|
||||||
u8 link[2], stat[3];
|
u8 link[2], stat[3];
|
||||||
u32 linkrate;
|
u32 linkrate;
|
||||||
@ -42,7 +42,8 @@ nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
|
|||||||
/* check that the link is trained at a high enough rate */
|
/* check that the link is trained at a high enough rate */
|
||||||
ret = nvkm_rdaux(outp->aux, DPCD_LC00_LINK_BW_SET, link, 2);
|
ret = nvkm_rdaux(outp->aux, DPCD_LC00_LINK_BW_SET, link, 2);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
DBG("failed to read link config, assuming no sink\n");
|
OUTP_DBG(&outp->base,
|
||||||
|
"failed to read link config, assuming no sink");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,14 +51,15 @@ nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
|
|||||||
linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */
|
linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */
|
||||||
datarate = (datarate + 9) / 10; /* -> decakilobits */
|
datarate = (datarate + 9) / 10; /* -> decakilobits */
|
||||||
if (linkrate < datarate) {
|
if (linkrate < datarate) {
|
||||||
DBG("link not trained at sufficient rate\n");
|
OUTP_DBG(&outp->base, "link not trained at sufficient rate");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check that link is still trained */
|
/* check that link is still trained */
|
||||||
ret = nvkm_rdaux(outp->aux, DPCD_LS02, stat, 3);
|
ret = nvkm_rdaux(outp->aux, DPCD_LS02, stat, 3);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
DBG("failed to read link status, assuming no sink\n");
|
OUTP_DBG(&outp->base,
|
||||||
|
"failed to read link status, assuming no sink");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +69,14 @@ nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
|
|||||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
|
if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
|
||||||
!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
|
!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
|
||||||
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
|
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
|
||||||
DBG("lane %d not equalised\n", lane);
|
OUTP_DBG(&outp->base,
|
||||||
|
"lane %d not equalised", lane);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retrain = false;
|
retrain = false;
|
||||||
} else {
|
} else {
|
||||||
DBG("no inter-lane alignment\n");
|
OUTP_DBG(&outp->base, "no inter-lane alignment");
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@ -108,7 +111,7 @@ nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable)
|
|||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
if (!outp->present) {
|
if (!outp->present) {
|
||||||
DBG("aux power -> always\n");
|
OUTP_DBG(&outp->base, "aux power -> always");
|
||||||
nvkm_i2c_aux_monitor(aux, true);
|
nvkm_i2c_aux_monitor(aux, true);
|
||||||
outp->present = true;
|
outp->present = true;
|
||||||
}
|
}
|
||||||
@ -121,7 +124,7 @@ nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (outp->present) {
|
if (outp->present) {
|
||||||
DBG("aux power -> demand\n");
|
OUTP_DBG(&outp->base, "aux power -> demand");
|
||||||
nvkm_i2c_aux_monitor(aux, false);
|
nvkm_i2c_aux_monitor(aux, false);
|
||||||
outp->present = false;
|
outp->present = false;
|
||||||
}
|
}
|
||||||
@ -132,116 +135,108 @@ nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable)
|
|||||||
static int
|
static int
|
||||||
nvkm_output_dp_hpd(struct nvkm_notify *notify)
|
nvkm_output_dp_hpd(struct nvkm_notify *notify)
|
||||||
{
|
{
|
||||||
struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
|
|
||||||
struct nvkm_output_dp *outp;
|
|
||||||
struct nvkm_disp *disp = nvkm_disp(conn);
|
|
||||||
const struct nvkm_i2c_ntfy_rep *line = notify->data;
|
const struct nvkm_i2c_ntfy_rep *line = notify->data;
|
||||||
|
struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), hpd);
|
||||||
|
struct nvkm_connector *conn = outp->base.conn;
|
||||||
|
struct nvkm_disp *disp = outp->base.disp;
|
||||||
struct nvif_notify_conn_rep_v0 rep = {};
|
struct nvif_notify_conn_rep_v0 rep = {};
|
||||||
|
|
||||||
list_for_each_entry(outp, &disp->outp, base.head) {
|
OUTP_DBG(&outp->base, "HPD: %d", line->mask);
|
||||||
if (outp->base.conn == conn &&
|
nvkm_output_dp_enable(outp, true);
|
||||||
outp->info.type == DCB_OUTPUT_DP) {
|
|
||||||
DBG("HPD: %d\n", line->mask);
|
|
||||||
nvkm_output_dp_enable(outp, true);
|
|
||||||
|
|
||||||
if (line->mask & NVKM_I2C_UNPLUG)
|
if (line->mask & NVKM_I2C_UNPLUG)
|
||||||
rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
|
rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
|
||||||
if (line->mask & NVKM_I2C_PLUG)
|
if (line->mask & NVKM_I2C_PLUG)
|
||||||
rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
|
rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
|
||||||
|
|
||||||
nvkm_event_send(&disp->hpd, rep.mask, conn->index,
|
nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
|
||||||
&rep, sizeof(rep));
|
return NVKM_NOTIFY_KEEP;
|
||||||
return NVKM_NOTIFY_KEEP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ON(1);
|
|
||||||
return NVKM_NOTIFY_DROP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nvkm_output_dp_irq(struct nvkm_notify *notify)
|
nvkm_output_dp_irq(struct nvkm_notify *notify)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq);
|
|
||||||
struct nvkm_disp *disp = nvkm_disp(outp);
|
|
||||||
const struct nvkm_i2c_ntfy_rep *line = notify->data;
|
const struct nvkm_i2c_ntfy_rep *line = notify->data;
|
||||||
|
struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq);
|
||||||
|
struct nvkm_connector *conn = outp->base.conn;
|
||||||
|
struct nvkm_disp *disp = outp->base.disp;
|
||||||
struct nvif_notify_conn_rep_v0 rep = {
|
struct nvif_notify_conn_rep_v0 rep = {
|
||||||
.mask = NVIF_NOTIFY_CONN_V0_IRQ,
|
.mask = NVIF_NOTIFY_CONN_V0_IRQ,
|
||||||
};
|
};
|
||||||
int index = outp->base.info.connector;
|
|
||||||
|
|
||||||
DBG("IRQ: %d\n", line->mask);
|
OUTP_DBG(&outp->base, "IRQ: %d", line->mask);
|
||||||
nvkm_output_dp_train(&outp->base, 0, true);
|
nvkm_output_dp_train(&outp->base, 0, true);
|
||||||
|
|
||||||
nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep));
|
nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
|
||||||
return NVKM_NOTIFY_DROP;
|
return NVKM_NOTIFY_DROP;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static void
|
||||||
_nvkm_output_dp_fini(struct nvkm_object *object, bool suspend)
|
nvkm_output_dp_fini(struct nvkm_output *base)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = (void *)object;
|
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||||
|
nvkm_notify_put(&outp->hpd);
|
||||||
nvkm_notify_put(&outp->irq);
|
nvkm_notify_put(&outp->irq);
|
||||||
|
flush_work(&outp->lt.work);
|
||||||
nvkm_output_dp_enable(outp, false);
|
nvkm_output_dp_enable(outp, false);
|
||||||
return nvkm_output_fini(&outp->base, suspend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static void
|
||||||
_nvkm_output_dp_init(struct nvkm_object *object)
|
nvkm_output_dp_init(struct nvkm_output *base)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = (void *)object;
|
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||||
|
nvkm_notify_put(&outp->base.conn->hpd);
|
||||||
nvkm_output_dp_enable(outp, true);
|
nvkm_output_dp_enable(outp, true);
|
||||||
return nvkm_output_init(&outp->base);
|
nvkm_notify_get(&outp->hpd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void *
|
||||||
_nvkm_output_dp_dtor(struct nvkm_object *object)
|
nvkm_output_dp_dtor(struct nvkm_output *base)
|
||||||
{
|
{
|
||||||
struct nvkm_output_dp *outp = (void *)object;
|
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||||
|
nvkm_notify_fini(&outp->hpd);
|
||||||
nvkm_notify_fini(&outp->irq);
|
nvkm_notify_fini(&outp->irq);
|
||||||
nvkm_output_destroy(&outp->base);
|
return outp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct nvkm_output_func
|
||||||
|
nvkm_output_dp_func = {
|
||||||
|
.dtor = nvkm_output_dp_dtor,
|
||||||
|
.init = nvkm_output_dp_init,
|
||||||
|
.fini = nvkm_output_dp_fini,
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
nvkm_output_dp_create_(struct nvkm_object *parent,
|
nvkm_output_dp_ctor(const struct nvkm_output_dp_func *func,
|
||||||
struct nvkm_object *engine,
|
struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||||
struct nvkm_oclass *oclass,
|
struct nvkm_i2c_aux *aux, struct nvkm_output_dp *outp)
|
||||||
struct dcb_output *info, int index,
|
|
||||||
int length, void **pobject)
|
|
||||||
{
|
{
|
||||||
struct nvkm_bios *bios = nvkm_bios(parent);
|
struct nvkm_device *device = disp->engine.subdev.device;
|
||||||
struct nvkm_i2c *i2c = nvkm_i2c(parent);
|
struct nvkm_bios *bios = device->bios;
|
||||||
struct nvkm_output_dp *outp;
|
struct nvkm_i2c *i2c = device->i2c;
|
||||||
u8 hdr, cnt, len;
|
u8 hdr, cnt, len;
|
||||||
u32 data;
|
u32 data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = nvkm_output_create_(parent, engine, oclass, info, index,
|
nvkm_output_ctor(&nvkm_output_dp_func, disp, index, dcbE, &outp->base);
|
||||||
length, pobject);
|
outp->func = func;
|
||||||
outp = *pobject;
|
outp->aux = aux;
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
nvkm_notify_fini(&outp->base.conn->hpd);
|
|
||||||
|
|
||||||
/* access to the aux channel is not optional... */
|
|
||||||
//XXX: breaks anx support
|
|
||||||
outp->aux = nvkm_i2c_aux_find(i2c, outp->base.info.i2c_index);
|
|
||||||
if (!outp->aux) {
|
if (!outp->aux) {
|
||||||
ERR("aux channel not found\n");
|
OUTP_ERR(&outp->base, "no aux");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nor is the bios data for this output... */
|
/* bios data is not optional */
|
||||||
data = nvbios_dpout_match(bios, outp->base.info.hasht,
|
data = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||||
outp->base.info.hashm, &outp->version,
|
outp->base.info.hashm, &outp->version,
|
||||||
&hdr, &cnt, &len, &outp->info);
|
&hdr, &cnt, &len, &outp->info);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
ERR("no bios dp data\n");
|
OUTP_ERR(&outp->base, "no bios dp data");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len);
|
OUTP_DBG(&outp->base, "bios dp %02x %02x %02x %02x",
|
||||||
|
outp->version, hdr, cnt, len);
|
||||||
|
|
||||||
/* link training */
|
/* link training */
|
||||||
INIT_WORK(&outp->lt.work, nvkm_dp_train);
|
INIT_WORK(&outp->lt.work, nvkm_dp_train);
|
||||||
@ -258,7 +253,7 @@ nvkm_output_dp_create_(struct nvkm_object *parent,
|
|||||||
sizeof(struct nvkm_i2c_ntfy_rep),
|
sizeof(struct nvkm_i2c_ntfy_rep),
|
||||||
&outp->irq);
|
&outp->irq);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ERR("error monitoring aux irq event: %d\n", ret);
|
OUTP_ERR(&outp->base, "error monitoring aux irq: %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,9 +265,9 @@ nvkm_output_dp_create_(struct nvkm_object *parent,
|
|||||||
},
|
},
|
||||||
sizeof(struct nvkm_i2c_ntfy_req),
|
sizeof(struct nvkm_i2c_ntfy_req),
|
||||||
sizeof(struct nvkm_i2c_ntfy_rep),
|
sizeof(struct nvkm_i2c_ntfy_rep),
|
||||||
&outp->base.conn->hpd);
|
&outp->hpd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ERR("error monitoring aux hpd events: %d\n", ret);
|
OUTP_ERR(&outp->base, "error monitoring aux hpd: %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,18 +275,17 @@ nvkm_output_dp_create_(struct nvkm_object *parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_nvkm_output_dp_ctor(struct nvkm_object *parent,
|
nvkm_output_dp_new_(const struct nvkm_output_dp_func *func,
|
||||||
struct nvkm_object *engine,
|
struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||||
struct nvkm_oclass *oclass, void *info, u32 index,
|
struct nvkm_output **poutp)
|
||||||
struct nvkm_object **pobject)
|
|
||||||
{
|
{
|
||||||
|
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
|
||||||
|
struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbE->i2c_index);
|
||||||
struct nvkm_output_dp *outp;
|
struct nvkm_output_dp *outp;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp);
|
if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL)))
|
||||||
*pobject = nv_object(outp);
|
return -ENOMEM;
|
||||||
if (ret)
|
*poutp = &outp->base;
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
return nvkm_output_dp_ctor(func, disp, index, dcbE, aux, outp);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
#ifndef __NVKM_DISP_OUTP_DP_H__
|
#ifndef __NVKM_DISP_OUTP_DP_H__
|
||||||
#define __NVKM_DISP_OUTP_DP_H__
|
#define __NVKM_DISP_OUTP_DP_H__
|
||||||
|
#define nvkm_output_dp(p) container_of((p), struct nvkm_output_dp, base)
|
||||||
|
#ifndef MSG
|
||||||
|
#define MSG(l,f,a...) \
|
||||||
|
nvkm_##l(&outp->base.disp->engine.subdev, "%02x:%04x:%04x: "f, \
|
||||||
|
outp->base.index, outp->base.info.hasht, \
|
||||||
|
outp->base.info.hashm, ##a)
|
||||||
|
#define DBG(f,a...) MSG(debug, f, ##a)
|
||||||
|
#define ERR(f,a...) MSG(error, f, ##a)
|
||||||
|
#endif
|
||||||
#include "outp.h"
|
#include "outp.h"
|
||||||
|
|
||||||
#include <core/notify.h>
|
#include <core/notify.h>
|
||||||
@ -7,6 +16,7 @@
|
|||||||
#include <subdev/bios/dp.h>
|
#include <subdev/bios/dp.h>
|
||||||
|
|
||||||
struct nvkm_output_dp {
|
struct nvkm_output_dp {
|
||||||
|
const struct nvkm_output_dp_func *func;
|
||||||
struct nvkm_output base;
|
struct nvkm_output base;
|
||||||
|
|
||||||
struct nvbios_dpout info;
|
struct nvbios_dpout info;
|
||||||
@ -15,6 +25,7 @@ struct nvkm_output_dp {
|
|||||||
struct nvkm_i2c_aux *aux;
|
struct nvkm_i2c_aux *aux;
|
||||||
|
|
||||||
struct nvkm_notify irq;
|
struct nvkm_notify irq;
|
||||||
|
struct nvkm_notify hpd;
|
||||||
bool present;
|
bool present;
|
||||||
u8 dpcd[16];
|
u8 dpcd[16];
|
||||||
|
|
||||||
@ -25,34 +36,7 @@ struct nvkm_output_dp {
|
|||||||
} lt;
|
} lt;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define nvkm_output_dp_create(p,e,c,b,i,d) \
|
struct nvkm_output_dp_func {
|
||||||
nvkm_output_dp_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
|
|
||||||
#define nvkm_output_dp_destroy(d) ({ \
|
|
||||||
struct nvkm_output_dp *_outp = (d); \
|
|
||||||
_nvkm_output_dp_dtor(nv_object(_outp)); \
|
|
||||||
})
|
|
||||||
#define nvkm_output_dp_init(d) ({ \
|
|
||||||
struct nvkm_output_dp *_outp = (d); \
|
|
||||||
_nvkm_output_dp_init(nv_object(_outp)); \
|
|
||||||
})
|
|
||||||
#define nvkm_output_dp_fini(d,s) ({ \
|
|
||||||
struct nvkm_output_dp *_outp = (d); \
|
|
||||||
_nvkm_output_dp_fini(nv_object(_outp), (s)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
int nvkm_output_dp_create_(struct nvkm_object *, struct nvkm_object *,
|
|
||||||
struct nvkm_oclass *, struct dcb_output *,
|
|
||||||
int, int, void **);
|
|
||||||
|
|
||||||
int _nvkm_output_dp_ctor(struct nvkm_object *, struct nvkm_object *,
|
|
||||||
struct nvkm_oclass *, void *, u32,
|
|
||||||
struct nvkm_object **);
|
|
||||||
void _nvkm_output_dp_dtor(struct nvkm_object *);
|
|
||||||
int _nvkm_output_dp_init(struct nvkm_object *);
|
|
||||||
int _nvkm_output_dp_fini(struct nvkm_object *, bool);
|
|
||||||
|
|
||||||
struct nvkm_output_dp_impl {
|
|
||||||
struct nvkm_output_impl base;
|
|
||||||
int (*pattern)(struct nvkm_output_dp *, int);
|
int (*pattern)(struct nvkm_output_dp *, int);
|
||||||
int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
|
int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
|
||||||
int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef);
|
int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef);
|
||||||
@ -60,4 +44,23 @@ struct nvkm_output_dp_impl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait);
|
int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait);
|
||||||
|
|
||||||
|
int nvkm_output_dp_ctor(const struct nvkm_output_dp_func *, struct nvkm_disp *,
|
||||||
|
int index, struct dcb_output *, struct nvkm_i2c_aux *,
|
||||||
|
struct nvkm_output_dp *);
|
||||||
|
int nvkm_output_dp_new_(const struct nvkm_output_dp_func *, struct nvkm_disp *,
|
||||||
|
int index, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
|
||||||
|
int nv50_pior_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
|
||||||
|
int g94_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
|
||||||
|
int gf110_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
|
||||||
|
int gm204_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
*
|
*
|
||||||
* Authors: Ben Skeggs
|
* Authors: Ben Skeggs
|
||||||
*/
|
*/
|
||||||
#include "nv50.h"
|
|
||||||
#include "outpdp.h"
|
#include "outpdp.h"
|
||||||
|
#include "nv50.h"
|
||||||
|
|
||||||
#include <core/client.h>
|
#include <core/client.h>
|
||||||
#include <subdev/i2c.h>
|
#include <subdev/i2c.h>
|
||||||
@ -31,104 +31,6 @@
|
|||||||
#include <nvif/class.h>
|
#include <nvif/class.h>
|
||||||
#include <nvif/unpack.h>
|
#include <nvif/unpack.h>
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* TMDS
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
static int
|
|
||||||
nv50_pior_tmds_ctor(struct nvkm_object *parent,
|
|
||||||
struct nvkm_object *engine,
|
|
||||||
struct nvkm_oclass *oclass, void *info, u32 index,
|
|
||||||
struct nvkm_object **pobject)
|
|
||||||
{
|
|
||||||
struct nvkm_output *outp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = nvkm_output_create(parent, engine, oclass, info, index, &outp);
|
|
||||||
*pobject = nv_object(outp);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct nvkm_output_impl
|
|
||||||
nv50_pior_tmds_impl = {
|
|
||||||
.base.handle = DCB_OUTPUT_TMDS | 0x0100,
|
|
||||||
.base.ofuncs = &(struct nvkm_ofuncs) {
|
|
||||||
.ctor = nv50_pior_tmds_ctor,
|
|
||||||
.dtor = _nvkm_output_dtor,
|
|
||||||
.init = _nvkm_output_init,
|
|
||||||
.fini = _nvkm_output_fini,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* DisplayPort
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
static int
|
|
||||||
nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
nv50_pior_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
nv50_pior_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
|
||||||
{
|
|
||||||
return nvkm_i2c_aux_lnk_ctl(outp->aux, nr, bw, ef);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
nv50_pior_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
nv50_pior_dp_ctor(struct nvkm_object *parent,
|
|
||||||
struct nvkm_object *engine,
|
|
||||||
struct nvkm_oclass *oclass, void *info, u32 index,
|
|
||||||
struct nvkm_object **pobject)
|
|
||||||
{
|
|
||||||
struct nvkm_i2c *i2c = nvkm_i2c(parent);
|
|
||||||
struct nvkm_output_dp *outp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp);
|
|
||||||
*pobject = nv_object(outp);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
outp->aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(outp->base.info.extdev));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct nvkm_output_dp_impl
|
|
||||||
nv50_pior_dp_impl = {
|
|
||||||
.base.base.handle = DCB_OUTPUT_DP | 0x0010,
|
|
||||||
.base.base.ofuncs = &(struct nvkm_ofuncs) {
|
|
||||||
.ctor = nv50_pior_dp_ctor,
|
|
||||||
.dtor = _nvkm_output_dp_dtor,
|
|
||||||
.init = _nvkm_output_dp_init,
|
|
||||||
.fini = _nvkm_output_dp_fini,
|
|
||||||
},
|
|
||||||
.pattern = nv50_pior_dp_pattern,
|
|
||||||
.lnk_pwr = nv50_pior_dp_lnk_pwr,
|
|
||||||
.lnk_ctl = nv50_pior_dp_lnk_ctl,
|
|
||||||
.drv_ctl = nv50_pior_dp_drv_ctl,
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* General PIOR handling
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
int
|
int
|
||||||
nv50_pior_power(NV50_DISP_MTHD_V1)
|
nv50_pior_power(NV50_DISP_MTHD_V1)
|
||||||
{
|
{
|
||||||
@ -163,3 +65,67 @@ nv50_pior_power(NV50_DISP_MTHD_V1)
|
|||||||
disp->pior.type[outp->or] = type;
|
disp->pior.type[outp->or] = type;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* TMDS
|
||||||
|
*****************************************************************************/
|
||||||
|
static const struct nvkm_output_func
|
||||||
|
nv50_pior_output_func = {
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
nv50_pior_output_new(struct nvkm_disp *disp, int index,
|
||||||
|
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
return nvkm_output_new_(&nv50_pior_output_func, disp,
|
||||||
|
index, dcbE, poutp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* DisplayPort
|
||||||
|
*****************************************************************************/
|
||||||
|
static int
|
||||||
|
nv50_pior_output_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nv50_pior_output_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nv50_pior_output_dp_lnk_ctl(struct nvkm_output_dp *outp,
|
||||||
|
int nr, int bw, bool ef)
|
||||||
|
{
|
||||||
|
int ret = nvkm_i2c_aux_lnk_ctl(outp->aux, nr, bw, ef);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct nvkm_output_dp_func
|
||||||
|
nv50_pior_output_dp_func = {
|
||||||
|
.pattern = nv50_pior_output_dp_pattern,
|
||||||
|
.lnk_pwr = nv50_pior_output_dp_lnk_pwr,
|
||||||
|
.lnk_ctl = nv50_pior_output_dp_lnk_ctl,
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
nv50_pior_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||||
|
struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
|
||||||
|
struct nvkm_i2c_aux *aux =
|
||||||
|
nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev));
|
||||||
|
struct nvkm_output_dp *outp;
|
||||||
|
|
||||||
|
if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL)))
|
||||||
|
return -ENOMEM;
|
||||||
|
*poutp = &outp->base;
|
||||||
|
|
||||||
|
return nvkm_output_dp_ctor(&nv50_pior_output_dp_func, disp,
|
||||||
|
index, dcbE, aux, outp);
|
||||||
|
}
|
||||||
|
@ -1,11 +1,28 @@
|
|||||||
#ifndef __NVKM_DISP_PRIV_H__
|
#ifndef __NVKM_DISP_PRIV_H__
|
||||||
#define __NVKM_DISP_PRIV_H__
|
#define __NVKM_DISP_PRIV_H__
|
||||||
#include <engine/disp.h>
|
#include <engine/disp.h>
|
||||||
|
#include "outp.h"
|
||||||
|
#include "outpdp.h"
|
||||||
|
|
||||||
|
struct nvkm_disp_func_outp {
|
||||||
|
int (* crt)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
int (* tv)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
int (* dp)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||||
|
struct nvkm_output **);
|
||||||
|
};
|
||||||
|
|
||||||
struct nvkm_disp_impl {
|
struct nvkm_disp_impl {
|
||||||
struct nvkm_oclass base;
|
struct nvkm_oclass base;
|
||||||
struct nvkm_oclass **outp;
|
struct {
|
||||||
struct nvkm_oclass **conn;
|
const struct nvkm_disp_func_outp internal;
|
||||||
|
const struct nvkm_disp_func_outp external;
|
||||||
|
} outp;
|
||||||
const struct nvkm_event_func *vblank;
|
const struct nvkm_event_func *vblank;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,15 +38,33 @@ g94_sor_loff(struct nvkm_output_dp *outp)
|
|||||||
return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
|
return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* TMDS/LVDS
|
||||||
|
******************************************************************************/
|
||||||
|
static const struct nvkm_output_func
|
||||||
|
g94_sor_output_func = {
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
g94_sor_output_new(struct nvkm_disp *disp, int index,
|
||||||
|
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
return nvkm_output_new_(&g94_sor_output_func, disp,
|
||||||
|
index, dcbE, poutp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* DisplayPort
|
||||||
|
******************************************************************************/
|
||||||
u32
|
u32
|
||||||
g94_sor_dp_lane_map(struct nv50_disp *disp, u8 lane)
|
g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
|
||||||
{
|
{
|
||||||
static const u8 gm100[] = { 0, 8, 16, 24 };
|
static const u8 gm100[] = { 0, 8, 16, 24 };
|
||||||
static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
|
static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
|
||||||
static const u8 g94[] = { 16, 8, 0, 24 };
|
static const u8 g94[] = { 16, 8, 0, 24 };
|
||||||
if (nv_device(disp)->chipset >= 0x110)
|
if (device->chipset >= 0x110)
|
||||||
return gm100[lane];
|
return gm100[lane];
|
||||||
if (nv_device(disp)->chipset == 0xaf)
|
if (device->chipset == 0xaf)
|
||||||
return mcp89[lane];
|
return mcp89[lane];
|
||||||
return g94[lane];
|
return g94[lane];
|
||||||
}
|
}
|
||||||
@ -54,8 +72,7 @@ g94_sor_dp_lane_map(struct nv50_disp *disp, u8 lane)
|
|||||||
static int
|
static int
|
||||||
g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 loff = g94_sor_loff(outp);
|
const u32 loff = g94_sor_loff(outp);
|
||||||
nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24);
|
nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24);
|
||||||
return 0;
|
return 0;
|
||||||
@ -64,14 +81,13 @@ g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
|||||||
int
|
int
|
||||||
g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 soff = g94_sor_soff(outp);
|
const u32 soff = g94_sor_soff(outp);
|
||||||
const u32 loff = g94_sor_loff(outp);
|
const u32 loff = g94_sor_loff(outp);
|
||||||
u32 mask = 0, i;
|
u32 mask = 0, i;
|
||||||
|
|
||||||
for (i = 0; i < nr; i++)
|
for (i = 0; i < nr; i++)
|
||||||
mask |= 1 << (g94_sor_dp_lane_map(disp, i) >> 3);
|
mask |= 1 << (g94_sor_dp_lane_map(device, i) >> 3);
|
||||||
|
|
||||||
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
|
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
|
||||||
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
|
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
|
||||||
@ -85,8 +101,7 @@ g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
|||||||
static int
|
static int
|
||||||
g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 soff = g94_sor_soff(outp);
|
const u32 soff = g94_sor_soff(outp);
|
||||||
const u32 loff = g94_sor_loff(outp);
|
const u32 loff = g94_sor_loff(outp);
|
||||||
u32 dpctrl = 0x00000000;
|
u32 dpctrl = 0x00000000;
|
||||||
@ -106,10 +121,9 @@ g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
|||||||
static int
|
static int
|
||||||
g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
struct nvkm_bios *bios = device->bios;
|
struct nvkm_bios *bios = device->bios;
|
||||||
const u32 shift = g94_sor_dp_lane_map(disp, ln);
|
const u32 shift = g94_sor_dp_lane_map(device, ln);
|
||||||
const u32 loff = g94_sor_loff(outp);
|
const u32 loff = g94_sor_loff(outp);
|
||||||
u32 addr, data[3];
|
u32 addr, data[3];
|
||||||
u8 ver, hdr, cnt, len;
|
u8 ver, hdr, cnt, len;
|
||||||
@ -118,12 +132,12 @@ g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
|||||||
|
|
||||||
addr = nvbios_dpout_match(bios, outp->base.info.hasht,
|
addr = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||||
outp->base.info.hashm,
|
outp->base.info.hashm,
|
||||||
&ver, &hdr, &cnt, &len, &info);
|
&ver, &hdr, &cnt, &len, &info);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
|
addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
|
||||||
&ver, &hdr, &cnt, &len, &ocfg);
|
&ver, &hdr, &cnt, &len, &ocfg);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -138,17 +152,17 @@ g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_output_dp_impl
|
static const struct nvkm_output_dp_func
|
||||||
g94_sor_dp_impl = {
|
g94_sor_dp_func = {
|
||||||
.base.base.handle = DCB_OUTPUT_DP,
|
|
||||||
.base.base.ofuncs = &(struct nvkm_ofuncs) {
|
|
||||||
.ctor = _nvkm_output_dp_ctor,
|
|
||||||
.dtor = _nvkm_output_dp_dtor,
|
|
||||||
.init = _nvkm_output_dp_init,
|
|
||||||
.fini = _nvkm_output_dp_fini,
|
|
||||||
},
|
|
||||||
.pattern = g94_sor_dp_pattern,
|
.pattern = g94_sor_dp_pattern,
|
||||||
.lnk_pwr = g94_sor_dp_lnk_pwr,
|
.lnk_pwr = g94_sor_dp_lnk_pwr,
|
||||||
.lnk_ctl = g94_sor_dp_lnk_ctl,
|
.lnk_ctl = g94_sor_dp_lnk_ctl,
|
||||||
.drv_ctl = g94_sor_dp_drv_ctl,
|
.drv_ctl = g94_sor_dp_drv_ctl,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||||
|
struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp);
|
||||||
|
}
|
||||||
|
@ -39,8 +39,7 @@ gf110_sor_loff(struct nvkm_output_dp *outp)
|
|||||||
static int
|
static int
|
||||||
gf110_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
gf110_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 loff = gf110_sor_loff(outp);
|
const u32 loff = gf110_sor_loff(outp);
|
||||||
nvkm_mask(device, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
|
nvkm_mask(device, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
|
||||||
return 0;
|
return 0;
|
||||||
@ -49,8 +48,7 @@ gf110_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
|||||||
int
|
int
|
||||||
gf110_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
gf110_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 soff = gf110_sor_soff(outp);
|
const u32 soff = gf110_sor_soff(outp);
|
||||||
const u32 loff = gf110_sor_loff(outp);
|
const u32 loff = gf110_sor_loff(outp);
|
||||||
u32 dpctrl = 0x00000000;
|
u32 dpctrl = 0x00000000;
|
||||||
@ -70,10 +68,9 @@ static int
|
|||||||
gf110_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
gf110_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
||||||
int ln, int vs, int pe, int pc)
|
int ln, int vs, int pe, int pc)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
struct nvkm_bios *bios = device->bios;
|
struct nvkm_bios *bios = device->bios;
|
||||||
const u32 shift = g94_sor_dp_lane_map(disp, ln);
|
const u32 shift = g94_sor_dp_lane_map(device, ln);
|
||||||
const u32 loff = gf110_sor_loff(outp);
|
const u32 loff = gf110_sor_loff(outp);
|
||||||
u32 addr, data[4];
|
u32 addr, data[4];
|
||||||
u8 ver, hdr, cnt, len;
|
u8 ver, hdr, cnt, len;
|
||||||
@ -104,17 +101,17 @@ gf110_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_output_dp_impl
|
static const struct nvkm_output_dp_func
|
||||||
gf110_sor_dp_impl = {
|
gf110_sor_dp_func = {
|
||||||
.base.base.handle = DCB_OUTPUT_DP,
|
|
||||||
.base.base.ofuncs = &(struct nvkm_ofuncs) {
|
|
||||||
.ctor = _nvkm_output_dp_ctor,
|
|
||||||
.dtor = _nvkm_output_dp_dtor,
|
|
||||||
.init = _nvkm_output_dp_init,
|
|
||||||
.fini = _nvkm_output_dp_fini,
|
|
||||||
},
|
|
||||||
.pattern = gf110_sor_dp_pattern,
|
.pattern = gf110_sor_dp_pattern,
|
||||||
.lnk_pwr = g94_sor_dp_lnk_pwr,
|
.lnk_pwr = g94_sor_dp_lnk_pwr,
|
||||||
.lnk_ctl = gf110_sor_dp_lnk_ctl,
|
.lnk_ctl = gf110_sor_dp_lnk_ctl,
|
||||||
.drv_ctl = gf110_sor_dp_drv_ctl,
|
.drv_ctl = gf110_sor_dp_drv_ctl,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
gf110_sor_dp_new(struct nvkm_disp *disp, int index,
|
||||||
|
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
return nvkm_output_dp_new_(&gf110_sor_dp_func, disp, index, dcbE, poutp);
|
||||||
|
}
|
||||||
|
@ -41,8 +41,7 @@ gm204_sor_loff(struct nvkm_output_dp *outp)
|
|||||||
void
|
void
|
||||||
gm204_sor_magic(struct nvkm_output *outp)
|
gm204_sor_magic(struct nvkm_output *outp)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 soff = outp->or * 0x100;
|
const u32 soff = outp->or * 0x100;
|
||||||
const u32 data = outp->or + 1;
|
const u32 data = outp->or + 1;
|
||||||
if (outp->info.sorconf.link & 1)
|
if (outp->info.sorconf.link & 1)
|
||||||
@ -52,7 +51,7 @@ gm204_sor_magic(struct nvkm_output *outp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline u32
|
static inline u32
|
||||||
gm204_sor_dp_lane_map(struct nv50_disp *disp, u8 lane)
|
gm204_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
|
||||||
{
|
{
|
||||||
return lane * 0x08;
|
return lane * 0x08;
|
||||||
}
|
}
|
||||||
@ -60,8 +59,7 @@ gm204_sor_dp_lane_map(struct nv50_disp *disp, u8 lane)
|
|||||||
static int
|
static int
|
||||||
gm204_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
gm204_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 soff = gm204_sor_soff(outp);
|
const u32 soff = gm204_sor_soff(outp);
|
||||||
const u32 data = 0x01010101 * pattern;
|
const u32 data = 0x01010101 * pattern;
|
||||||
if (outp->base.info.sorconf.link & 1)
|
if (outp->base.info.sorconf.link & 1)
|
||||||
@ -74,14 +72,13 @@ gm204_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
|||||||
static int
|
static int
|
||||||
gm204_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
gm204_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
const u32 soff = gm204_sor_soff(outp);
|
const u32 soff = gm204_sor_soff(outp);
|
||||||
const u32 loff = gm204_sor_loff(outp);
|
const u32 loff = gm204_sor_loff(outp);
|
||||||
u32 mask = 0, i;
|
u32 mask = 0, i;
|
||||||
|
|
||||||
for (i = 0; i < nr; i++)
|
for (i = 0; i < nr; i++)
|
||||||
mask |= 1 << (gm204_sor_dp_lane_map(disp, i) >> 3);
|
mask |= 1 << (gm204_sor_dp_lane_map(device, i) >> 3);
|
||||||
|
|
||||||
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
|
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
|
||||||
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
|
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
|
||||||
@ -96,10 +93,9 @@ static int
|
|||||||
gm204_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
gm204_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
||||||
int ln, int vs, int pe, int pc)
|
int ln, int vs, int pe, int pc)
|
||||||
{
|
{
|
||||||
struct nv50_disp *disp = (void *)nvkm_disp(outp);
|
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
|
||||||
struct nvkm_bios *bios = device->bios;
|
struct nvkm_bios *bios = device->bios;
|
||||||
const u32 shift = gm204_sor_dp_lane_map(disp, ln);
|
const u32 shift = gm204_sor_dp_lane_map(device, ln);
|
||||||
const u32 loff = gm204_sor_loff(outp);
|
const u32 loff = gm204_sor_loff(outp);
|
||||||
u32 addr, data[4];
|
u32 addr, data[4];
|
||||||
u8 ver, hdr, cnt, len;
|
u8 ver, hdr, cnt, len;
|
||||||
@ -131,17 +127,17 @@ gm204_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nvkm_output_dp_impl
|
static const struct nvkm_output_dp_func
|
||||||
gm204_sor_dp_impl = {
|
gm204_sor_dp_func = {
|
||||||
.base.base.handle = DCB_OUTPUT_DP,
|
|
||||||
.base.base.ofuncs = &(struct nvkm_ofuncs) {
|
|
||||||
.ctor = _nvkm_output_dp_ctor,
|
|
||||||
.dtor = _nvkm_output_dp_dtor,
|
|
||||||
.init = _nvkm_output_dp_init,
|
|
||||||
.fini = _nvkm_output_dp_fini,
|
|
||||||
},
|
|
||||||
.pattern = gm204_sor_dp_pattern,
|
.pattern = gm204_sor_dp_pattern,
|
||||||
.lnk_pwr = gm204_sor_dp_lnk_pwr,
|
.lnk_pwr = gm204_sor_dp_lnk_pwr,
|
||||||
.lnk_ctl = gf110_sor_dp_lnk_ctl,
|
.lnk_ctl = gf110_sor_dp_lnk_ctl,
|
||||||
.drv_ctl = gm204_sor_dp_drv_ctl,
|
.drv_ctl = gm204_sor_dp_drv_ctl,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
gm204_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||||
|
struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
return nvkm_output_dp_new_(&gm204_sor_dp_func, disp, index, dcbE, poutp);
|
||||||
|
}
|
||||||
|
@ -65,3 +65,15 @@ nv50_sor_power(NV50_DISP_MTHD_V1)
|
|||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct nvkm_output_func
|
||||||
|
nv50_sor_output_func = {
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
nv50_sor_output_new(struct nvkm_disp *disp, int index,
|
||||||
|
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||||
|
{
|
||||||
|
return nvkm_output_new_(&nv50_sor_output_func, disp,
|
||||||
|
index, dcbE, poutp);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user