Commit af2922fa authored by Dave Airlie's avatar Dave Airlie

Merge branch 'linux-5.12' of git://github.com/skeggsb/linux into drm-next

Nothing too major here, I actually thought I'd sent most of these
right before the new year, but that apparently got lost in the bustle:
- Turing MMU fault recovery fixes
- Fix mDP connectors being reported as eDP to userspace
- Fixes for audio locking, and other bit-rot from DRM changes since
atomic support was written
- Misc other minor fixes.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
From: Ben Skeggs <skeggsb@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CACAvsv7yLfhuVbYa-4g0vxVt93OaC7Sodiz2R-TDHu-MoofEdw@mail.gmail.com
parents 32c3d9b0 d1f5a3fc
...@@ -36,7 +36,7 @@ core507d_update(struct nv50_core *core, u32 *interlock, bool ntfy) ...@@ -36,7 +36,7 @@ core507d_update(struct nv50_core *core, u32 *interlock, bool ntfy)
struct nvif_push *push = core->chan.push; struct nvif_push *push = core->chan.push;
int ret; int ret;
if ((ret = PUSH_WAIT(push, 5))) if ((ret = PUSH_WAIT(push, (ntfy ? 2 : 0) + 3)))
return ret; return ret;
if (ntfy) { if (ntfy) {
......
...@@ -54,7 +54,7 @@ corec37d_update(struct nv50_core *core, u32 *interlock, bool ntfy) ...@@ -54,7 +54,7 @@ corec37d_update(struct nv50_core *core, u32 *interlock, bool ntfy)
struct nvif_push *push = core->chan.push; struct nvif_push *push = core->chan.push;
int ret; int ret;
if ((ret = PUSH_WAIT(push, 9))) if ((ret = PUSH_WAIT(push, (ntfy ? 2 * 2 : 0) + 5)))
return ret; return ret;
if (ntfy) { if (ntfy) {
......
...@@ -220,6 +220,10 @@ nv50_dmac_wait(struct nvif_push *push, u32 size) ...@@ -220,6 +220,10 @@ nv50_dmac_wait(struct nvif_push *push, u32 size)
return 0; return 0;
} }
MODULE_PARM_DESC(kms_vram_pushbuf, "Place EVO/NVD push buffers in VRAM (default: auto)");
static int nv50_dmac_vram_pushbuf = -1;
module_param_named(kms_vram_pushbuf, nv50_dmac_vram_pushbuf, int, 0400);
int int
nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
const s32 *oclass, u8 head, void *data, u32 size, s64 syncbuf, const s32 *oclass, u8 head, void *data, u32 size, s64 syncbuf,
...@@ -241,7 +245,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, ...@@ -241,7 +245,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
* *
* This appears to match NVIDIA's behaviour on Pascal. * This appears to match NVIDIA's behaviour on Pascal.
*/ */
if (device->info.family == NV_DEVICE_INFO_V0_PASCAL) if ((nv50_dmac_vram_pushbuf > 0) ||
(nv50_dmac_vram_pushbuf < 0 && device->info.family == NV_DEVICE_INFO_V0_PASCAL))
type |= NVIF_MEM_VRAM; type |= NVIF_MEM_VRAM;
ret = nvif_mem_ctor_map(&cli->mmu, "kmsChanPush", type, 0x1000, ret = nvif_mem_ctor_map(&cli->mmu, "kmsChanPush", type, 0x1000,
...@@ -304,6 +309,14 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, ...@@ -304,6 +309,14 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
/****************************************************************************** /******************************************************************************
* Output path helpers * Output path helpers
*****************************************************************************/ *****************************************************************************/
static void
nv50_outp_dump_caps(struct nouveau_drm *drm,
struct nouveau_encoder *outp)
{
NV_DEBUG(drm, "%s caps: dp_interlace=%d\n",
outp->base.base.name, outp->caps.dp_interlace);
}
static void static void
nv50_outp_release(struct nouveau_encoder *nv_encoder) nv50_outp_release(struct nouveau_encoder *nv_encoder)
{ {
...@@ -419,8 +432,7 @@ nv50_outp_atomic_check(struct drm_encoder *encoder, ...@@ -419,8 +432,7 @@ nv50_outp_atomic_check(struct drm_encoder *encoder,
} }
struct nouveau_connector * struct nouveau_connector *
nv50_outp_get_new_connector(struct nouveau_encoder *outp, nv50_outp_get_new_connector(struct drm_atomic_state *state, struct nouveau_encoder *outp)
struct drm_atomic_state *state)
{ {
struct drm_connector *connector; struct drm_connector *connector;
struct drm_connector_state *connector_state; struct drm_connector_state *connector_state;
...@@ -436,8 +448,7 @@ nv50_outp_get_new_connector(struct nouveau_encoder *outp, ...@@ -436,8 +448,7 @@ nv50_outp_get_new_connector(struct nouveau_encoder *outp,
} }
struct nouveau_connector * struct nouveau_connector *
nv50_outp_get_old_connector(struct nouveau_encoder *outp, nv50_outp_get_old_connector(struct drm_atomic_state *state, struct nouveau_encoder *outp)
struct drm_atomic_state *state)
{ {
struct drm_connector *connector; struct drm_connector *connector;
struct drm_connector_state *connector_state; struct drm_connector_state *connector_state;
...@@ -452,27 +463,44 @@ nv50_outp_get_old_connector(struct nouveau_encoder *outp, ...@@ -452,27 +463,44 @@ nv50_outp_get_old_connector(struct nouveau_encoder *outp,
return NULL; return NULL;
} }
static struct nouveau_crtc *
nv50_outp_get_new_crtc(const struct drm_atomic_state *state, const struct nouveau_encoder *outp)
{
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
const u32 mask = drm_encoder_mask(&outp->base.base);
int i;
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
if (crtc_state->encoder_mask & mask)
return nouveau_crtc(crtc);
}
return NULL;
}
/****************************************************************************** /******************************************************************************
* DAC * DAC
*****************************************************************************/ *****************************************************************************/
static void static void
nv50_dac_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) nv50_dac_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *state)
{ {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_core *core = nv50_disp(encoder->dev)->core; struct nv50_core *core = nv50_disp(encoder->dev)->core;
const u32 ctrl = NVDEF(NV507D, DAC_SET_CONTROL, OWNER, NONE); const u32 ctrl = NVDEF(NV507D, DAC_SET_CONTROL, OWNER, NONE);
if (nv_encoder->crtc)
core->func->dac->ctrl(core, nv_encoder->or, ctrl, NULL); core->func->dac->ctrl(core, nv_encoder->or, ctrl, NULL);
nv_encoder->crtc = NULL; nv_encoder->crtc = NULL;
nv50_outp_release(nv_encoder); nv50_outp_release(nv_encoder);
} }
static void static void
nv50_dac_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) nv50_dac_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
{ {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_crtc *nv_crtc = nv50_outp_get_new_crtc(state, nv_encoder);
struct nv50_head_atom *asyh = nv50_head_atom(nv_crtc->base.state); struct nv50_head_atom *asyh =
nv50_head_atom(drm_atomic_get_new_crtc_state(state, &nv_crtc->base));
struct nv50_core *core = nv50_disp(encoder->dev)->core; struct nv50_core *core = nv50_disp(encoder->dev)->core;
u32 ctrl = 0; u32 ctrl = 0;
...@@ -493,7 +521,7 @@ nv50_dac_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -493,7 +521,7 @@ nv50_dac_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
core->func->dac->ctrl(core, nv_encoder->or, ctrl, asyh); core->func->dac->ctrl(core, nv_encoder->or, ctrl, asyh);
asyh->or.depth = 0; asyh->or.depth = 0;
nv_encoder->crtc = encoder->crtc; nv_encoder->crtc = &nv_crtc->base;
} }
static enum drm_connector_status static enum drm_connector_status
...@@ -526,8 +554,8 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) ...@@ -526,8 +554,8 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
static const struct drm_encoder_helper_funcs static const struct drm_encoder_helper_funcs
nv50_dac_help = { nv50_dac_help = {
.atomic_check = nv50_outp_atomic_check, .atomic_check = nv50_outp_atomic_check,
.atomic_enable = nv50_dac_enable, .atomic_enable = nv50_dac_atomic_enable,
.atomic_disable = nv50_dac_disable, .atomic_disable = nv50_dac_atomic_disable,
.detect = nv50_dac_detect .detect = nv50_dac_detect
}; };
...@@ -593,34 +621,27 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id, ...@@ -593,34 +621,27 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id,
struct nouveau_drm *drm = nouveau_drm(drm_dev); struct nouveau_drm *drm = nouveau_drm(drm_dev);
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct nouveau_encoder *nv_encoder; struct nouveau_encoder *nv_encoder;
struct drm_connector *connector;
struct nouveau_crtc *nv_crtc; struct nouveau_crtc *nv_crtc;
struct drm_connector_list_iter conn_iter;
int ret = 0; int ret = 0;
*enabled = false; *enabled = false;
mutex_lock(&drm->audio.lock);
drm_for_each_encoder(encoder, drm->dev) { drm_for_each_encoder(encoder, drm->dev) {
struct nouveau_connector *nv_connector = NULL; struct nouveau_connector *nv_connector = NULL;
if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST)
continue; /* TODO */
nv_encoder = nouveau_encoder(encoder); nv_encoder = nouveau_encoder(encoder);
nv_connector = nouveau_connector(nv_encoder->audio.connector);
nv_crtc = nouveau_crtc(nv_encoder->crtc);
drm_connector_list_iter_begin(drm_dev, &conn_iter); if (!nv_crtc || nv_encoder->or != port || nv_crtc->index != dev_id)
drm_for_each_connector_iter(connector, &conn_iter) {
if (connector->state->best_encoder == encoder) {
nv_connector = nouveau_connector(connector);
break;
}
}
drm_connector_list_iter_end(&conn_iter);
if (!nv_connector)
continue; continue;
nv_crtc = nouveau_crtc(encoder->crtc); *enabled = nv_encoder->audio.enabled;
if (!nv_crtc || nv_encoder->or != port ||
nv_crtc->index != dev_id)
continue;
*enabled = nv_encoder->audio;
if (*enabled) { if (*enabled) {
ret = drm_eld_size(nv_connector->base.eld); ret = drm_eld_size(nv_connector->base.eld);
memcpy(buf, nv_connector->base.eld, memcpy(buf, nv_connector->base.eld,
...@@ -629,6 +650,8 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id, ...@@ -629,6 +650,8 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id,
break; break;
} }
mutex_unlock(&drm->audio.lock);
return ret; return ret;
} }
...@@ -678,17 +701,22 @@ static const struct component_ops nv50_audio_component_bind_ops = { ...@@ -678,17 +701,22 @@ static const struct component_ops nv50_audio_component_bind_ops = {
static void static void
nv50_audio_component_init(struct nouveau_drm *drm) nv50_audio_component_init(struct nouveau_drm *drm)
{ {
if (!component_add(drm->dev->dev, &nv50_audio_component_bind_ops)) if (component_add(drm->dev->dev, &nv50_audio_component_bind_ops))
drm->audio.component_registered = true; return;
drm->audio.component_registered = true;
mutex_init(&drm->audio.lock);
} }
static void static void
nv50_audio_component_fini(struct nouveau_drm *drm) nv50_audio_component_fini(struct nouveau_drm *drm)
{ {
if (drm->audio.component_registered) { if (!drm->audio.component_registered)
component_del(drm->dev->dev, &nv50_audio_component_bind_ops); return;
drm->audio.component_registered = false;
} component_del(drm->dev->dev, &nv50_audio_component_bind_ops);
drm->audio.component_registered = false;
mutex_destroy(&drm->audio.lock);
} }
/****************************************************************************** /******************************************************************************
...@@ -711,24 +739,25 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) ...@@ -711,24 +739,25 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
(0x0100 << nv_crtc->index), (0x0100 << nv_crtc->index),
}; };
if (!nv_encoder->audio) mutex_lock(&drm->audio.lock);
return; if (nv_encoder->audio.enabled) {
nv_encoder->audio.enabled = false;
nv_encoder->audio = false; nv_encoder->audio.connector = NULL;
nvif_mthd(&disp->disp->object, 0, &args, sizeof(args)); nvif_mthd(&disp->disp->object, 0, &args, sizeof(args));
}
mutex_unlock(&drm->audio.lock);
nv50_audio_component_eld_notify(drm->audio.component, nv_encoder->or, nv50_audio_component_eld_notify(drm->audio.component, nv_encoder->or,
nv_crtc->index); nv_crtc->index);
} }
static void static void
nv50_audio_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, nv50_audio_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc,
struct nouveau_connector *nv_connector, struct drm_atomic_state *state,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct nouveau_drm *drm = nouveau_drm(encoder->dev); struct nouveau_drm *drm = nouveau_drm(encoder->dev);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nouveau_connector *nv_connector;
struct nv50_disp *disp = nv50_disp(encoder->dev); struct nv50_disp *disp = nv50_disp(encoder->dev);
struct __packed { struct __packed {
struct { struct {
...@@ -744,15 +773,19 @@ nv50_audio_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, ...@@ -744,15 +773,19 @@ nv50_audio_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
(0x0100 << nv_crtc->index), (0x0100 << nv_crtc->index),
}; };
nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
if (!drm_detect_monitor_audio(nv_connector->edid)) if (!drm_detect_monitor_audio(nv_connector->edid))
return; return;
mutex_lock(&drm->audio.lock);
memcpy(args.data, nv_connector->base.eld, sizeof(args.data)); memcpy(args.data, nv_connector->base.eld, sizeof(args.data));
nvif_mthd(&disp->disp->object, 0, &args, nvif_mthd(&disp->disp->object, 0, &args,
sizeof(args.base) + drm_eld_size(args.data)); sizeof(args.base) + drm_eld_size(args.data));
nv_encoder->audio = true; nv_encoder->audio.enabled = true;
nv_encoder->audio.connector = &nv_connector->base;
mutex_unlock(&drm->audio.lock);
nv50_audio_component_eld_notify(drm->audio.component, nv_encoder->or, nv50_audio_component_eld_notify(drm->audio.component, nv_encoder->or,
nv_crtc->index); nv_crtc->index);
...@@ -781,12 +814,12 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) ...@@ -781,12 +814,12 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
} }
static void static void
nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc,
struct nouveau_connector *nv_connector, struct drm_atomic_state *state,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct nouveau_drm *drm = nouveau_drm(encoder->dev); struct nouveau_drm *drm = nouveau_drm(encoder->dev);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nv50_disp *disp = nv50_disp(encoder->dev); struct nv50_disp *disp = nv50_disp(encoder->dev);
struct { struct {
struct nv50_disp_mthd_v1 base; struct nv50_disp_mthd_v1 base;
...@@ -801,7 +834,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, ...@@ -801,7 +834,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
.pwr.state = 1, .pwr.state = 1,
.pwr.rekey = 56, /* binary driver, and tegra, constant */ .pwr.rekey = 56, /* binary driver, and tegra, constant */
}; };
struct nouveau_connector *nv_connector;
struct drm_hdmi_info *hdmi; struct drm_hdmi_info *hdmi;
u32 max_ac_packet; u32 max_ac_packet;
union hdmi_infoframe avi_frame; union hdmi_infoframe avi_frame;
...@@ -811,7 +843,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, ...@@ -811,7 +843,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
int ret; int ret;
int size; int size;
nv_connector = nv50_outp_get_new_connector(nv_encoder, state);
if (!drm_detect_hdmi_monitor(nv_connector->edid)) if (!drm_detect_hdmi_monitor(nv_connector->edid))
return; return;
...@@ -857,7 +888,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, ...@@ -857,7 +888,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state,
+ args.pwr.vendor_infoframe_length; + args.pwr.vendor_infoframe_length;
nvif_mthd(&disp->disp->object, 0, &args, size); nvif_mthd(&disp->disp->object, 0, &args, size);
nv50_audio_enable(encoder, state, mode); nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode);
/* If SCDC is supported by the downstream monitor, update /* If SCDC is supported by the downstream monitor, update
* divider / scrambling settings to what we programmed above. * divider / scrambling settings to what we programmed above.
...@@ -898,6 +929,7 @@ struct nv50_mstc { ...@@ -898,6 +929,7 @@ struct nv50_mstc {
struct nv50_msto { struct nv50_msto {
struct drm_encoder encoder; struct drm_encoder encoder;
/* head is statically assigned on msto creation */
struct nv50_head *head; struct nv50_head *head;
struct nv50_mstc *mstc; struct nv50_mstc *mstc;
bool disabled; bool disabled;
...@@ -1056,11 +1088,12 @@ nv50_dp_bpc_to_depth(unsigned int bpc) ...@@ -1056,11 +1088,12 @@ nv50_dp_bpc_to_depth(unsigned int bpc)
} }
static void static void
nv50_msto_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
{ {
struct nv50_head *head = nv50_head(encoder->crtc);
struct nv50_head_atom *armh = nv50_head_atom(head->base.base.state);
struct nv50_msto *msto = nv50_msto(encoder); struct nv50_msto *msto = nv50_msto(encoder);
struct nv50_head *head = msto->head;
struct nv50_head_atom *asyh =
nv50_head_atom(drm_atomic_get_new_crtc_state(state, &head->base.base));
struct nv50_mstc *mstc = NULL; struct nv50_mstc *mstc = NULL;
struct nv50_mstm *mstm = NULL; struct nv50_mstm *mstm = NULL;
struct drm_connector *connector; struct drm_connector *connector;
...@@ -1081,8 +1114,7 @@ nv50_msto_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1081,8 +1114,7 @@ nv50_msto_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
if (WARN_ON(!mstc)) if (WARN_ON(!mstc))
return; return;
r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, armh->dp.pbn, r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, asyh->dp.pbn, asyh->dp.tu);
armh->dp.tu);
if (!r) if (!r)
DRM_DEBUG_KMS("Failed to allocate VCPI\n"); DRM_DEBUG_KMS("Failed to allocate VCPI\n");
...@@ -1094,15 +1126,15 @@ nv50_msto_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1094,15 +1126,15 @@ nv50_msto_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
else else
proto = NV917D_SOR_SET_CONTROL_PROTOCOL_DP_B; proto = NV917D_SOR_SET_CONTROL_PROTOCOL_DP_B;
mstm->outp->update(mstm->outp, head->base.index, armh, proto, mstm->outp->update(mstm->outp, head->base.index, asyh, proto,
nv50_dp_bpc_to_depth(armh->or.bpc)); nv50_dp_bpc_to_depth(asyh->or.bpc));
msto->mstc = mstc; msto->mstc = mstc;
mstm->modified = true; mstm->modified = true;
} }
static void static void
nv50_msto_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) nv50_msto_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *state)
{ {
struct nv50_msto *msto = nv50_msto(encoder); struct nv50_msto *msto = nv50_msto(encoder);
struct nv50_mstc *mstc = msto->mstc; struct nv50_mstc *mstc = msto->mstc;
...@@ -1119,8 +1151,8 @@ nv50_msto_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1119,8 +1151,8 @@ nv50_msto_disable(struct drm_encoder *encoder, struct drm_atomic_state *state)
static const struct drm_encoder_helper_funcs static const struct drm_encoder_helper_funcs
nv50_msto_help = { nv50_msto_help = {
.atomic_disable = nv50_msto_disable, .atomic_disable = nv50_msto_atomic_disable,
.atomic_enable = nv50_msto_enable, .atomic_enable = nv50_msto_atomic_enable,
.atomic_check = nv50_msto_atomic_check, .atomic_check = nv50_msto_atomic_check,
}; };
...@@ -1616,43 +1648,38 @@ nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, ...@@ -1616,43 +1648,38 @@ nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head,
} }
static void static void
nv50_sor_disable(struct drm_encoder *encoder, nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *state)
struct drm_atomic_state *state)
{ {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
struct nouveau_connector *nv_connector = struct nouveau_connector *nv_connector = nv50_outp_get_old_connector(state, nv_encoder);
nv50_outp_get_old_connector(nv_encoder, state); struct drm_dp_aux *aux = &nv_connector->aux;
u8 pwr;
nv_encoder->crtc = NULL;
if (nv_crtc) {
struct drm_dp_aux *aux = &nv_connector->aux;
u8 pwr;
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
int ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); int ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr);
if (ret == 0) { if (ret == 0) {
pwr &= ~DP_SET_POWER_MASK; pwr &= ~DP_SET_POWER_MASK;
pwr |= DP_SET_POWER_D3; pwr |= DP_SET_POWER_D3;
drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr);
}
} }
nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0);
nv50_audio_disable(encoder, nv_crtc);
nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc);
nv50_outp_release(nv_encoder);
} }
nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0);
nv50_audio_disable(encoder, nv_crtc);
nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc);
nv50_outp_release(nv_encoder);
nv_encoder->crtc = NULL;
} }
static void static void
nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
{ {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_crtc *nv_crtc = nv50_outp_get_new_crtc(state, nv_encoder);
struct nv50_head_atom *asyh = nv50_head_atom(nv_crtc->base.state); struct nv50_head_atom *asyh =
nv50_head_atom(drm_atomic_get_new_crtc_state(state, &nv_crtc->base));
struct drm_display_mode *mode = &asyh->state.adjusted_mode; struct drm_display_mode *mode = &asyh->state.adjusted_mode;
struct { struct {
struct nv50_disp_mthd_v1 base; struct nv50_disp_mthd_v1 base;
...@@ -1672,8 +1699,8 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1672,8 +1699,8 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
u8 proto = NV507D_SOR_SET_CONTROL_PROTOCOL_CUSTOM; u8 proto = NV507D_SOR_SET_CONTROL_PROTOCOL_CUSTOM;
u8 depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT; u8 depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT;
nv_connector = nv50_outp_get_new_connector(nv_encoder, state); nv_connector = nv50_outp_get_new_connector(state, nv_encoder);
nv_encoder->crtc = encoder->crtc; nv_encoder->crtc = &nv_crtc->base;
if ((disp->disp->object.oclass == GT214_DISP || if ((disp->disp->object.oclass == GT214_DISP ||
disp->disp->object.oclass >= GF110_DISP) && disp->disp->object.oclass >= GF110_DISP) &&
...@@ -1699,7 +1726,7 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1699,7 +1726,7 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B; proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B;
} }
nv50_hdmi_enable(&nv_encoder->base.base, state, mode); nv50_hdmi_enable(&nv_encoder->base.base, nv_crtc, nv_connector, state, mode);
break; break;
case DCB_OUTPUT_LVDS: case DCB_OUTPUT_LVDS:
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM; proto = NV507D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM;
...@@ -1740,7 +1767,7 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1740,7 +1767,7 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
else else
proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B; proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B;
nv50_audio_enable(encoder, state, mode); nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode);
break; break;
default: default:
BUG(); BUG();
...@@ -1753,8 +1780,8 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1753,8 +1780,8 @@ nv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
static const struct drm_encoder_helper_funcs static const struct drm_encoder_helper_funcs
nv50_sor_help = { nv50_sor_help = {
.atomic_check = nv50_outp_atomic_check, .atomic_check = nv50_outp_atomic_check,
.atomic_enable = nv50_sor_enable, .atomic_enable = nv50_sor_atomic_enable,
.atomic_disable = nv50_sor_disable, .atomic_disable = nv50_sor_atomic_disable,
}; };
static void static void
...@@ -1821,6 +1848,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) ...@@ -1821,6 +1848,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
drm_connector_attach_encoder(connector, encoder); drm_connector_attach_encoder(connector, encoder);
disp->core->func->sor->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); disp->core->func->sor->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1);
nv50_outp_dump_caps(drm, nv_encoder);
if (dcbe->type == DCB_OUTPUT_DP) { if (dcbe->type == DCB_OUTPUT_DP) {
struct nvkm_i2c_aux *aux = struct nvkm_i2c_aux *aux =
...@@ -1875,23 +1903,24 @@ nv50_pior_atomic_check(struct drm_encoder *encoder, ...@@ -1875,23 +1903,24 @@ nv50_pior_atomic_check(struct drm_encoder *encoder,
} }
static void static void
nv50_pior_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) nv50_pior_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *state)
{ {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_core *core = nv50_disp(encoder->dev)->core; struct nv50_core *core = nv50_disp(encoder->dev)->core;
const u32 ctrl = NVDEF(NV507D, PIOR_SET_CONTROL, OWNER, NONE); const u32 ctrl = NVDEF(NV507D, PIOR_SET_CONTROL, OWNER, NONE);
if (nv_encoder->crtc)
core->func->pior->ctrl(core, nv_encoder->or, ctrl, NULL); core->func->pior->ctrl(core, nv_encoder->or, ctrl, NULL);
nv_encoder->crtc = NULL; nv_encoder->crtc = NULL;
nv50_outp_release(nv_encoder); nv50_outp_release(nv_encoder);
} }
static void static void
nv50_pior_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) nv50_pior_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
{ {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_crtc *nv_crtc = nv50_outp_get_new_crtc(state, nv_encoder);
struct nv50_head_atom *asyh = nv50_head_atom(nv_crtc->base.state); struct nv50_head_atom *asyh =
nv50_head_atom(drm_atomic_get_new_crtc_state(state, &nv_crtc->base));
struct nv50_core *core = nv50_disp(encoder->dev)->core; struct nv50_core *core = nv50_disp(encoder->dev)->core;
u32 ctrl = 0; u32 ctrl = 0;
...@@ -1929,8 +1958,8 @@ nv50_pior_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) ...@@ -1929,8 +1958,8 @@ nv50_pior_enable(struct drm_encoder *encoder, struct drm_atomic_state *state)
static const struct drm_encoder_helper_funcs static const struct drm_encoder_helper_funcs
nv50_pior_help = { nv50_pior_help = {
.atomic_check = nv50_pior_atomic_check, .atomic_check = nv50_pior_atomic_check,
.atomic_enable = nv50_pior_enable, .atomic_enable = nv50_pior_atomic_enable,
.atomic_disable = nv50_pior_disable, .atomic_disable = nv50_pior_atomic_disable,
}; };
static void static void
...@@ -1991,6 +2020,7 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) ...@@ -1991,6 +2020,7 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
drm_connector_attach_encoder(connector, encoder); drm_connector_attach_encoder(connector, encoder);
disp->core->func->pior->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); disp->core->func->pior->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1);
nv50_outp_dump_caps(drm, nv_encoder);
return 0; return 0;
} }
......
...@@ -322,7 +322,7 @@ head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) ...@@ -322,7 +322,7 @@ head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
const int i = head->base.index; const int i = head->base.index;
int ret; int ret;
if ((ret = PUSH_WAIT(push, 14))) if ((ret = PUSH_WAIT(push, 13)))
return ret; return ret;
PUSH_MTHD(push, NV907D, HEAD_SET_OVERSCAN_COLOR(i), PUSH_MTHD(push, NV907D, HEAD_SET_OVERSCAN_COLOR(i),
...@@ -353,14 +353,7 @@ head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) ...@@ -353,14 +353,7 @@ head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
PUSH_MTHD(push, NV907D, HEAD_SET_DEFAULT_BASE_COLOR(i), PUSH_MTHD(push, NV907D, HEAD_SET_DEFAULT_BASE_COLOR(i),
NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, RED, 0) | NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, RED, 0) |
NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, GREEN, 0) | NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, GREEN, 0) |
NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, BLUE, 0), NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, BLUE, 0));
HEAD_SET_CRC_CONTROL(i),
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, CORE) |
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, EXPECT_BUFFER_COLLAPSE, FALSE) |
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, TIMESTAMP_MODE, FALSE) |
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, NONE) |
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, SECONDARY_OUTPUT, NONE));
PUSH_MTHD(push, NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i), PUSH_MTHD(push, NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i),
NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000) | NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000) |
......
...@@ -14,6 +14,7 @@ enum dcb_connector_type { ...@@ -14,6 +14,7 @@ enum dcb_connector_type {
DCB_CONNECTOR_LVDS_SPWG = 0x41, DCB_CONNECTOR_LVDS_SPWG = 0x41,
DCB_CONNECTOR_DP = 0x46, DCB_CONNECTOR_DP = 0x46,
DCB_CONNECTOR_eDP = 0x47, DCB_CONNECTOR_eDP = 0x47,
DCB_CONNECTOR_mDP = 0x48,
DCB_CONNECTOR_HDMI_0 = 0x60, DCB_CONNECTOR_HDMI_0 = 0x60,
DCB_CONNECTOR_HDMI_1 = 0x61, DCB_CONNECTOR_HDMI_1 = 0x61,
DCB_CONNECTOR_HDMI_C = 0x63, DCB_CONNECTOR_HDMI_C = 0x63,
......
...@@ -533,6 +533,7 @@ nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device, ...@@ -533,6 +533,7 @@ nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device,
if (ret) { if (ret) {
NV_PRINTK(err, cli, "channel failed to initialise, %d\n", ret); NV_PRINTK(err, cli, "channel failed to initialise, %d\n", ret);
nouveau_channel_del(pchan); nouveau_channel_del(pchan);
goto done;
} }
ret = nouveau_svmm_join((*pchan)->vmm->svmm, (*pchan)->inst); ret = nouveau_svmm_join((*pchan)->vmm->svmm, (*pchan)->inst);
......
...@@ -1212,6 +1212,7 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb) ...@@ -1212,6 +1212,7 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb)
case DCB_CONNECTOR_DMS59_DP0: case DCB_CONNECTOR_DMS59_DP0:
case DCB_CONNECTOR_DMS59_DP1: case DCB_CONNECTOR_DMS59_DP1:
case DCB_CONNECTOR_DP : case DCB_CONNECTOR_DP :
case DCB_CONNECTOR_mDP :
case DCB_CONNECTOR_USB_C : return DRM_MODE_CONNECTOR_DisplayPort; case DCB_CONNECTOR_USB_C : return DRM_MODE_CONNECTOR_DisplayPort;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP; case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP;
case DCB_CONNECTOR_HDMI_0 : case DCB_CONNECTOR_HDMI_0 :
......
...@@ -221,6 +221,7 @@ struct nouveau_drm { ...@@ -221,6 +221,7 @@ struct nouveau_drm {
struct { struct {
struct drm_audio_component *component; struct drm_audio_component *component;
struct mutex lock;
bool component_registered; bool component_registered;
} audio; } audio;
}; };
......
...@@ -53,7 +53,12 @@ struct nouveau_encoder { ...@@ -53,7 +53,12 @@ struct nouveau_encoder {
* actually programmed on the hw, not the proposed crtc */ * actually programmed on the hw, not the proposed crtc */
struct drm_crtc *crtc; struct drm_crtc *crtc;
u32 ctrl; u32 ctrl;
bool audio;
/* Protected by nouveau_drm.audio.lock */
struct {
bool enabled;
struct drm_connector *connector;
} audio;
struct drm_display_mode mode; struct drm_display_mode mode;
int last_dpms; int last_dpms;
...@@ -141,11 +146,9 @@ enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *, ...@@ -141,11 +146,9 @@ enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *,
unsigned *clock); unsigned *clock);
struct nouveau_connector * struct nouveau_connector *
nv50_outp_get_new_connector(struct nouveau_encoder *outp, nv50_outp_get_new_connector(struct drm_atomic_state *state, struct nouveau_encoder *outp);
struct drm_atomic_state *state);
struct nouveau_connector * struct nouveau_connector *
nv50_outp_get_old_connector(struct nouveau_encoder *outp, nv50_outp_get_old_connector(struct drm_atomic_state *state, struct nouveau_encoder *outp);
struct drm_atomic_state *state);
int nv50_mstm_detect(struct nouveau_encoder *encoder); int nv50_mstm_detect(struct nouveau_encoder *encoder);
void nv50_mstm_remove(struct nv50_mstm *mstm); void nv50_mstm_remove(struct nv50_mstm *mstm);
......
...@@ -36,19 +36,7 @@ ...@@ -36,19 +36,7 @@
#include <nvif/class.h> #include <nvif/class.h>
#include <nvif/cl0080.h> #include <nvif/cl0080.h>
struct gk104_fifo_engine_status { void
bool busy;
bool faulted;
bool chsw;
bool save;
bool load;
struct {
bool tsg;
u32 id;
} prev, next, *chan;
};
static void
gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn, gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn,
struct gk104_fifo_engine_status *status) struct gk104_fifo_engine_status *status)
{ {
...@@ -95,7 +83,7 @@ gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn, ...@@ -95,7 +83,7 @@ gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn,
status->chan == &status->next ? "*" : " "); status->chan == &status->next ? "*" : " ");
} }
static int int
gk104_fifo_class_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, gk104_fifo_class_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass,
void *argv, u32 argc, struct nvkm_object **pobject) void *argv, u32 argc, struct nvkm_object **pobject)
{ {
...@@ -112,7 +100,7 @@ gk104_fifo_class_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass, ...@@ -112,7 +100,7 @@ gk104_fifo_class_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass,
return -EINVAL; return -EINVAL;
} }
static int int
gk104_fifo_class_get(struct nvkm_fifo *base, int index, gk104_fifo_class_get(struct nvkm_fifo *base, int index,
struct nvkm_oclass *oclass) struct nvkm_oclass *oclass)
{ {
...@@ -134,14 +122,14 @@ gk104_fifo_class_get(struct nvkm_fifo *base, int index, ...@@ -134,14 +122,14 @@ gk104_fifo_class_get(struct nvkm_fifo *base, int index,
return c; return c;
} }
static void void
gk104_fifo_uevent_fini(struct nvkm_fifo *fifo) gk104_fifo_uevent_fini(struct nvkm_fifo *fifo)
{ {
struct nvkm_device *device = fifo->engine.subdev.device; struct nvkm_device *device = fifo->engine.subdev.device;
nvkm_mask(device, 0x002140, 0x80000000, 0x00000000); nvkm_mask(device, 0x002140, 0x80000000, 0x00000000);
} }
static void void
gk104_fifo_uevent_init(struct nvkm_fifo *fifo) gk104_fifo_uevent_init(struct nvkm_fifo *fifo)
{ {
struct nvkm_device *device = fifo->engine.subdev.device; struct nvkm_device *device = fifo->engine.subdev.device;
...@@ -556,7 +544,7 @@ gk104_fifo_bind_reason[] = { ...@@ -556,7 +544,7 @@ gk104_fifo_bind_reason[] = {
{} {}
}; };
static void void
gk104_fifo_intr_bind(struct gk104_fifo *fifo) gk104_fifo_intr_bind(struct gk104_fifo *fifo)
{ {
struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
...@@ -627,7 +615,7 @@ gk104_fifo_intr_sched(struct gk104_fifo *fifo) ...@@ -627,7 +615,7 @@ gk104_fifo_intr_sched(struct gk104_fifo *fifo)
} }
} }
static void void
gk104_fifo_intr_chsw(struct gk104_fifo *fifo) gk104_fifo_intr_chsw(struct gk104_fifo *fifo)
{ {
struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
...@@ -637,7 +625,7 @@ gk104_fifo_intr_chsw(struct gk104_fifo *fifo) ...@@ -637,7 +625,7 @@ gk104_fifo_intr_chsw(struct gk104_fifo *fifo)
nvkm_wr32(device, 0x00256c, stat); nvkm_wr32(device, 0x00256c, stat);
} }
static void void
gk104_fifo_intr_dropped_fault(struct gk104_fifo *fifo) gk104_fifo_intr_dropped_fault(struct gk104_fifo *fifo)
{ {
struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
...@@ -680,7 +668,7 @@ static const struct nvkm_bitfield gk104_fifo_pbdma_intr_0[] = { ...@@ -680,7 +668,7 @@ static const struct nvkm_bitfield gk104_fifo_pbdma_intr_0[] = {
{} {}
}; };
static void void
gk104_fifo_intr_pbdma_0(struct gk104_fifo *fifo, int unit) gk104_fifo_intr_pbdma_0(struct gk104_fifo *fifo, int unit)
{ {
struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
...@@ -729,7 +717,7 @@ static const struct nvkm_bitfield gk104_fifo_pbdma_intr_1[] = { ...@@ -729,7 +717,7 @@ static const struct nvkm_bitfield gk104_fifo_pbdma_intr_1[] = {
{} {}
}; };
static void void
gk104_fifo_intr_pbdma_1(struct gk104_fifo *fifo, int unit) gk104_fifo_intr_pbdma_1(struct gk104_fifo *fifo, int unit)
{ {
struct nvkm_subdev *subdev = &fifo->base.engine.subdev; struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
...@@ -750,7 +738,7 @@ gk104_fifo_intr_pbdma_1(struct gk104_fifo *fifo, int unit) ...@@ -750,7 +738,7 @@ gk104_fifo_intr_pbdma_1(struct gk104_fifo *fifo, int unit)
nvkm_wr32(device, 0x040148 + (unit * 0x2000), stat); nvkm_wr32(device, 0x040148 + (unit * 0x2000), stat);
} }
static void void
gk104_fifo_intr_runlist(struct gk104_fifo *fifo) gk104_fifo_intr_runlist(struct gk104_fifo *fifo)
{ {
struct nvkm_device *device = fifo->base.engine.subdev.device; struct nvkm_device *device = fifo->base.engine.subdev.device;
...@@ -763,7 +751,7 @@ gk104_fifo_intr_runlist(struct gk104_fifo *fifo) ...@@ -763,7 +751,7 @@ gk104_fifo_intr_runlist(struct gk104_fifo *fifo)
} }
} }
static void void
gk104_fifo_intr_engine(struct gk104_fifo *fifo) gk104_fifo_intr_engine(struct gk104_fifo *fifo)
{ {
nvkm_fifo_uevent(&fifo->base); nvkm_fifo_uevent(&fifo->base);
...@@ -861,7 +849,7 @@ gk104_fifo_intr(struct nvkm_fifo *base) ...@@ -861,7 +849,7 @@ gk104_fifo_intr(struct nvkm_fifo *base)
} }
} }
static void void
gk104_fifo_fini(struct nvkm_fifo *base) gk104_fifo_fini(struct nvkm_fifo *base)
{ {
struct gk104_fifo *fifo = gk104_fifo(base); struct gk104_fifo *fifo = gk104_fifo(base);
...@@ -871,7 +859,7 @@ gk104_fifo_fini(struct nvkm_fifo *base) ...@@ -871,7 +859,7 @@ gk104_fifo_fini(struct nvkm_fifo *base)
nvkm_mask(device, 0x002140, 0x10000000, 0x10000000); nvkm_mask(device, 0x002140, 0x10000000, 0x10000000);
} }
static int int
gk104_fifo_info(struct nvkm_fifo *base, u64 mthd, u64 *data) gk104_fifo_info(struct nvkm_fifo *base, u64 mthd, u64 *data)
{ {
struct gk104_fifo *fifo = gk104_fifo(base); struct gk104_fifo *fifo = gk104_fifo(base);
...@@ -899,7 +887,7 @@ gk104_fifo_info(struct nvkm_fifo *base, u64 mthd, u64 *data) ...@@ -899,7 +887,7 @@ gk104_fifo_info(struct nvkm_fifo *base, u64 mthd, u64 *data)
} }
} }
static int int
gk104_fifo_oneinit(struct nvkm_fifo *base) gk104_fifo_oneinit(struct nvkm_fifo *base)
{ {
struct gk104_fifo *fifo = gk104_fifo(base); struct gk104_fifo *fifo = gk104_fifo(base);
...@@ -974,7 +962,7 @@ gk104_fifo_oneinit(struct nvkm_fifo *base) ...@@ -974,7 +962,7 @@ gk104_fifo_oneinit(struct nvkm_fifo *base)
return nvkm_memory_map(fifo->user.mem, 0, bar, fifo->user.bar, NULL, 0); return nvkm_memory_map(fifo->user.mem, 0, bar, fifo->user.bar, NULL, 0);
} }
static void void
gk104_fifo_init(struct nvkm_fifo *base) gk104_fifo_init(struct nvkm_fifo *base)
{ {
struct gk104_fifo *fifo = gk104_fifo(base); struct gk104_fifo *fifo = gk104_fifo(base);
...@@ -1006,7 +994,7 @@ gk104_fifo_init(struct nvkm_fifo *base) ...@@ -1006,7 +994,7 @@ gk104_fifo_init(struct nvkm_fifo *base)
nvkm_wr32(device, 0x002140, 0x7fffffff); nvkm_wr32(device, 0x002140, 0x7fffffff);
} }
static void * void *
gk104_fifo_dtor(struct nvkm_fifo *base) gk104_fifo_dtor(struct nvkm_fifo *base)
{ {
struct gk104_fifo *fifo = gk104_fifo(base); struct gk104_fifo *fifo = gk104_fifo(base);
......
...@@ -87,11 +87,43 @@ struct gk104_fifo_func { ...@@ -87,11 +87,43 @@ struct gk104_fifo_func {
bool cgrp_force; bool cgrp_force;
}; };
struct gk104_fifo_engine_status {
bool busy;
bool faulted;
bool chsw;
bool save;
bool load;
struct {
bool tsg;
u32 id;
} prev, next, *chan;
};
int gk104_fifo_new_(const struct gk104_fifo_func *, struct nvkm_device *, int gk104_fifo_new_(const struct gk104_fifo_func *, struct nvkm_device *,
int index, int nr, struct nvkm_fifo **); int index, int nr, struct nvkm_fifo **);
void gk104_fifo_runlist_insert(struct gk104_fifo *, struct gk104_fifo_chan *); void gk104_fifo_runlist_insert(struct gk104_fifo *, struct gk104_fifo_chan *);
void gk104_fifo_runlist_remove(struct gk104_fifo *, struct gk104_fifo_chan *); void gk104_fifo_runlist_remove(struct gk104_fifo *, struct gk104_fifo_chan *);
void gk104_fifo_runlist_update(struct gk104_fifo *, int runl); void gk104_fifo_runlist_update(struct gk104_fifo *, int runl);
void gk104_fifo_engine_status(struct gk104_fifo *fifo, int engn,
struct gk104_fifo_engine_status *status);
void gk104_fifo_intr_bind(struct gk104_fifo *fifo);
void gk104_fifo_intr_chsw(struct gk104_fifo *fifo);
void gk104_fifo_intr_dropped_fault(struct gk104_fifo *fifo);
void gk104_fifo_intr_pbdma_0(struct gk104_fifo *fifo, int unit);
void gk104_fifo_intr_pbdma_1(struct gk104_fifo *fifo, int unit);
void gk104_fifo_intr_runlist(struct gk104_fifo *fifo);
void gk104_fifo_intr_engine(struct gk104_fifo *fifo);
void *gk104_fifo_dtor(struct nvkm_fifo *base);
int gk104_fifo_oneinit(struct nvkm_fifo *base);
int gk104_fifo_info(struct nvkm_fifo *base, u64 mthd, u64 *data);
void gk104_fifo_init(struct nvkm_fifo *base);
void gk104_fifo_fini(struct nvkm_fifo *base);
int gk104_fifo_class_new(struct nvkm_fifo *base, const struct nvkm_oclass *oclass,
void *argv, u32 argc, struct nvkm_object **pobject);
int gk104_fifo_class_get(struct nvkm_fifo *base, int index,
struct nvkm_oclass *oclass);
void gk104_fifo_uevent_fini(struct nvkm_fifo *fifo);
void gk104_fifo_uevent_init(struct nvkm_fifo *fifo);
extern const struct gk104_fifo_pbdma_func gk104_fifo_pbdma; extern const struct gk104_fifo_pbdma_func gk104_fifo_pbdma;
int gk104_fifo_pbdma_nr(struct gk104_fifo *); int gk104_fifo_pbdma_nr(struct gk104_fifo *);
......
...@@ -24,7 +24,13 @@ ...@@ -24,7 +24,13 @@
#include "changk104.h" #include "changk104.h"
#include "user.h" #include "user.h"
#include <core/client.h>
#include <core/gpuobj.h> #include <core/gpuobj.h>
#include <subdev/bar.h>
#include <subdev/fault.h>
#include <subdev/top.h>
#include <subdev/timer.h>
#include <engine/sw.h>
#include <nvif/class.h> #include <nvif/class.h>
...@@ -109,8 +115,364 @@ tu102_fifo = { ...@@ -109,8 +115,364 @@ tu102_fifo = {
.cgrp_force = true, .cgrp_force = true,
}; };
static void
tu102_fifo_recover_work(struct work_struct *w)
{
struct gk104_fifo *fifo = container_of(w, typeof(*fifo), recover.work);
struct nvkm_device *device = fifo->base.engine.subdev.device;
struct nvkm_engine *engine;
unsigned long flags;
u32 engm, runm, todo;
int engn, runl;
spin_lock_irqsave(&fifo->base.lock, flags);
runm = fifo->recover.runm;
engm = fifo->recover.engm;
fifo->recover.engm = 0;
fifo->recover.runm = 0;
spin_unlock_irqrestore(&fifo->base.lock, flags);
nvkm_mask(device, 0x002630, runm, runm);
for (todo = engm; engn = __ffs(todo), todo; todo &= ~BIT(engn)) {
if ((engine = fifo->engine[engn].engine)) {
nvkm_subdev_fini(&engine->subdev, false);
WARN_ON(nvkm_subdev_init(&engine->subdev));
}
}
for (todo = runm; runl = __ffs(todo), todo; todo &= ~BIT(runl))
gk104_fifo_runlist_update(fifo, runl);
nvkm_mask(device, 0x002630, runm, 0x00000000);
}
static void tu102_fifo_recover_engn(struct gk104_fifo *fifo, int engn);
static void
tu102_fifo_recover_runl(struct gk104_fifo *fifo, int runl)
{
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
const u32 runm = BIT(runl);
assert_spin_locked(&fifo->base.lock);
if (fifo->recover.runm & runm)
return;
fifo->recover.runm |= runm;
/* Block runlist to prevent channel assignment(s) from changing. */
nvkm_mask(device, 0x002630, runm, runm);
/* Schedule recovery. */
nvkm_warn(subdev, "runlist %d: scheduled for recovery\n", runl);
schedule_work(&fifo->recover.work);
}
static struct gk104_fifo_chan *
tu102_fifo_recover_chid(struct gk104_fifo *fifo, int runl, int chid)
{
struct gk104_fifo_chan *chan;
struct nvkm_fifo_cgrp *cgrp;
list_for_each_entry(chan, &fifo->runlist[runl].chan, head) {
if (chan->base.chid == chid) {
list_del_init(&chan->head);
return chan;
}
}
list_for_each_entry(cgrp, &fifo->runlist[runl].cgrp, head) {
if (cgrp->id == chid) {
chan = list_first_entry(&cgrp->chan, typeof(*chan), head);
list_del_init(&chan->head);
if (!--cgrp->chan_nr)
list_del_init(&cgrp->head);
return chan;
}
}
return NULL;
}
static void
tu102_fifo_recover_chan(struct nvkm_fifo *base, int chid)
{
struct gk104_fifo *fifo = gk104_fifo(base);
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
const u32 stat = nvkm_rd32(device, 0x800004 + (chid * 0x08));
const u32 runl = (stat & 0x000f0000) >> 16;
const bool used = (stat & 0x00000001);
unsigned long engn, engm = fifo->runlist[runl].engm;
struct gk104_fifo_chan *chan;
assert_spin_locked(&fifo->base.lock);
if (!used)
return;
/* Lookup SW state for channel, and mark it as dead. */
chan = tu102_fifo_recover_chid(fifo, runl, chid);
if (chan) {
chan->killed = true;
nvkm_fifo_kevent(&fifo->base, chid);
}
/* Disable channel. */
nvkm_wr32(device, 0x800004 + (chid * 0x08), stat | 0x00000800);
nvkm_warn(subdev, "channel %d: killed\n", chid);
/* Block channel assignments from changing during recovery. */
tu102_fifo_recover_runl(fifo, runl);
/* Schedule recovery for any engines the channel is on. */
for_each_set_bit(engn, &engm, fifo->engine_nr) {
struct gk104_fifo_engine_status status;
gk104_fifo_engine_status(fifo, engn, &status);
if (!status.chan || status.chan->id != chid)
continue;
tu102_fifo_recover_engn(fifo, engn);
}
}
static void
tu102_fifo_recover_engn(struct gk104_fifo *fifo, int engn)
{
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
const u32 runl = fifo->engine[engn].runl;
const u32 engm = BIT(engn);
struct gk104_fifo_engine_status status;
assert_spin_locked(&fifo->base.lock);
if (fifo->recover.engm & engm)
return;
fifo->recover.engm |= engm;
/* Block channel assignments from changing during recovery. */
tu102_fifo_recover_runl(fifo, runl);
/* Determine which channel (if any) is currently on the engine. */
gk104_fifo_engine_status(fifo, engn, &status);
if (status.chan) {
/* The channel is not longer viable, kill it. */
tu102_fifo_recover_chan(&fifo->base, status.chan->id);
}
/* Preempt the runlist */
nvkm_wr32(device, 0x2638, BIT(runl));
/* Schedule recovery. */
nvkm_warn(subdev, "engine %d: scheduled for recovery\n", engn);
schedule_work(&fifo->recover.work);
}
static void
tu102_fifo_fault(struct nvkm_fifo *base, struct nvkm_fault_data *info)
{
struct gk104_fifo *fifo = gk104_fifo(base);
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
const struct nvkm_enum *er, *ee, *ec, *ea;
struct nvkm_engine *engine = NULL;
struct nvkm_fifo_chan *chan;
unsigned long flags;
char ct[8] = "HUB/", en[16] = "";
int engn;
er = nvkm_enum_find(fifo->func->fault.reason, info->reason);
ee = nvkm_enum_find(fifo->func->fault.engine, info->engine);
if (info->hub) {
ec = nvkm_enum_find(fifo->func->fault.hubclient, info->client);
} else {
ec = nvkm_enum_find(fifo->func->fault.gpcclient, info->client);
snprintf(ct, sizeof(ct), "GPC%d/", info->gpc);
}
ea = nvkm_enum_find(fifo->func->fault.access, info->access);
if (ee && ee->data2) {
switch (ee->data2) {
case NVKM_SUBDEV_BAR:
nvkm_bar_bar1_reset(device);
break;
case NVKM_SUBDEV_INSTMEM:
nvkm_bar_bar2_reset(device);
break;
case NVKM_ENGINE_IFB:
nvkm_mask(device, 0x001718, 0x00000000, 0x00000000);
break;
default:
engine = nvkm_device_engine(device, ee->data2);
break;
}
}
if (ee == NULL) {
enum nvkm_devidx engidx = nvkm_top_fault(device, info->engine);
if (engidx < NVKM_SUBDEV_NR) {
const char *src = nvkm_subdev_name[engidx];
char *dst = en;
do {
*dst++ = toupper(*src++);
} while (*src);
engine = nvkm_device_engine(device, engidx);
}
} else {
snprintf(en, sizeof(en), "%s", ee->name);
}
spin_lock_irqsave(&fifo->base.lock, flags);
chan = nvkm_fifo_chan_inst_locked(&fifo->base, info->inst);
nvkm_error(subdev,
"fault %02x [%s] at %016llx engine %02x [%s] client %02x "
"[%s%s] reason %02x [%s] on channel %d [%010llx %s]\n",
info->access, ea ? ea->name : "", info->addr,
info->engine, ee ? ee->name : en,
info->client, ct, ec ? ec->name : "",
info->reason, er ? er->name : "", chan ? chan->chid : -1,
info->inst, chan ? chan->object.client->name : "unknown");
/* Kill the channel that caused the fault. */
if (chan)
tu102_fifo_recover_chan(&fifo->base, chan->chid);
/* Channel recovery will probably have already done this for the
* correct engine(s), but just in case we can't find the channel
* information...
*/
for (engn = 0; engn < fifo->engine_nr && engine; engn++) {
if (fifo->engine[engn].engine == engine) {
tu102_fifo_recover_engn(fifo, engn);
break;
}
}
spin_unlock_irqrestore(&fifo->base.lock, flags);
}
static void
tu102_fifo_intr_ctxsw_timeout(struct gk104_fifo *fifo)
{
struct nvkm_device *device = fifo->base.engine.subdev.device;
unsigned long flags, engm;
u32 engn;
spin_lock_irqsave(&fifo->base.lock, flags);
engm = nvkm_rd32(device, 0x2a30);
nvkm_wr32(device, 0x2a30, engm);
for_each_set_bit(engn, &engm, 32)
tu102_fifo_recover_engn(fifo, engn);
spin_unlock_irqrestore(&fifo->base.lock, flags);
}
static void
tu102_fifo_intr_sched(struct gk104_fifo *fifo)
{
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
u32 intr = nvkm_rd32(device, 0x00254c);
u32 code = intr & 0x000000ff;
nvkm_error(subdev, "SCHED_ERROR %02x\n", code);
}
static void
tu102_fifo_intr(struct nvkm_fifo *base)
{
struct gk104_fifo *fifo = gk104_fifo(base);
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
u32 mask = nvkm_rd32(device, 0x002140);
u32 stat = nvkm_rd32(device, 0x002100) & mask;
if (stat & 0x00000001) {
gk104_fifo_intr_bind(fifo);
nvkm_wr32(device, 0x002100, 0x00000001);
stat &= ~0x00000001;
}
if (stat & 0x00000002) {
tu102_fifo_intr_ctxsw_timeout(fifo);
stat &= ~0x00000002;
}
if (stat & 0x00000100) {
tu102_fifo_intr_sched(fifo);
nvkm_wr32(device, 0x002100, 0x00000100);
stat &= ~0x00000100;
}
if (stat & 0x00010000) {
gk104_fifo_intr_chsw(fifo);
nvkm_wr32(device, 0x002100, 0x00010000);
stat &= ~0x00010000;
}
if (stat & 0x20000000) {
u32 mask = nvkm_rd32(device, 0x0025a0);
while (mask) {
u32 unit = __ffs(mask);
gk104_fifo_intr_pbdma_0(fifo, unit);
gk104_fifo_intr_pbdma_1(fifo, unit);
nvkm_wr32(device, 0x0025a0, (1 << unit));
mask &= ~(1 << unit);
}
stat &= ~0x20000000;
}
if (stat & 0x40000000) {
gk104_fifo_intr_runlist(fifo);
stat &= ~0x40000000;
}
if (stat & 0x80000000) {
nvkm_wr32(device, 0x002100, 0x80000000);
gk104_fifo_intr_engine(fifo);
stat &= ~0x80000000;
}
if (stat) {
nvkm_error(subdev, "INTR %08x\n", stat);
nvkm_mask(device, 0x002140, stat, 0x00000000);
nvkm_wr32(device, 0x002100, stat);
}
}
static const struct nvkm_fifo_func
tu102_fifo_ = {
.dtor = gk104_fifo_dtor,
.oneinit = gk104_fifo_oneinit,
.info = gk104_fifo_info,
.init = gk104_fifo_init,
.fini = gk104_fifo_fini,
.intr = tu102_fifo_intr,
.fault = tu102_fifo_fault,
.uevent_init = gk104_fifo_uevent_init,
.uevent_fini = gk104_fifo_uevent_fini,
.recover_chan = tu102_fifo_recover_chan,
.class_get = gk104_fifo_class_get,
.class_new = gk104_fifo_class_new,
};
int int
tu102_fifo_new(struct nvkm_device *device, int index, struct nvkm_fifo **pfifo) tu102_fifo_new(struct nvkm_device *device, int index, struct nvkm_fifo **pfifo)
{ {
return gk104_fifo_new_(&tu102_fifo, device, index, 4096, pfifo); struct gk104_fifo *fifo;
if (!(fifo = kzalloc(sizeof(*fifo), GFP_KERNEL)))
return -ENOMEM;
fifo->func = &tu102_fifo;
INIT_WORK(&fifo->recover.work, tu102_fifo_recover_work);
*pfifo = &fifo->base;
return nvkm_fifo_ctor(&tu102_fifo_, device, index, 4096, &fifo->base);
} }
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "priv.h" #include "priv.h"
#include <core/memory.h> #include <core/memory.h>
#include <subdev/mc.h>
#include <subdev/mmu.h> #include <subdev/mmu.h>
#include <engine/fifo.h> #include <engine/fifo.h>
...@@ -34,6 +35,9 @@ tu102_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable) ...@@ -34,6 +35,9 @@ tu102_fault_buffer_intr(struct nvkm_fault_buffer *buffer, bool enable)
* which don't appear to actually work anymore, but newer * which don't appear to actually work anymore, but newer
* versions of RM don't appear to touch anything at all.. * versions of RM don't appear to touch anything at all..
*/ */
struct nvkm_device *device = buffer->fault->subdev.device;
nvkm_mc_intr_mask(device, NVKM_SUBDEV_FAULT, enable);
} }
static void static void
...@@ -41,6 +45,11 @@ tu102_fault_buffer_fini(struct nvkm_fault_buffer *buffer) ...@@ -41,6 +45,11 @@ tu102_fault_buffer_fini(struct nvkm_fault_buffer *buffer)
{ {
struct nvkm_device *device = buffer->fault->subdev.device; struct nvkm_device *device = buffer->fault->subdev.device;
const u32 foff = buffer->id * 0x20; const u32 foff = buffer->id * 0x20;
/* Disable the fault interrupts */
nvkm_wr32(device, 0xb81408, 0x1);
nvkm_wr32(device, 0xb81410, 0x10);
nvkm_mask(device, 0xb83010 + foff, 0x80000000, 0x00000000); nvkm_mask(device, 0xb83010 + foff, 0x80000000, 0x00000000);
} }
...@@ -50,6 +59,10 @@ tu102_fault_buffer_init(struct nvkm_fault_buffer *buffer) ...@@ -50,6 +59,10 @@ tu102_fault_buffer_init(struct nvkm_fault_buffer *buffer)
struct nvkm_device *device = buffer->fault->subdev.device; struct nvkm_device *device = buffer->fault->subdev.device;
const u32 foff = buffer->id * 0x20; const u32 foff = buffer->id * 0x20;
/* Enable the fault interrupts */
nvkm_wr32(device, 0xb81208, 0x1);
nvkm_wr32(device, 0xb81210, 0x10);
nvkm_mask(device, 0xb83010 + foff, 0xc0000000, 0x40000000); nvkm_mask(device, 0xb83010 + foff, 0xc0000000, 0x40000000);
nvkm_wr32(device, 0xb83004 + foff, upper_32_bits(buffer->addr)); nvkm_wr32(device, 0xb83004 + foff, upper_32_bits(buffer->addr));
nvkm_wr32(device, 0xb83000 + foff, lower_32_bits(buffer->addr)); nvkm_wr32(device, 0xb83000 + foff, lower_32_bits(buffer->addr));
...@@ -109,14 +122,20 @@ tu102_fault_intr(struct nvkm_fault *fault) ...@@ -109,14 +122,20 @@ tu102_fault_intr(struct nvkm_fault *fault)
} }
if (stat & 0x00000200) { if (stat & 0x00000200) {
/* Clear the associated interrupt flag */
nvkm_wr32(device, 0xb81010, 0x10);
if (fault->buffer[0]) { if (fault->buffer[0]) {
nvkm_event_send(&fault->event, 1, 0, NULL, 0); nvkm_event_send(&fault->event, 1, 0, NULL, 0);
stat &= ~0x00000200; stat &= ~0x00000200;
} }
} }
/*XXX: guess, can't confirm until we get fw... */ /* Replayable MMU fault */
if (stat & 0x00000100) { if (stat & 0x00000100) {
/* Clear the associated interrupt flag */
nvkm_wr32(device, 0xb81008, 0x1);
if (fault->buffer[1]) { if (fault->buffer[1]) {
nvkm_event_send(&fault->event, 1, 1, NULL, 0); nvkm_event_send(&fault->event, 1, 1, NULL, 0);
stat &= ~0x00000100; stat &= ~0x00000100;
......
...@@ -108,9 +108,6 @@ nvkm_mc_intr(struct nvkm_device *device, bool *handled) ...@@ -108,9 +108,6 @@ nvkm_mc_intr(struct nvkm_device *device, bool *handled)
if (stat) if (stat)
nvkm_error(&mc->subdev, "intr %08x\n", stat); nvkm_error(&mc->subdev, "intr %08x\n", stat);
*handled = intr != 0; *handled = intr != 0;
if (mc->func->intr_hack)
mc->func->intr_hack(mc, handled);
} }
static u32 static u32
......
...@@ -26,7 +26,6 @@ struct nvkm_mc_func { ...@@ -26,7 +26,6 @@ struct nvkm_mc_func {
void (*intr_mask)(struct nvkm_mc *, u32 mask, u32 stat); void (*intr_mask)(struct nvkm_mc *, u32 mask, u32 stat);
/* retrieve pending interrupt mask (NV_PMC_INTR) */ /* retrieve pending interrupt mask (NV_PMC_INTR) */
u32 (*intr_stat)(struct nvkm_mc *); u32 (*intr_stat)(struct nvkm_mc *);
void (*intr_hack)(struct nvkm_mc *, bool *handled);
const struct nvkm_mc_map *reset; const struct nvkm_mc_map *reset;
void (*unk260)(struct nvkm_mc *, u32); void (*unk260)(struct nvkm_mc *, u32);
}; };
......
...@@ -19,37 +19,118 @@ ...@@ -19,37 +19,118 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
#define tu102_mc(p) container_of((p), struct tu102_mc, base)
#include "priv.h" #include "priv.h"
struct tu102_mc {
struct nvkm_mc base;
spinlock_t lock;
bool intr;
u32 mask;
};
static void static void
tu102_mc_intr_hack(struct nvkm_mc *mc, bool *handled) tu102_mc_intr_update(struct tu102_mc *mc)
{ {
struct nvkm_device *device = mc->subdev.device; struct nvkm_device *device = mc->base.subdev.device;
u32 stat = nvkm_rd32(device, 0xb81010); u32 mask = mc->intr ? mc->mask : 0, i;
if (stat & 0x00000050) {
struct nvkm_subdev *subdev = for (i = 0; i < 2; i++) {
nvkm_device_subdev(device, NVKM_SUBDEV_FAULT); nvkm_wr32(device, 0x000180 + (i * 0x04), ~mask);
nvkm_wr32(device, 0xb81010, stat & 0x00000050); nvkm_wr32(device, 0x000160 + (i * 0x04), mask);
if (subdev)
nvkm_subdev_intr(subdev);
*handled = true;
} }
if (mask & 0x00000200)
nvkm_wr32(device, 0xb81608, 0x6);
else
nvkm_wr32(device, 0xb81610, 0x6);
}
void
tu102_mc_intr_unarm(struct nvkm_mc *base)
{
struct tu102_mc *mc = tu102_mc(base);
unsigned long flags;
spin_lock_irqsave(&mc->lock, flags);
mc->intr = false;
tu102_mc_intr_update(mc);
spin_unlock_irqrestore(&mc->lock, flags);
}
void
tu102_mc_intr_rearm(struct nvkm_mc *base)
{
struct tu102_mc *mc = tu102_mc(base);
unsigned long flags;
spin_lock_irqsave(&mc->lock, flags);
mc->intr = true;
tu102_mc_intr_update(mc);
spin_unlock_irqrestore(&mc->lock, flags);
}
void
tu102_mc_intr_mask(struct nvkm_mc *base, u32 mask, u32 intr)
{
struct tu102_mc *mc = tu102_mc(base);
unsigned long flags;
spin_lock_irqsave(&mc->lock, flags);
mc->mask = (mc->mask & ~mask) | intr;
tu102_mc_intr_update(mc);
spin_unlock_irqrestore(&mc->lock, flags);
}
static u32
tu102_mc_intr_stat(struct nvkm_mc *mc)
{
struct nvkm_device *device = mc->subdev.device;
u32 intr0 = nvkm_rd32(device, 0x000100);
u32 intr1 = nvkm_rd32(device, 0x000104);
u32 intr_top = nvkm_rd32(device, 0xb81600);
/* Turing and above route the MMU fault interrupts via a different
* interrupt tree with different control registers. For the moment remap
* them back to the old PMC vector.
*/
if (intr_top & 0x00000006)
intr0 |= 0x00000200;
return intr0 | intr1;
} }
static const struct nvkm_mc_func static const struct nvkm_mc_func
tu102_mc = { tu102_mc = {
.init = nv50_mc_init, .init = nv50_mc_init,
.intr = gp100_mc_intr, .intr = gp100_mc_intr,
.intr_unarm = gp100_mc_intr_unarm, .intr_unarm = tu102_mc_intr_unarm,
.intr_rearm = gp100_mc_intr_rearm, .intr_rearm = tu102_mc_intr_rearm,
.intr_mask = gp100_mc_intr_mask, .intr_mask = tu102_mc_intr_mask,
.intr_stat = gf100_mc_intr_stat, .intr_stat = tu102_mc_intr_stat,
.intr_hack = tu102_mc_intr_hack,
.reset = gk104_mc_reset, .reset = gk104_mc_reset,
}; };
int
tu102_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device,
int index, struct nvkm_mc **pmc)
{
struct tu102_mc *mc;
if (!(mc = kzalloc(sizeof(*mc), GFP_KERNEL)))
return -ENOMEM;
nvkm_mc_ctor(func, device, index, &mc->base);
*pmc = &mc->base;
spin_lock_init(&mc->lock);
mc->intr = false;
mc->mask = 0x7fffffff;
return 0;
}
int int
tu102_mc_new(struct nvkm_device *device, int index, struct nvkm_mc **pmc) tu102_mc_new(struct nvkm_device *device, int index, struct nvkm_mc **pmc)
{ {
return gp100_mc_new_(&tu102_mc, device, index, pmc); return tu102_mc_new_(&tu102_mc, device, index, pmc);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment