Skip to content

Commit

Permalink
drm/nouveau/disp: move HDMI config into acquire + infoframe methods
Browse files Browse the repository at this point in the history
v2:
- fix typo in sorhdmi/g84 struct initialiser (kbuild test robot)
v3:
- less convoluted flow control in nvkm_uoutp_mthd_acquire_tmds() (lyude)
v4:
- we don't support hdmi on original nv50, don't try

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
  • Loading branch information
Ben Skeggs committed Nov 8, 2022
1 parent 9793083 commit f530bc6
Show file tree
Hide file tree
Showing 21 changed files with 503 additions and 378 deletions.
172 changes: 69 additions & 103 deletions drivers/gpu/drm/nouveau/dispnv50/disp.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include <nvif/cl0002.h>
#include <nvif/cl5070.h>
#include <nvif/event.h>
#include <nvif/if0012.h>
#include <nvif/if0014.h>
#include <nvif/timer.h>

Expand Down Expand Up @@ -744,123 +745,85 @@ nv50_audio_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc,
/******************************************************************************
* HDMI
*****************************************************************************/
static void
nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_disp *disp = nv50_disp(encoder->dev);
struct {
struct nv50_disp_mthd_v1 base;
struct nv50_disp_sor_hdmi_pwr_v0 pwr;
} args = {
.base.version = 1,
.base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR,
.base.hasht = nv_encoder->dcb->hasht,
.base.hashm = (0xf0ff & nv_encoder->dcb->hashm) |
(0x0100 << nv_crtc->index),
};

nvif_mthd(&disp->disp->object, 0, &args, sizeof(args));
}

static void
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, bool hda)
{
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_disp *disp = nv50_disp(encoder->dev);
struct {
struct nv50_disp_mthd_v1 base;
struct nv50_disp_sor_hdmi_pwr_v0 pwr;
u8 infoframes[2 * 17]; /* two frames, up to 17 bytes each */
} args = {
.base.version = 1,
.base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR,
.base.hasht = nv_encoder->dcb->hasht,
.base.hashm = (0xf0ff & nv_encoder->dcb->hashm) |
(0x0100 << nv_crtc->index),
.pwr.state = 1,
.pwr.rekey = 56, /* binary driver, and tegra, constant */
};
struct drm_hdmi_info *hdmi;
struct drm_hdmi_info *hdmi = &nv_connector->base.display_info.hdmi;
union hdmi_infoframe infoframe;
const u8 rekey = 56; /* binary driver, and tegra, constant */
u8 config, scdc = 0;
u32 max_ac_packet;
union hdmi_infoframe avi_frame;
union hdmi_infoframe vendor_frame;
bool high_tmds_clock_ratio = false, scrambling = false;
u8 config;
int ret;
int size;

if (!drm_detect_hdmi_monitor(nv_connector->edid))
return;

hdmi = &nv_connector->base.display_info.hdmi;

ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi,
&nv_connector->base, mode);
if (!ret) {
drm_hdmi_avi_infoframe_quant_range(&avi_frame.avi,
&nv_connector->base, mode,
HDMI_QUANTIZATION_RANGE_FULL);
/* We have an AVI InfoFrame, populate it to the display */
args.pwr.avi_infoframe_length
= hdmi_infoframe_pack(&avi_frame, args.infoframes, 17);
}

ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi,
&nv_connector->base, mode);
if (!ret) {
/* We have a Vendor InfoFrame, populate it to the display */
args.pwr.vendor_infoframe_length
= hdmi_infoframe_pack(&vendor_frame,
args.infoframes
+ args.pwr.avi_infoframe_length,
17);
}
struct {
struct nvif_outp_infoframe_v0 infoframe;
u8 data[17];
} args;
int ret, size;

max_ac_packet = mode->htotal - mode->hdisplay;
max_ac_packet -= args.pwr.rekey;
max_ac_packet -= rekey;
max_ac_packet -= 18; /* constant from tegra */
args.pwr.max_ac_packet = max_ac_packet / 32;
max_ac_packet /= 32;

if (hdmi->scdc.scrambling.supported) {
high_tmds_clock_ratio = mode->clock > 340000;
scrambling = high_tmds_clock_ratio ||
hdmi->scdc.scrambling.low_rates;
}
const bool high_tmds_clock_ratio = mode->clock > 340000;

args.pwr.scdc =
NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE * scrambling |
NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 * high_tmds_clock_ratio;
ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &config);
if (ret < 0) {
NV_ERROR(drm, "Failure to read SCDC_TMDS_CONFIG: %d\n", ret);
return;
}

size = sizeof(args.base)
+ sizeof(args.pwr)
+ args.pwr.avi_infoframe_length
+ args.pwr.vendor_infoframe_length;
nvif_mthd(&disp->disp->object, 0, &args, size);
config &= ~(SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE);
if (high_tmds_clock_ratio || hdmi->scdc.scrambling.low_rates)
config |= SCDC_SCRAMBLING_ENABLE;
if (high_tmds_clock_ratio)
config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;

nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode);
ret = drm_scdc_writeb(nv_encoder->i2c, SCDC_TMDS_CONFIG, config);
if (ret < 0)
NV_ERROR(drm, "Failure to write SCDC_TMDS_CONFIG = 0x%02x: %d\n",
config, ret);

/* If SCDC is supported by the downstream monitor, update
* divider / scrambling settings to what we programmed above.
*/
if (!hdmi->scdc.scrambling.supported)
return;
if (high_tmds_clock_ratio || hdmi->scdc.scrambling.low_rates)
scdc |= NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_SCRAMBLE;
if (high_tmds_clock_ratio)
scdc |= NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_DIV_BY_4;
}

ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &config);
if (ret < 0) {
NV_ERROR(drm, "Failure to read SCDC_TMDS_CONFIG: %d\n", ret);
ret = nvif_outp_acquire_tmds(&nv_encoder->outp, nv_crtc->index, true,
max_ac_packet, rekey, scdc, hda);
if (ret)
return;

/* AVI InfoFrame. */
args.infoframe.version = 0;
args.infoframe.head = nv_crtc->index;

if (!drm_hdmi_avi_infoframe_from_display_mode(&infoframe.avi, &nv_connector->base, mode)) {
drm_hdmi_avi_infoframe_quant_range(&infoframe.avi, &nv_connector->base, mode,
HDMI_QUANTIZATION_RANGE_FULL);

size = hdmi_infoframe_pack(&infoframe, args.data, 17);
} else {
size = 0;
}
config &= ~(SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE);
config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 * high_tmds_clock_ratio;
config |= SCDC_SCRAMBLING_ENABLE * scrambling;
ret = drm_scdc_writeb(nv_encoder->i2c, SCDC_TMDS_CONFIG, config);
if (ret < 0)
NV_ERROR(drm, "Failure to write SCDC_TMDS_CONFIG = 0x%02x: %d\n",
config, ret);

nvif_outp_infoframe(&nv_encoder->outp, NVIF_OUTP_INFOFRAME_V0_AVI, &args.infoframe, size);

/* Vendor InfoFrame. */
if (!drm_hdmi_vendor_infoframe_from_display_mode(&infoframe.vendor.hdmi,
&nv_connector->base, mode))
size = hdmi_infoframe_pack(&infoframe, args.data, 17);
else
size = 0;

nvif_outp_infoframe(&nv_encoder->outp, NVIF_OUTP_INFOFRAME_V0_VSI, &args.infoframe, size);

nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode);
}

/******************************************************************************
Expand Down Expand Up @@ -1622,7 +1585,6 @@ nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *st

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);
nvif_outp_release(&nv_encoder->outp);
nv_encoder->crtc = NULL;
}
Expand All @@ -1636,6 +1598,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta
nv50_head_atom(drm_atomic_get_new_crtc_state(state, &nv_crtc->base));
struct drm_display_mode *mode = &asyh->state.adjusted_mode;
struct nv50_disp *disp = nv50_disp(encoder->dev);
struct nvif_outp *outp = &nv_encoder->outp;
struct drm_device *dev = encoder->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_connector *nv_connector;
Expand All @@ -1657,7 +1620,12 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta

switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_TMDS:
nvif_outp_acquire_tmds(&nv_encoder->outp, hda);
if (disp->disp->object.oclass == NV50_DISP ||
!drm_detect_hdmi_monitor(nv_connector->edid))
nvif_outp_acquire_tmds(outp, nv_crtc->index, false, 0, 0, 0, false);
else
nv50_hdmi_enable(encoder, nv_crtc, nv_connector, state, mode, hda);

if (nv_encoder->outp.or.link & 1) {
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_A;
/* Only enable dual-link if:
Expand All @@ -1673,8 +1641,6 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta
} else {
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B;
}

nv50_hdmi_enable(&nv_encoder->base.base, nv_crtc, nv_connector, state, mode);
break;
case DCB_OUTPUT_LVDS:
proto = NV507D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM;
Expand Down Expand Up @@ -1900,7 +1866,7 @@ nv50_pior_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st
switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_TMDS:
ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC);
nvif_outp_acquire_tmds(&nv_encoder->outp, false);
nvif_outp_acquire_tmds(&nv_encoder->outp, false, false, 0, 0, 0, false);
break;
case DCB_OUTPUT_DP:
ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC);
Expand Down
14 changes: 0 additions & 14 deletions drivers/gpu/drm/nouveau/include/nvif/cl5070.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ struct nv50_disp_scanoutpos_v0 {
struct nv50_disp_mthd_v1 {
__u8 version;
#define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21
#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22
#define NV50_DISP_MTHD_V1_SOR_DP_MST_LINK 0x25
#define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26
__u8 method;
Expand All @@ -44,19 +43,6 @@ struct nv50_disp_sor_hda_eld_v0 {
__u8 data[];
};

struct nv50_disp_sor_hdmi_pwr_v0 {
__u8 version;
__u8 state;
__u8 max_ac_packet;
__u8 rekey;
__u8 avi_infoframe_length;
__u8 vendor_infoframe_length;
#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE (1 << 0)
#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 (1 << 1)
__u8 scdc;
__u8 pad07[1];
};

struct nv50_disp_sor_dp_mst_link_v0 {
__u8 version;
__u8 state;
Expand Down
24 changes: 22 additions & 2 deletions drivers/gpu/drm/nouveau/include/nvif/if0012.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ union nvif_outp_args {
#define NVIF_OUTP_V0_LOAD_DETECT 0x00
#define NVIF_OUTP_V0_ACQUIRE 0x01
#define NVIF_OUTP_V0_RELEASE 0x02
#define NVIF_OUTP_V0_INFOFRAME 0x03

union nvif_outp_load_detect_args {
struct nvif_outp_load_detect_v0 {
Expand All @@ -37,8 +38,15 @@ union nvif_outp_acquire_args {
__u8 pad04[4];
union {
struct {
__u8 hda;
__u8 pad01[7];
__u8 head;
__u8 hdmi;
__u8 hdmi_max_ac_packet;
__u8 hdmi_rekey;
#define NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_SCRAMBLE (1 << 0)
#define NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_DIV_BY_4 (1 << 1)
__u8 hdmi_scdc;
__u8 hdmi_hda;
__u8 pad06[2];
} tmds;
struct {
__u8 dual;
Expand All @@ -57,4 +65,16 @@ union nvif_outp_release_args {
struct nvif_outp_release_vn {
} vn;
};

union nvif_outp_infoframe_args {
struct nvif_outp_infoframe_v0 {
__u8 version;
#define NVIF_OUTP_INFOFRAME_V0_AVI 0
#define NVIF_OUTP_INFOFRAME_V0_VSI 1
__u8 type;
__u8 head;
__u8 pad03[5];
__u8 data[];
} v0;
};
#endif
5 changes: 4 additions & 1 deletion drivers/gpu/drm/nouveau/include/nvif/outp.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#ifndef __NVIF_OUTP_H__
#define __NVIF_OUTP_H__
#include <nvif/object.h>
#include <nvif/if0012.h>
struct nvif_disp;

struct nvif_outp {
Expand All @@ -17,8 +18,10 @@ int nvif_outp_ctor(struct nvif_disp *, const char *name, int id, struct nvif_out
void nvif_outp_dtor(struct nvif_outp *);
int nvif_outp_load_detect(struct nvif_outp *, u32 loadval);
int nvif_outp_acquire_rgb_crt(struct nvif_outp *);
int nvif_outp_acquire_tmds(struct nvif_outp *, bool hda);
int nvif_outp_acquire_tmds(struct nvif_outp *, int head,
bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda);
int nvif_outp_acquire_lvds(struct nvif_outp *, bool dual, bool bpc8);
int nvif_outp_acquire_dp(struct nvif_outp *, bool hda);
void nvif_outp_release(struct nvif_outp *);
int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size);
#endif
28 changes: 24 additions & 4 deletions drivers/gpu/drm/nouveau/nvif/outp.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,18 @@
#include <nvif/printf.h>

#include <nvif/class.h>
#include <nvif/if0012.h>

int
nvif_outp_infoframe(struct nvif_outp *outp, u8 type, struct nvif_outp_infoframe_v0 *args, u32 size)
{
int ret;

args->type = type;

ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_INFOFRAME, args, sizeof(*args) + size);
NVIF_ERRON(ret, &outp->object, "[INFOFRAME type:%d size:%d]", type, size);
return ret;
}

void
nvif_outp_release(struct nvif_outp *outp)
Expand Down Expand Up @@ -82,16 +93,25 @@ nvif_outp_acquire_lvds(struct nvif_outp *outp, bool dual, bool bpc8)
}

int
nvif_outp_acquire_tmds(struct nvif_outp *outp, bool hda)
nvif_outp_acquire_tmds(struct nvif_outp *outp, int head,
bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda)
{
struct nvif_outp_acquire_v0 args;
int ret;

args.tmds.hda = hda;
args.tmds.head = head;
args.tmds.hdmi = hdmi;
args.tmds.hdmi_max_ac_packet = max_ac_packet;
args.tmds.hdmi_rekey = rekey;
args.tmds.hdmi_scdc = scdc;
args.tmds.hdmi_hda = hda;

ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_TMDS, &args);
NVIF_ERRON(ret, &outp->object,
"[ACQUIRE proto:TMDS hda:%d] or:%d link:%d", args.tmds.hda, args.or, args.link);
"[ACQUIRE proto:TMDS head:%d hdmi:%d max_ac_packet:%d rekey:%d scdc:%d hda:%d]"
" or:%d link:%d", args.tmds.head, args.tmds.hdmi, args.tmds.hdmi_max_ac_packet,
args.tmds.hdmi_rekey, args.tmds.hdmi_scdc, args.tmds.hdmi_hda,
args.or, args.link);
return ret;
}

Expand Down
Loading

0 comments on commit f530bc6

Please sign in to comment.