Alastair Bridgewater
2017-Jan-17 22:41 UTC
[Nouveau] [PATCH 0/6] drm/nouveau: Enable HDMI Stereoscopy
This is an initial implementation of HDMI 3D mode support for the nouveau kernel driver. It works on all of the hardware that I have available to test at the moment, but I am unsure as to the overall approach taken for setting HDMI InfoFrames, there's no support for g84 or gf119 disps, and the criteria for enabling stereo support for an output seems a bit iffy. The first four patches arrange to set the HDMI InfoFrames for gt215 and gk104 disps, and provide the parsing side of support for g84 and gf119 disps. The fifth patch removes code that sets up the "mandatory" 3D modes. The requirement is that a display that supports 3D support at least one of these modes, not that it must support all of them. The sixth patch enables stereo support on all TMDS outputs, and adds a term (copied from the i915 driver) to adjust the clock required for frame-packed stereo modes. The criteria used here for enabling stereo on an output seems wrong to me, but it's the least wrong thing that I've been able to come up with so far. Even just taking the InfoFrame patches and leaving the stereoscopy patches until the rest of the InfoFrame support is worked out would probably be a win. Alastair Bridgewater (6): drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to NVKM drm/nouveau: Use supplied HDMI InfoFrames on GT215 hardware drm/nouveau: Use supplied HDMI InfoFrames on GK104 hardware drm: Delete "mandatory" stereographic modes drm/nouveau: Enable stereoscopic 3D output over HDMI drivers/gpu/drm/drm_edid.c | 66 -------------------- drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 16 ++++- drivers/gpu/drm/nouveau/nouveau_connector.c | 13 ++++ drivers/gpu/drm/nouveau/nv50_display.c | 49 ++++++++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 32 +++++++++- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 32 +++++++++- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 58 +++++++++++++++--- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 70 ++++++++++++++++++---- 8 files changed, 248 insertions(+), 88 deletions(-) -- 2.10.2
Alastair Bridgewater
2017-Jan-17 22:41 UTC
[Nouveau] [PATCH 1/6] drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames
The nouveau driver, in the Linux 3.7 days, used to try and set the AVI InfoFrame based on the selected display mode. These days, it uses a fixed set of InfoFrames. Start to correct that, by providing a mechanism whereby InfoFrame data may be passed to the NVKM functions that do the actual configuration. At this point, only establish the new parameters and their parsing, don't actually use the data anywhere yet (since it's not supplied anywhere). Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 16 ++++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 32 +++++++++++++++++++++- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 32 +++++++++++++++++++++- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 32 +++++++++++++++++++++- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 32 +++++++++++++++++++++- 5 files changed, 139 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h index ae49dfd..a3ce3bf 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h @@ -76,7 +76,21 @@ struct nv50_disp_sor_hdmi_pwr_v0 { __u8 state; __u8 max_ac_packet; __u8 rekey; - __u8 pad04[4]; + __u8 flags; +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME 0x01 +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME 0x02 +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME 0x04 + __u8 pad05[3]; +}; + +struct nv50_disp_sor_hdmi_pwr_v0_infoframe { + __u8 version; + __u8 pad01[3]; + __u32 header; + __u32 subpack0_low; + __u32 subpack0_high; + __u32 subpack1_low; + __u32 subpack1_high; }; struct nv50_disp_sor_lvds_script_v0 { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c index 1c4256e..f767588 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c @@ -36,11 +36,14 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; u32 ctrl; int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -54,6 +57,33 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { + audio_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *audio_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { + avi_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *avi_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { + vendor_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *vendor_infoframe, 0, 0, true))) + return ret; + } + + if (size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c index 632f02d..c492cd7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c @@ -36,11 +36,14 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; u32 ctrl; int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -53,6 +56,33 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { + audio_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *audio_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { + avi_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *avi_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { + vendor_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *vendor_infoframe, 0, 0, true))) + return ret; + } + + if (size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 4e8067d..6c38d6d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c @@ -37,11 +37,14 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; u32 ctrl; int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -54,6 +57,33 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { + audio_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *audio_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { + avi_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *avi_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { + vendor_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *vendor_infoframe, 0, 0, true))) + return ret; + } + + if (size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index f1afc16..e2fbe4c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -37,11 +37,14 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; u32 ctrl; int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -55,6 +58,33 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { + audio_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *audio_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { + avi_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *avi_infoframe, 0, 0, true))) + return ret; + } + + if (args->v0.flags & + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { + vendor_infoframe = data; + if ((ret = nvif_unpack(-ENOSYS, &data, &size, + *vendor_infoframe, 0, 0, true))) + return ret; + } + + if (size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Jan-17 22:42 UTC
[Nouveau] [PATCH 2/6] drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to NVKM
Now that we have mechanism by which to pass mode-dependent HDMI InfoFrames to the low-level hardware driver, it is incumbent upon us to do so. Experimentation on a gt215 device suggests that the Audio InfoFrame is not required here, possibly being provided by the HDA device when necessary (because where else would it come from?). Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nv50_display.c | 49 +++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 2c2c645..d52d0b8 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -23,6 +23,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -31,6 +32,7 @@ #include <drm/drm_dp_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_edid.h> #include <nvif/class.h> #include <nvif/cl0002.h> @@ -2772,6 +2774,28 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) nvif_mthd(disp->disp, 0, &args, sizeof(args)); } +static ssize_t +nv50_hdmi_pack_infoframe(struct nv50_disp_sor_hdmi_pwr_v0_infoframe *frame_out, + union hdmi_infoframe *frame_in) +{ + uint8_t buffer[17]; /* The header plus two "subpacks" */ + ssize_t len; + + len = hdmi_infoframe_pack(frame_in, buffer, sizeof(buffer)); + + frame_out->header = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16); + frame_out->subpack0_low = buffer[3] | (buffer[4] << 8) | + (buffer[5] << 16) | (buffer[6] << 24); + frame_out->subpack0_high = buffer[7] | (buffer[8] << 8) | + (buffer[9] << 16); + frame_out->subpack1_low = buffer[10] | (buffer[11] << 8) | + (buffer[12] << 16) | (buffer[13] << 24); + frame_out->subpack1_high = buffer[14] | (buffer[15] << 8) | + (buffer[16] << 16); + + return len; +} + static void nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) { @@ -2781,6 +2805,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) struct { struct nv50_disp_mthd_v1 base; struct nv50_disp_sor_hdmi_pwr_v0 pwr; + struct nv50_disp_sor_hdmi_pwr_v0_infoframe iframe[3]; } args = { .base.version = 1, .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, @@ -2792,17 +2817,39 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) }; struct nouveau_connector *nv_connector; u32 max_ac_packet; + union hdmi_infoframe avi_frame; + union hdmi_infoframe vendor_frame; + int ret; + int size; + int frame = 0; nv_connector = nouveau_encoder_connector_get(nv_encoder); if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; + /* Audio InfoFrame apparently not required (supplied by HDA device?) */ + + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode); + if (ret >= 0) { + /* We have an AVI InfoFrame, populate it to the display */ + args.pwr.flags |= NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME; + nv50_hdmi_pack_infoframe(&args.iframe[frame++], &avi_frame); + } + + ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, mode); + if (ret >= 0) { + /* We have a Vendor InfoFrame, populate it to the display */ + args.pwr.flags |= NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME; + nv50_hdmi_pack_infoframe(&args.iframe[frame++], &vendor_frame); + } + max_ac_packet = mode->htotal - mode->hdisplay; max_ac_packet -= args.pwr.rekey; max_ac_packet -= 18; /* constant from tegra */ args.pwr.max_ac_packet = max_ac_packet / 32; - nvif_mthd(disp->disp, 0, &args, sizeof(args)); + size = sizeof(args.base) + sizeof(args.pwr) + frame * sizeof(args.iframe[0]); + nvif_mthd(disp->disp, 0, &args, size); nv50_audio_enable(encoder, mode); } -- 2.10.2
Alastair Bridgewater
2017-Jan-17 22:42 UTC
[Nouveau] [PATCH 3/6] drm/nouveau: Use supplied HDMI InfoFrames on GT215 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic") InfoFrame. Also don't enable any InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. This change should have two net effects: The Audio infoframe will no longer be configured (because it's not being supplied, not that it appears to be necessary), and the AVI and Vendor InfoFrames will start being emitted correctly for the selected video mode... For GT215 hardware only. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 38 ++++++++++++++++------ 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index e2fbe4c..fc8e1e4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -87,6 +87,7 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); return 0; @@ -94,19 +95,36 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61c528 + soff, 0x000d0282); - nvkm_wr32(device, 0x61c52c + soff, 0x0000006f); - nvkm_wr32(device, 0x61c530 + soff, 0x00000000); - nvkm_wr32(device, 0x61c534 + soff, 0x00000000); - nvkm_wr32(device, 0x61c538 + soff, 0x00000000); - nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + if (avi_infoframe) { + nvkm_wr32(device, 0x61c528 + soff, avi_infoframe->header); + nvkm_wr32(device, 0x61c52c + soff, avi_infoframe->subpack0_low); + nvkm_wr32(device, 0x61c530 + soff, avi_infoframe->subpack0_high); + nvkm_wr32(device, 0x61c534 + soff, avi_infoframe->subpack1_low); + nvkm_wr32(device, 0x61c538 + soff, avi_infoframe->subpack1_high); + nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + } /* Audio InfoFrame */ nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61c508 + soff, 0x000a0184); - nvkm_wr32(device, 0x61c50c + soff, 0x00000071); - nvkm_wr32(device, 0x61c510 + soff, 0x00000000); - nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); + if (audio_infoframe) { + nvkm_wr32(device, 0x61c508 + soff, audio_infoframe->header); + nvkm_wr32(device, 0x61c50c + soff, audio_infoframe->subpack0_low); + nvkm_wr32(device, 0x61c510 + soff, audio_infoframe->subpack0_high); + /* Audio InfoFrame supposedly has only one subpack. */ + nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); + } + + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000); + if (vendor_infoframe) { + nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe->header); + nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe->subpack0_low); + nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe->subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe->subpack1_low); */ + /* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe->subpack1_high); */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001); + } nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ -- 2.10.2
Alastair Bridgewater
2017-Jan-17 22:42 UTC
[Nouveau] [PATCH 4/6] drm/nouveau: Use supplied HDMI InfoFrames on GK104 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic"?) InfoFrame. Also don't enable any InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Ignore the Audio InfoFrame: We don't supply it, and there is no indication that the hardware supports it through this interface. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 26 +++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 6c38d6d..64b21a4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c @@ -86,6 +86,7 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); return 0; @@ -93,12 +94,25 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x690008 + hdmi, 0x000d0282); - nvkm_wr32(device, 0x69000c + hdmi, 0x0000006f); - nvkm_wr32(device, 0x690010 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690014 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690018 + hdmi, 0x00000000); - nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + if (avi_infoframe) { + nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe->header); + nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe->subpack0_low); + nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe->subpack0_high); + nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe->subpack1_low); + nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe->subpack1_high); + nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000); + if (vendor_infoframe) { + nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe->header); + nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe->subpack0_low); + nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe->subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001); + } + /* ??? InfoFrame? */ nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Jan-17 22:42 UTC
[Nouveau] [PATCH 5/6] drm: Delete "mandatory" stereographic modes
HDMI specification 1.4a, table 8-15 is very explicitly a "must support at least one of" table, not a "must support all of" table. It is not hard to find hardware that does not support some of the so-called "mandatory" modes. More seriously, this code generates invalid display modes for both of the 3D-capable panels that I have (a 42-inch LG TV and a Sony PlayStation 3D Display). If we want to be persnickety, one option would be to check the final list of modes against the table and give some message if none of them are valid, but it's a whole lot easier just to delete the code in question. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/drm_edid.c | 66 ---------------------------------------------- 1 file changed, 66 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 336be31..723116a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2926,70 +2926,6 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) return modes; } -struct stereo_mandatory_mode { - int width, height, vrefresh; - unsigned int flags; -}; - -static const struct stereo_mandatory_mode stereo_mandatory_modes[] = { - { 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, - { 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING }, - { 1920, 1080, 50, - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, - { 1920, 1080, 60, - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, - { 1280, 720, 50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, - { 1280, 720, 50, DRM_MODE_FLAG_3D_FRAME_PACKING }, - { 1280, 720, 60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, - { 1280, 720, 60, DRM_MODE_FLAG_3D_FRAME_PACKING } -}; - -static bool -stereo_match_mandatory(const struct drm_display_mode *mode, - const struct stereo_mandatory_mode *stereo_mode) -{ - unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; - - return mode->hdisplay == stereo_mode->width && - mode->vdisplay == stereo_mode->height && - interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) && - drm_mode_vrefresh(mode) == stereo_mode->vrefresh; -} - -static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector) -{ - struct drm_device *dev = connector->dev; - const struct drm_display_mode *mode; - struct list_head stereo_modes; - int modes = 0, i; - - INIT_LIST_HEAD(&stereo_modes); - - list_for_each_entry(mode, &connector->probed_modes, head) { - for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) { - const struct stereo_mandatory_mode *mandatory; - struct drm_display_mode *new_mode; - - if (!stereo_match_mandatory(mode, - &stereo_mandatory_modes[i])) - continue; - - mandatory = &stereo_mandatory_modes[i]; - new_mode = drm_mode_duplicate(dev, mode); - if (!new_mode) - continue; - - new_mode->flags |= mandatory->flags; - list_add_tail(&new_mode->head, &stereo_modes); - modes++; - } - } - - list_splice_tail(&stereo_modes, &connector->probed_modes); - - return modes; -} - static int add_hdmi_mode(struct drm_connector *connector, u8 vic) { struct drm_device *dev = connector->dev; @@ -3090,8 +3026,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, /* 3D_Present */ offset++; if (db[8 + offset] & (1 << 7)) { - modes += add_hdmi_mandatory_stereo_modes(connector); - /* 3D_Multi_present */ multi_present = (db[8 + offset] & 0x60) >> 5; } -- 2.10.2
Alastair Bridgewater
2017-Jan-17 22:42 UTC
[Nouveau] [PATCH 6/6] drm/nouveau: Enable stereoscopic 3D output over HDMI
This is a bit sketchy in terms of implementation, with some rough edges, but for the most part IT WORKS. That is to say, I get an obvious 3D output when using the "testdisplay" program from intel-gpu-tools with the "-3" parameter and outputting to a 3D-capable HDMI display. Rough edges include: the criteria for when to enable 3D mode selection, and the inconsistent support for setting InfoFrames on HDMI outputs. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_connector.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 947c200..11b4977 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -547,6 +547,16 @@ nouveau_connector_set_encoder(struct drm_connector *connector, DRM_MODE_SUBCONNECTOR_DVID : DRM_MODE_SUBCONNECTOR_DVIA); } + + if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS) { + /* Can we just ask for the drm connector type? */ + /* + * FIXME: Does this also kick in for DVI or DP->DVI + * connectors? It shouldn't. + */ + /* FIXME: Should only allow when we can set InfoFrames */ + connector->stereo_allowed = true; + } } static enum drm_connector_status @@ -1044,6 +1054,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; } + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + if (clock < min_clock) return MODE_CLOCK_LOW; -- 2.10.2
Ilia Mirkin
2017-Jan-18 03:19 UTC
[Nouveau] [PATCH 1/6] drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames
On Tue, Jan 17, 2017 at 5:41 PM, Alastair Bridgewater <alastair.bridgewater at gmail.com> wrote:> The nouveau driver, in the Linux 3.7 days, used to try and set the > AVI InfoFrame based on the selected display mode. These days, it > uses a fixed set of InfoFrames. Start to correct that, by > providing a mechanism whereby InfoFrame data may be passed to the > NVKM functions that do the actual configuration. > > At this point, only establish the new parameters and their parsing, > don't actually use the data anywhere yet (since it's not supplied > anywhere). > > Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> > --- > drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 16 ++++++++++- > drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 32 +++++++++++++++++++++- > .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 32 +++++++++++++++++++++- > .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 32 +++++++++++++++++++++- > .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 32 +++++++++++++++++++++- > 5 files changed, 139 insertions(+), 5 deletions(-) > > diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h > index ae49dfd..a3ce3bf 100644 > --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h > +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h > @@ -76,7 +76,21 @@ struct nv50_disp_sor_hdmi_pwr_v0 { > __u8 state; > __u8 max_ac_packet; > __u8 rekey; > - __u8 pad04[4]; > + __u8 flags; > +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME 0x01 > +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME 0x02 > +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME 0x04These should come before flags, not after (based on the other ones). Also, this is nv50_disp_sor_hdmi_pwr_v0, so they should be NV50_DISP_SOR_HDMI_PWR_V0_FLAG_AUDIO_INFOFRAME and so on.> + __u8 pad05[3]; > +}; > + > +struct nv50_disp_sor_hdmi_pwr_v0_infoframe { > + __u8 version;Why do you need a version here? Do you anticipate mixing and matching, e.g. calling the sor_hdmi_pwr_v0 method with v0 or v1 infoframes attached, and needing to tell them apart? I'd just as soon drop it and not use nvif_unpack for these.> + __u8 pad01[3]; > + __u32 header; > + __u32 subpack0_low; > + __u32 subpack0_high; > + __u32 subpack1_low; > + __u32 subpack1_high;Is this nomenclature from the spec? I've never seen it... (not that I'm some spec expert). Why not make it a __u8 buffer[20]?> }; > > struct nv50_disp_sor_lvds_script_v0 { > diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c > index 1c4256e..f767588 100644 > --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c > +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c > @@ -36,11 +36,14 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) > union { > struct nv50_disp_sor_hdmi_pwr_v0 v0; > } *args = data; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; > u32 ctrl; > int ret = -ENOSYS; > > nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); > - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { > + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { > nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " > "max_ac_packet %d rekey %d\n", > args->v0.version, args->v0.state, > @@ -54,6 +57,33 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) > } else > return ret; > > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { > + audio_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *audio_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { > + avi_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *avi_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { > + vendor_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *vendor_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (size) > + return -E2BIG; > + > if (!(ctrl & 0x40000000)) { > nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); > nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); > diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c > index 632f02d..c492cd7 100644 > --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c > +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c > @@ -36,11 +36,14 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) > union { > struct nv50_disp_sor_hdmi_pwr_v0 v0; > } *args = data; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; > u32 ctrl; > int ret = -ENOSYS; > > nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); > - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { > + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { > nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " > "max_ac_packet %d rekey %d\n", > args->v0.version, args->v0.state, > @@ -53,6 +56,33 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) > } else > return ret; > > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { > + audio_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *audio_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { > + avi_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *avi_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { > + vendor_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *vendor_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (size) > + return -E2BIG; > + > if (!(ctrl & 0x40000000)) { > nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); > nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); > diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c > index 4e8067d..6c38d6d 100644 > --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c > +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c > @@ -37,11 +37,14 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) > union { > struct nv50_disp_sor_hdmi_pwr_v0 v0; > } *args = data; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; > u32 ctrl; > int ret = -ENOSYS; > > nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); > - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { > + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { > nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " > "max_ac_packet %d rekey %d\n", > args->v0.version, args->v0.state, > @@ -54,6 +57,33 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) > } else > return ret; > > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { > + audio_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *audio_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { > + avi_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *avi_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { > + vendor_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *vendor_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (size) > + return -E2BIG; > + > if (!(ctrl & 0x40000000)) { > nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); > nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); > diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c > index f1afc16..e2fbe4c 100644 > --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c > +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c > @@ -37,11 +37,14 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) > union { > struct nv50_disp_sor_hdmi_pwr_v0 v0; > } *args = data; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *audio_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *avi_infoframe = NULL; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe *vendor_infoframe = NULL; > u32 ctrl; > int ret = -ENOSYS; > > nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); > - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { > + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { > nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " > "max_ac_packet %d rekey %d\n", > args->v0.version, args->v0.state, > @@ -55,6 +58,33 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) > } else > return ret; > > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AUDIO_INFOFRAME) { > + audio_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *audio_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME) { > + avi_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *avi_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (args->v0.flags & > + NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME) { > + vendor_infoframe = data; > + if ((ret = nvif_unpack(-ENOSYS, &data, &size, > + *vendor_infoframe, 0, 0, true))) > + return ret; > + } > + > + if (size) > + return -E2BIG; > + > if (!(ctrl & 0x40000000)) { > nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); > nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); > -- > 2.10.2 > > _______________________________________________ > Nouveau mailing list > Nouveau at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau
Ilia Mirkin
2017-Jan-18 03:31 UTC
[Nouveau] [PATCH 2/6] drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to NVKM
On Tue, Jan 17, 2017 at 5:42 PM, Alastair Bridgewater <alastair.bridgewater at gmail.com> wrote:> Now that we have mechanism by which to pass mode-dependent HDMI > InfoFrames to the low-level hardware driver, it is incumbent upon > us to do so. > > Experimentation on a gt215 device suggests that the Audio InfoFrame > is not required here, possibly being provided by the HDA device > when necessary (because where else would it come from?).Presumably it's necessary on G84, which doesn't have the HDA device? Looks like there's no helper for computing such a thing in drm_edid. It's a pretty fixed setup on G84... you're supposed to hook the audio from your sound card into an internal S/PDIF connector, so just leaving the default audio infoframe on in there might be enough.> > Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> > --- > drivers/gpu/drm/nouveau/nv50_display.c | 49 +++++++++++++++++++++++++++++++++- > 1 file changed, 48 insertions(+), 1 deletion(-) > > diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c > index 2c2c645..d52d0b8 100644 > --- a/drivers/gpu/drm/nouveau/nv50_display.c > +++ b/drivers/gpu/drm/nouveau/nv50_display.c > @@ -23,6 +23,7 @@ > */ > > #include <linux/dma-mapping.h> > +#include <linux/hdmi.h> > > #include <drm/drmP.h> > #include <drm/drm_atomic.h> > @@ -31,6 +32,7 @@ > #include <drm/drm_dp_helper.h> > #include <drm/drm_fb_helper.h> > #include <drm/drm_plane_helper.h> > +#include <drm/drm_edid.h> > > #include <nvif/class.h> > #include <nvif/cl0002.h> > @@ -2772,6 +2774,28 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) > nvif_mthd(disp->disp, 0, &args, sizeof(args)); > } > > +static ssize_t > +nv50_hdmi_pack_infoframe(struct nv50_disp_sor_hdmi_pwr_v0_infoframe *frame_out, > + union hdmi_infoframe *frame_in) > +{ > + uint8_t buffer[17]; /* The header plus two "subpacks" */ > + ssize_t len; > + > + len = hdmi_infoframe_pack(frame_in, buffer, sizeof(buffer)); > + > + frame_out->header = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16); > + frame_out->subpack0_low = buffer[3] | (buffer[4] << 8) | > + (buffer[5] << 16) | (buffer[6] << 24); > + frame_out->subpack0_high = buffer[7] | (buffer[8] << 8) | > + (buffer[9] << 16); > + frame_out->subpack1_low = buffer[10] | (buffer[11] << 8) | > + (buffer[12] << 16) | (buffer[13] << 24); > + frame_out->subpack1_high = buffer[14] | (buffer[15] << 8) | > + (buffer[16] << 16); > + > + return len; > +} > + > static void > nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) > { > @@ -2781,6 +2805,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) > struct { > struct nv50_disp_mthd_v1 base; > struct nv50_disp_sor_hdmi_pwr_v0 pwr; > + struct nv50_disp_sor_hdmi_pwr_v0_infoframe iframe[3]; > } args = { > .base.version = 1, > .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, > @@ -2792,17 +2817,39 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) > }; > struct nouveau_connector *nv_connector; > u32 max_ac_packet; > + union hdmi_infoframe avi_frame; > + union hdmi_infoframe vendor_frame; > + int ret; > + int size; > + int frame = 0; > > nv_connector = nouveau_encoder_connector_get(nv_encoder); > if (!drm_detect_hdmi_monitor(nv_connector->edid)) > return; > > + /* Audio InfoFrame apparently not required (supplied by HDA device?) */ > + > + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode); > + if (ret >= 0) {if (!ret) or if (ret == 0) is more idiomatic.> + /* We have an AVI InfoFrame, populate it to the display */ > + args.pwr.flags |= NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_AVI_INFOFRAME; > + nv50_hdmi_pack_infoframe(&args.iframe[frame++], &avi_frame); > + } > + > + ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, mode); > + if (ret >= 0) { > + /* We have a Vendor InfoFrame, populate it to the display */ > + args.pwr.flags |= NV50_DISP_MTHD_V1_SOR_HDMI_PWR_FLAG_VENDOR_INFOFRAME; > + nv50_hdmi_pack_infoframe(&args.iframe[frame++], &vendor_frame); > + } > + > max_ac_packet = mode->htotal - mode->hdisplay; > max_ac_packet -= args.pwr.rekey; > max_ac_packet -= 18; /* constant from tegra */ > args.pwr.max_ac_packet = max_ac_packet / 32; > > - nvif_mthd(disp->disp, 0, &args, sizeof(args)); > + size = sizeof(args.base) + sizeof(args.pwr) + frame * sizeof(args.iframe[0]); > + nvif_mthd(disp->disp, 0, &args, size); > nv50_audio_enable(encoder, mode); > } > > -- > 2.10.2 > > _______________________________________________ > Nouveau mailing list > Nouveau at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau
Ilia Mirkin
2017-Jan-18 05:10 UTC
[Nouveau] [PATCH 5/6] drm: Delete "mandatory" stereographic modes
On Tue, Jan 17, 2017 at 5:42 PM, Alastair Bridgewater <alastair.bridgewater at gmail.com> wrote:> HDMI specification 1.4a, table 8-15 is very explicitly a "must > support at least one of" table, not a "must support all of" table. > It is not hard to find hardware that does not support some of the > so-called "mandatory" modes. > > More seriously, this code generates invalid display modes for both > of the 3D-capable panels that I have (a 42-inch LG TV and a Sony > PlayStation 3D Display). > > If we want to be persnickety, one option would be to check the > final list of modes against the table and give some message if > none of them are valid, but it's a whole lot easier just to delete > the code in question.Damien added this in commit c858cfcae6d some 3 years ago. Damien, do you remember why you added these "required" modes? Did you have a monitor that only advertised 3D support without the actual modes?> > Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> > --- > drivers/gpu/drm/drm_edid.c | 66 ---------------------------------------------- > 1 file changed, 66 deletions(-) > > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c > index 336be31..723116a 100644 > --- a/drivers/gpu/drm/drm_edid.c > +++ b/drivers/gpu/drm/drm_edid.c > @@ -2926,70 +2926,6 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) > return modes; > } > > -struct stereo_mandatory_mode { > - int width, height, vrefresh; > - unsigned int flags; > -}; > - > -static const struct stereo_mandatory_mode stereo_mandatory_modes[] = { > - { 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, > - { 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING }, > - { 1920, 1080, 50, > - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, > - { 1920, 1080, 60, > - DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, > - { 1280, 720, 50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, > - { 1280, 720, 50, DRM_MODE_FLAG_3D_FRAME_PACKING }, > - { 1280, 720, 60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, > - { 1280, 720, 60, DRM_MODE_FLAG_3D_FRAME_PACKING } > -}; > - > -static bool > -stereo_match_mandatory(const struct drm_display_mode *mode, > - const struct stereo_mandatory_mode *stereo_mode) > -{ > - unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; > - > - return mode->hdisplay == stereo_mode->width && > - mode->vdisplay == stereo_mode->height && > - interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) && > - drm_mode_vrefresh(mode) == stereo_mode->vrefresh; > -} > - > -static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector) > -{ > - struct drm_device *dev = connector->dev; > - const struct drm_display_mode *mode; > - struct list_head stereo_modes; > - int modes = 0, i; > - > - INIT_LIST_HEAD(&stereo_modes); > - > - list_for_each_entry(mode, &connector->probed_modes, head) { > - for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) { > - const struct stereo_mandatory_mode *mandatory; > - struct drm_display_mode *new_mode; > - > - if (!stereo_match_mandatory(mode, > - &stereo_mandatory_modes[i])) > - continue; > - > - mandatory = &stereo_mandatory_modes[i]; > - new_mode = drm_mode_duplicate(dev, mode); > - if (!new_mode) > - continue; > - > - new_mode->flags |= mandatory->flags; > - list_add_tail(&new_mode->head, &stereo_modes); > - modes++; > - } > - } > - > - list_splice_tail(&stereo_modes, &connector->probed_modes); > - > - return modes; > -} > - > static int add_hdmi_mode(struct drm_connector *connector, u8 vic) > { > struct drm_device *dev = connector->dev; > @@ -3090,8 +3026,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, > /* 3D_Present */ > offset++; > if (db[8 + offset] & (1 << 7)) { > - modes += add_hdmi_mandatory_stereo_modes(connector); > - > /* 3D_Multi_present */ > multi_present = (db[8 + offset] & 0x60) >> 5; > } > -- > 2.10.2 > > _______________________________________________ > Nouveau mailing list > Nouveau at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau
Alastair Bridgewater
2017-Mar-27 21:57 UTC
[Nouveau] [PATCH v2 00/10] Enable HDMI Stereoscopy
HDMI 3D mode support, round two. Revisions include no longer dealing with audio InfoFrames, passing infoframe data to NVKM as bags of bytes rather than as data pre-packed for the hardware, more-normal return value checking for drm_hdmi_*_infoframe_from_display_mode() results, Frame-Packing mode support, more-principled logic for enabling stereo mode support on a connector, and support for all four nv50+ DISPs. Additionally, this patch set is being sent to the dri-devel list as well as the nouveau list. Dropped in this version is the removal of the "mandatory" 3D mode logic. After discussion, I'm still convinced that it's wrong, but now I'm convinced that it's too conservative rather than my original belief which was that it was too liberal. Thanks to Ilia Mirkin, Damien Lespiau, and Ben Skeggs for feedback on the original patch set. If I have neglected to mention anyone, mea culpa. The first patch perhaps isn't technically necessary, but simplifies the later logic for fixing frame-packing mode timing. There may be some effect from applying this, as drm_mode_set_crtcinfo() does something with vscan which can affect the given CRTC timing. I'm not sure what's going on there, if there's any behavioral change or not, and if there is what to do about it (other than using drm_mode_set_crtcinfo() twice, once with CRTC_NO_VSCAN and once without). The second through eighth patches are all the InfoFrame logic again, this time with support for all four DISP types. This does not appear to cause regressions in HDMI audio on GT215, GF119, or GK104. I hope that it doesn't cause a regression on G84, but haven't yet managed to set up a test case for G84 and HDMI audio. The ninth patch is to fix up frame-packing mode timings and geometry. As the patch comment says, there are clearly-correct parts, and there are possibly-hacky parts. But it gets frame-packing modes working, and we can revise from there if necessary. And the tenth patch enables stereo mode support... on HDMI and DPort connectors on nv50+ hardware. HDMI connectors because obvious. DPort connectors because of DPort to HDMI adaptors. eDP connectors because it shouldn't do any harm, and someone might have been maniac enough to set up an eDP port to point to something with an HDMI 3D setup. And nv50+ hardware only because while I'm not aware of any pre-nv50 cards that have HDMI outputs, there could be a setup out there like the PS3 that uses an external HDMI encoder on pre-nv50 hardware, at which point none of the mode timing or InfoFrame support is in place for it. Alastair Bridgewater (10): drm/nouveau: Use drm_mode_set_crtcinfo() to compensate for vscan / ilace drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to NVKM drm/nouveau: Add mechanism to convert HDMI InfoFrames to hardware format drm/nouveau: Use supplied HDMI InfoFrames on G84 hardware drm/nouveau: Use supplied HDMI InfoFrames on GT215 hardware drm/nouveau: Use supplied HDMI InfoFrames on GF119 hardware drm/nouveau: Use supplied HDMI InfoFrames on GK104 hardware drm/nouveau: Handle frame-packing mode geometry and timing effects drm/nouveau: Enable stereoscopic 3D output over HDMI drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 4 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 10 +++ drivers/gpu/drm/nouveau/nv50_display.c | 80 ++++++++++++++++------ drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 1 + .../drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c | 66 ++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 46 +++++++++++-- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 49 +++++++++++-- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 45 ++++++++++-- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 46 +++++++++++-- drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h | 11 +++ 10 files changed, 307 insertions(+), 51 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:57 UTC
[Nouveau] [PATCH v2 01/10] drm/nouveau: Use drm_mode_set_crtcinfo() to compensate for vscan / ilace
* drm_mode_set_crtcinfo() does compensation for interlace and doublescan timing effects already, so do it first and use the compensated figures instead of the constant "vscan / ilace" terms that we had before. * Also prefer using the mode->crtc_* fields generally instead of the non-crtc_-prefixed versions, largely to avoid potential confusion as to why some fields are the crtc_ versions and some are not, but also in case drm_mode_set_crtcinfo() adds some more mode-specific logic that we might want to use. * In one case, we use the non-crtc_-prefixed version of a mode field, because the original form didn't include a "vscan / ilace" term. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nv50_display.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 32097fd..1acbb5a 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -2032,32 +2032,34 @@ static void nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) { struct drm_display_mode *mode = &asyh->state.adjusted_mode; - u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; - u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; - u32 hbackp = mode->htotal - mode->hsync_end; - u32 vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; - u32 hfrontp = mode->hsync_start - mode->hdisplay; - u32 vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; struct nv50_head_mode *m = &asyh->mode; + u32 hbackp, vbackp, hfrontp, vfrontp; - m->h.active = mode->htotal; - m->h.synce = mode->hsync_end - mode->hsync_start - 1; + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + + hbackp = mode->crtc_htotal - mode->crtc_hsync_end; + vbackp = mode->crtc_vtotal - mode->crtc_vsync_end; + hfrontp = mode->crtc_hsync_start - mode->crtc_hdisplay; + vfrontp = mode->crtc_vsync_start - mode->crtc_vdisplay; + + m->h.active = mode->crtc_htotal; + m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1; m->h.blanke = m->h.synce + hbackp; - m->h.blanks = mode->htotal - hfrontp - 1; + m->h.blanks = mode->crtc_htotal - hfrontp - 1; - m->v.active = mode->vtotal * vscan / ilace; - m->v.synce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; + m->v.active = mode->crtc_vtotal; + m->v.synce = (mode->crtc_vsync_end - mode->crtc_vsync_start) - 1; m->v.blanke = m->v.synce + vbackp; m->v.blanks = m->v.active - vfrontp - 1; /*XXX: Safe underestimate, even "0" works */ m->v.blankus = (m->v.active - mode->vdisplay - 2) * m->h.active; m->v.blankus *= 1000; - m->v.blankus /= mode->clock; + m->v.blankus /= mode->crtc_clock; if (mode->flags & DRM_MODE_FLAG_INTERLACE) { m->v.blank2e = m->v.active + m->v.synce + vbackp; - m->v.blank2s = m->v.blank2e + (mode->vdisplay * vscan / ilace); + m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay; m->v.active = (m->v.active * 2) + 1; m->interlace = true; } else { @@ -2065,9 +2067,8 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) m->v.blank2s = 1; m->interlace = false; } - m->clock = mode->clock; + m->clock = mode->crtc_clock; - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); asyh->set.mode = true; } -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:57 UTC
[Nouveau] [PATCH v2 02/10] drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames
The nouveau driver, in the Linux 3.7 days, used to try and set the AVI InfoFrame based on the selected display mode. These days, it uses a fixed set of InfoFrames. Start to correct that, by providing a mechanism whereby InfoFrame data may be passed to the NVKM functions that do the actual configuration. At this point, only establish the new parameters and their parsing, don't actually use the data anywhere yet (since it's not supplied anywhere). Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 4 +++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 9 ++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 9 ++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 9 ++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 9 ++++++++- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h index ae49dfd..9d46eba 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h @@ -76,7 +76,9 @@ struct nv50_disp_sor_hdmi_pwr_v0 { __u8 state; __u8 max_ac_packet; __u8 rekey; - __u8 pad04[4]; + __u8 avi_infoframe_length; + __u8 vendor_infoframe_length; + __u8 pad06[2]; }; struct nv50_disp_sor_lvds_script_v0 { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c index 1c4256e..77e5f5a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c @@ -40,7 +40,7 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -54,6 +54,13 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c index 632f02d..66ee883 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c @@ -40,7 +40,7 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -53,6 +53,13 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 4e8067d..3c8c26a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c @@ -41,7 +41,7 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -54,6 +54,13 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index f1afc16..8ed00db 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -41,7 +41,7 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -55,6 +55,13 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 03/10] drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to NVKM
Now that we have mechanism by which to pass mode-dependent HDMI InfoFrames to the low-level hardware driver, it is incumbent upon us to do so. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nv50_display.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 1acbb5a..008aea6 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -23,6 +23,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -31,6 +32,7 @@ #include <drm/drm_dp_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_edid.h> #include <nvif/class.h> #include <nvif/cl0002.h> @@ -2782,6 +2784,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) 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, @@ -2793,17 +2796,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) }; struct nouveau_connector *nv_connector; u32 max_ac_packet; + union hdmi_infoframe avi_frame; + union hdmi_infoframe vendor_frame; + int ret; + int size; nv_connector = nouveau_encoder_connector_get(nv_encoder); if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode); + if (!ret) { + /* 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, 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); + } + max_ac_packet = mode->htotal - mode->hdisplay; max_ac_packet -= args.pwr.rekey; max_ac_packet -= 18; /* constant from tegra */ args.pwr.max_ac_packet = max_ac_packet / 32; - nvif_mthd(disp->disp, 0, &args, sizeof(args)); + size = sizeof(args.base) + + sizeof(args.pwr) + + args.pwr.avi_infoframe_length + + args.pwr.vendor_infoframe_length; + nvif_mthd(disp->disp, 0, &args, size); nv50_audio_enable(encoder, mode); } -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 04/10] drm/nouveau: Add mechanism to convert HDMI InfoFrames to hardware format
HDMI InfoFrames are passed to NVKM as bags of bytes, but the hardware needs them to be packed into words. Rather than having four (or more) copies of the packing logic introduce a single copy now, in a central place. We currently need these for AVI and Vendor InfoFrames, but we may also expect to need them for Audio InfoFrames at some point. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 1 + .../drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c | 66 ++++++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h | 11 ++++ 3 files changed, 78 insertions(+) create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index fa05d16..65ae870 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -29,6 +29,7 @@ nvkm-y += nvkm/engine/disp/conn.o nvkm-y += nvkm/engine/disp/hdagt215.o nvkm-y += nvkm/engine/disp/hdagf119.o +nvkm-y += nvkm/engine/disp/hdmi_infoframe.o nvkm-y += nvkm/engine/disp/hdmig84.o nvkm-y += nvkm/engine/disp/hdmigt215.o nvkm-y += nvkm/engine/disp/hdmigf119.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c new file mode 100644 index 0000000..e04f2e8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c @@ -0,0 +1,66 @@ +#include "nv50.h" + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len) +{ + u32 header = 0; + u32 subpack0_low = 0; + u32 subpack0_high = 0; + u32 subpack1_low = 0; + u32 subpack1_high = 0; + + switch (len) { + /* + * "When in doubt, use brute force." + * -- Ken Thompson. + */ + default: + /* + * We presume that no valid frame is longer than 17 + * octets, including header... And truncate to that + * if it's longer. + */ + case 17: + subpack1_high = (raw_frame[16] << 16); + case 16: + subpack1_high |= (raw_frame[15] << 8); + case 15: + subpack1_high |= raw_frame[14]; + case 14: + subpack1_low = (raw_frame[13] << 24); + case 13: + subpack1_low |= (raw_frame[12] << 16); + case 12: + subpack1_low |= (raw_frame[11] << 8); + case 11: + subpack1_low |= raw_frame[10]; + case 10: + subpack0_high = (raw_frame[9] << 16); + case 9: + subpack0_high |= (raw_frame[8] << 8); + case 8: + subpack0_high |= raw_frame[7]; + case 7: + subpack0_low = (raw_frame[6] << 24); + case 6: + subpack0_low |= (raw_frame[5] << 16); + case 5: + subpack0_low |= (raw_frame[4] << 8); + case 4: + subpack0_low |= raw_frame[3]; + case 3: + header = (raw_frame[2] << 16); + case 2: + header |= (raw_frame[1] << 8); + case 1: + header |= raw_frame[0]; + case 0: + break; + } + + packed_frame->header = header; + packed_frame->subpack0_low = subpack0_low; + packed_frame->subpack0_high = subpack0_high; + packed_frame->subpack1_low = subpack1_low; + packed_frame->subpack1_high = subpack1_high; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h index 1e1de6b..37ec2a1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h @@ -40,6 +40,17 @@ int nv50_dac_sense(NV50_DISP_MTHD_V1); int gt215_hda_eld(NV50_DISP_MTHD_V1); int gf119_hda_eld(NV50_DISP_MTHD_V1); +struct packed_hdmi_infoframe { + u32 header; + u32 subpack0_low; + u32 subpack0_high; + u32 subpack1_low; + u32 subpack1_high; +}; + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len); + int g84_hdmi_ctrl(NV50_DISP_MTHD_V1); int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1); int gf119_hdmi_ctrl(NV50_DISP_MTHD_V1); -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 05/10] drm/nouveau: Use supplied HDMI InfoFrames on G84 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic"?) InfoFrame. Also don't enable any AVI or Vendor InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Ignore the Audio InfoFrame: We don't supply it, and altering HDMI audio semantics (for better or worse) on this hardware is out of scope for me at this time. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 37 ++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c index 77e5f5a..139344e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c @@ -36,6 +36,8 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -61,8 +63,17 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61653c + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); return 0; @@ -70,12 +81,14 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x616528 + hoff, 0x000d0282); - nvkm_wr32(device, 0x61652c + hoff, 0x0000006f); - nvkm_wr32(device, 0x616530 + hoff, 0x00000000); - nvkm_wr32(device, 0x616534 + hoff, 0x00000000); - nvkm_wr32(device, 0x616538 + hoff, 0x00000000); - nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x616528 + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x61652c + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616530 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616534 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x616538 + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); + } /* Audio InfoFrame */ nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); @@ -84,6 +97,18 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) nvkm_wr32(device, 0x616510 + hoff, 0x00000000); nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001); + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000); + if (args->v0.vendor_infoframe_length) { + nvkm_wr32(device, 0x616544 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x616548 + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61654c + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x616550 + hoff, vendor_infoframe->subpack1_low); */ + /* nvkm_wr32(device, 0x616554 + hoff, vendor_infoframe->subpack1_high); */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001); + } + nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 06/10] drm/nouveau: Use supplied HDMI InfoFrames on GT215 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic") InfoFrame. Also don't enable any AVI or Vendor InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Ignore the Audio InfoFrame: We don't supply it, and altering HDMI audio semantics (for better or worse) on this hardware is out of scope for me at this time. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 37 ++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index 8ed00db..257f7c7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -37,6 +37,8 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -62,8 +64,17 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); return 0; @@ -71,12 +82,14 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61c528 + soff, 0x000d0282); - nvkm_wr32(device, 0x61c52c + soff, 0x0000006f); - nvkm_wr32(device, 0x61c530 + soff, 0x00000000); - nvkm_wr32(device, 0x61c534 + soff, 0x00000000); - nvkm_wr32(device, 0x61c538 + soff, 0x00000000); - nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x61c528 + soff, avi_infoframe.header); + nvkm_wr32(device, 0x61c52c + soff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c530 + soff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x61c534 + soff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61c538 + soff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + } /* Audio InfoFrame */ nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); @@ -85,6 +98,18 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) nvkm_wr32(device, 0x61c510 + soff, 0x00000000); nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000); + if (args->v0.vendor_infoframe_length) { + nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe.header); + nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe.subpack1_low); */ + /* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe.subpack1_high); */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001); + } + nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 07/10] drm/nouveau: Use supplied HDMI InfoFrames on GF119 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic"?) InfoFrame. Also don't enable any InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 40 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c index 66ee883..d80e86c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c @@ -36,6 +36,8 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -60,8 +62,17 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); return 0; @@ -69,12 +80,29 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61671c + hoff, 0x000d0282); - nvkm_wr32(device, 0x616720 + hoff, 0x0000006f); - nvkm_wr32(device, 0x616724 + hoff, 0x00000000); - nvkm_wr32(device, 0x616728 + hoff, 0x00000000); - nvkm_wr32(device, 0x61672c + hoff, 0x00000000); - nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x61671c + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x616720 + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616724 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616728 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61672c + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000); + if (args->v0.vendor_infoframe_length) { + /* + * These appear to be the audio infoframe registers, + * but no other set of infoframe registers has yet + * been found. + */ + nvkm_wr32(device, 0x616738 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x61673c + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x616740 + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001); + } /* ??? InfoFrame? */ nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 08/10] drm/nouveau: Use supplied HDMI InfoFrames on GK104 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic"?) InfoFrame. Also don't enable any InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 36 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 3c8c26a..99d2731 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c @@ -37,6 +37,8 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -61,8 +63,17 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); return 0; @@ -70,12 +81,25 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x690008 + hdmi, 0x000d0282); - nvkm_wr32(device, 0x69000c + hdmi, 0x0000006f); - nvkm_wr32(device, 0x690010 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690014 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690018 + hdmi, 0x00000000); - nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe.header); + nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000); + if (args->v0.vendor_infoframe_length) { + nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe.header); + nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001); + } + /* ??? InfoFrame? */ nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 09/10] drm/nouveau: Handle frame-packing mode geometry and timing effects
* Frame-packing modes add an extra vtotal raster lines to each frame above and beyond what the basic mode description calls for. Account for this during scaler configuration (possibly a bit of a hack), during CRTC configuration (clearly not a hack), and when checking that a mode is vaild for a given connector (cribbed from the i915 driver). Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_connector.c | 3 +++ drivers/gpu/drm/nouveau/nv50_display.c | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 947c200..4e3563e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1044,6 +1044,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; } + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + if (clock < min_clock) return MODE_CLOCK_LOW; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 008aea6..fa97604 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1963,6 +1963,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, struct drm_display_mode *umode = &asyh->state.mode; int mode = asyc->scaler.mode; struct edid *edid; + int umode_vdisplay, omode_hdisplay, omode_vdisplay; if (connector->edid_blob_ptr) edid = (struct edid *)connector->edid_blob_ptr->data; @@ -1977,12 +1978,18 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, mode = DRM_MODE_SCALE_FULLSCREEN; } + /* For the user-specified mode, we must ignore doublescan and + * the like, but honor frame packing. + */ + umode_vdisplay = umode->vdisplay; + if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + umode_vdisplay += umode->vtotal; asyh->view.iW = umode->hdisplay; - asyh->view.iH = umode->vdisplay; - asyh->view.oW = omode->hdisplay; - asyh->view.oH = omode->vdisplay; - if (omode->flags & DRM_MODE_FLAG_DBLSCAN) - asyh->view.oH *= 2; + asyh->view.iH = umode_vdisplay; + /* For the ouput mode, we can just use the stock helper. */ + drm_crtc_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay); + asyh->view.oW = omode_hdisplay; + asyh->view.oH = omode_vdisplay; /* Add overscan compensation if necessary, will keep the aspect * ratio the same as the backend mode unless overridden by the @@ -2012,7 +2019,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, switch (mode) { case DRM_MODE_SCALE_CENTER: asyh->view.oW = min((u16)umode->hdisplay, asyh->view.oW); - asyh->view.oH = min((u16)umode->vdisplay, asyh->view.oH); + asyh->view.oH = min((u16)umode_vdisplay, asyh->view.oH); /* fall-through */ case DRM_MODE_SCALE_ASPECT: if (asyh->view.oH < asyh->view.oW) { @@ -2037,7 +2044,7 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) struct nv50_head_mode *m = &asyh->mode; u32 hbackp, vbackp, hfrontp, vfrontp; - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); hbackp = mode->crtc_htotal - mode->crtc_hsync_end; vbackp = mode->crtc_vtotal - mode->crtc_vsync_end; -- 2.10.2
Alastair Bridgewater
2017-Mar-27 21:58 UTC
[Nouveau] [PATCH v2 10/10] drm/nouveau: Enable stereoscopic 3D output over HDMI
Enable stereoscopic output for HDMI and DisplayPort connectors on NV50+ (G80+) hardware. We do not enable stereoscopy on older hardware in case there is some older board that still has HDMI output but for which we have no logic for setting the Vendor InfoFrame. With this, I get an obvious 3D output when using the "testdisplay" program from intel-gpu-tools with the "-3" parameter and outputting to a 3D-capable HDMI display, for all available 3D modes (be they TB, SBSH, or FP) on all four G80+ DISPs. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_connector.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 4e3563e..a2ee93a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1322,6 +1322,13 @@ nouveau_connector_create(struct drm_device *dev, int index) break; } + /* HDMI 3D support */ + if ((disp->disp.oclass >= NV50_DISP) + && ((type == DRM_MODE_CONNECTOR_DisplayPort) + || (type == DRM_MODE_CONNECTOR_eDP) + || (type == DRM_MODE_CONNECTOR_HDMIA))) + connector->stereo_allowed = true; + /* defaults, will get overridden in detect() */ connector->interlace_allowed = false; connector->doublescan_allowed = false; -- 2.10.2
On Mon, Mar 27, 2017 at 05:57:57PM -0400, Alastair Bridgewater wrote:> HDMI 3D mode support, round two. Revisions include no longer dealing > with audio InfoFrames, passing infoframe data to NVKM as bags of bytes > rather than as data pre-packed for the hardware, more-normal return > value checking for drm_hdmi_*_infoframe_from_display_mode() results, > Frame-Packing mode support, more-principled logic for enabling stereo > mode support on a connector, and support for all four nv50+ DISPs. > Additionally, this patch set is being sent to the dri-devel list as > well as the nouveau list. Dropped in this version is the removal of > the "mandatory" 3D mode logic. After discussion, I'm still convinced > that it's wrong, but now I'm convinced that it's too conservative > rather than my original belief which was that it was too liberal. > > Thanks to Ilia Mirkin, Damien Lespiau, and Ben Skeggs for feedback on > the original patch set. If I have neglected to mention anyone, mea > culpa. > > The first patch perhaps isn't technically necessary, but simplifies > the later logic for fixing frame-packing mode timing. There may be > some effect from applying this, as drm_mode_set_crtcinfo() does > something with vscan which can affect the given CRTC timing. I'm > not sure what's going on there, if there's any behavioral change or > not, and if there is what to do about it (other than using > drm_mode_set_crtcinfo() twice, once with CRTC_NO_VSCAN and once > without). > > The second through eighth patches are all the InfoFrame logic again, > this time with support for all four DISP types. This does not appear > to cause regressions in HDMI audio on GT215, GF119, or GK104. I hope > that it doesn't cause a regression on G84, but haven't yet managed to > set up a test case for G84 and HDMI audio. > > The ninth patch is to fix up frame-packing mode timings and geometry. > As the patch comment says, there are clearly-correct parts, and there > are possibly-hacky parts. But it gets frame-packing modes working, > and we can revise from there if necessary. > > And the tenth patch enables stereo mode support... on HDMI and DPort > connectors on nv50+ hardware. HDMI connectors because obvious. DPort > connectors because of DPort to HDMI adaptors.Do you mean DP++ or actual protocol converters? DP++ is just HDMI with a some electrical tweaks, but IIRC proper DP defines 3D stereo quite differently than HDMI. I'm not sure if we should be able to push HDMI style 3D through DP->HDMI/DP++ adaptors. The specs are unfortunately very vague when it comes to such devices.> eDP connectors because > it shouldn't do any harm, and someone might have been maniac enough to > set up an eDP port to point to something with an HDMI 3D setup. And > nv50+ hardware only because while I'm not aware of any pre-nv50 cards > that have HDMI outputs, there could be a setup out there like the PS3 > that uses an external HDMI encoder on pre-nv50 hardware, at which > point none of the mode timing or InfoFrame support is in place for it. > > Alastair Bridgewater (10): > drm/nouveau: Use drm_mode_set_crtcinfo() to compensate for vscan / > ilace > drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames > drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to > NVKM > drm/nouveau: Add mechanism to convert HDMI InfoFrames to hardware > format > drm/nouveau: Use supplied HDMI InfoFrames on G84 hardware > drm/nouveau: Use supplied HDMI InfoFrames on GT215 hardware > drm/nouveau: Use supplied HDMI InfoFrames on GF119 hardware > drm/nouveau: Use supplied HDMI InfoFrames on GK104 hardware > drm/nouveau: Handle frame-packing mode geometry and timing effects > drm/nouveau: Enable stereoscopic 3D output over HDMI > > drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 4 +- > drivers/gpu/drm/nouveau/nouveau_connector.c | 10 +++ > drivers/gpu/drm/nouveau/nv50_display.c | 80 ++++++++++++++++------ > drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 1 + > .../drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c | 66 ++++++++++++++++++ > drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 46 +++++++++++-- > .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 49 +++++++++++-- > .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 45 ++++++++++-- > .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 46 +++++++++++-- > drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h | 11 +++ > 10 files changed, 307 insertions(+), 51 deletions(-) > create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c > > -- > 2.10.2 > > _______________________________________________ > dri-devel mailing list > dri-devel at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel-- Ville Syrjälä Intel OTC
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 00/10] drm/nouveau Enable HDMI Stereoscopy
HDMI 3D mode support, round three. Rebased to drm-next as it was on Sunday morning. Overall structure is the same as v2. Substantially rewrote the first patch (nv50_head_atomic_check_mode()) since a recent change to the calculation of m->v.blankus caused a merge conflict and problems with frame-packed and interlaced 3D modes, and I found a much saner model for how the timing parameters work while I was sorting that out. Observable behavior changes from this patch should be limited to better values for m->v.blankus on interlaced non-doublescan modes and non-interlaced doublescan modes, and some sort of effect on any mode with mode->vscan set (I don't know if this case actually happens, but if it did then I would expect it to have had some sort of problem in the previous implementation anyway). Of the entire series, this first patch is more cleanup-and-bugfix than anything else, and may be a candidate for merging early even if the rest of the series is deferred until the next merge window. Typo-busted the ninth patch (frame-packing geometry and timing) and adjusted for the rename of drm_crtc_get_hv_timing() to drm_mode_get_hv_timing(). Possibly some minor whitespace changes in one or two patches, plus reformatted some of the patch comments. Tested on my gt215 hardware only at this time, since I'm away from the rest of my test hardware for about another week. That said, all of the changes are in the generic nv50 code, rather than being specific to some chipset or family. Thanks to Roy Spliet for assistance (mostly background information) in working out what was going on with m->v.blankus. Alastair Bridgewater (10): drm/nouveau: Clean up nv50_head_atomic_check_mode() and fix blankus calculation drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to NVKM drm/nouveau: Add mechanism to convert HDMI InfoFrames to hardware format drm/nouveau: Use supplied HDMI InfoFrames on G84 hardware drm/nouveau: Use supplied HDMI InfoFrames on GT215 hardware drm/nouveau: Use supplied HDMI InfoFrames on GF119 hardware drm/nouveau: Use supplied HDMI InfoFrames on GK104 hardware drm/nouveau: Handle frame-packing mode geometry and timing effects drm/nouveau: Enable stereoscopic 3D output over HDMI drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 4 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 10 +++ drivers/gpu/drm/nouveau/nv50_display.c | 93 +++++++++++++++------- drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 1 + .../drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c | 66 +++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 46 +++++++++-- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 49 ++++++++++-- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 45 +++++++++-- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 46 +++++++++-- drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h | 11 +++ 10 files changed, 314 insertions(+), 57 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 01/10] drm/nouveau: Clean up nv50_head_atomic_check_mode() and fix blankus calculation
drm_mode_set_crtcinfo() does compensation for interlace and doublescan timing effects already, so do it first and use the compensated figures instead of the constant "vscan / ilace" terms that we had before. And then it turns out that the hardware model for how the timing parameters are configured is basically the standard model, but starting one clock before the sync pulse rather than at the start of the display area, which lets us drastically simplify the overall timing calculations (verifying the changes by algebraic operations is left as an exercise for the reader). Finally, there were a couple of issues with the computation of m->v.blankus that are addressed here. Interlaced modes would generate a negative intermediate result. Double scan modes would generate an overestimate rather than an underestimate. And when enabling frame-packing modes, a rather extreme overestimate would be generated. Fixed, by using the timings as adjusted for the CRTC to find the length of the vertical blanking period instead of mixing adjusted and pre-adjustment timing parameters. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nv50_display.c | 44 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index c9910c8..c901f32 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -2035,34 +2035,37 @@ static void nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) { struct drm_display_mode *mode = &asyh->state.adjusted_mode; - u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; - u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; - u32 hbackp = mode->htotal - mode->hsync_end; - u32 vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; - u32 hfrontp = mode->hsync_start - mode->hdisplay; - u32 vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; - u32 blankus; struct nv50_head_mode *m = &asyh->mode; + u32 blankus; - m->h.active = mode->htotal; - m->h.synce = mode->hsync_end - mode->hsync_start - 1; - m->h.blanke = m->h.synce + hbackp; - m->h.blanks = mode->htotal - hfrontp - 1; + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - m->v.active = mode->vtotal * vscan / ilace; - m->v.synce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; - m->v.blanke = m->v.synce + vbackp; - m->v.blanks = m->v.active - vfrontp - 1; + /* + * DRM modes are defined in terms of a repeating interval + * starting with the active display area. The hardware modes + * are defined in terms of a repeating interval starting one + * unit (pixel or line) into the sync pulse. So, add bias. + */ + + m->h.active = mode->crtc_htotal; + m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1; + m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1; + m->h.blanks = m->h.blanke + mode->crtc_hdisplay; + + m->v.active = mode->crtc_vtotal; + m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1; + m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1; + m->v.blanks = m->v.blanke + mode->crtc_vdisplay; /*XXX: Safe underestimate, even "0" works */ - blankus = (m->v.active - mode->vdisplay - 2) * m->h.active; + blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active; blankus *= 1000; - blankus /= mode->clock; + blankus /= mode->crtc_clock; m->v.blankus = blankus; if (mode->flags & DRM_MODE_FLAG_INTERLACE) { - m->v.blank2e = m->v.active + m->v.synce + vbackp; - m->v.blank2s = m->v.blank2e + (mode->vdisplay * vscan / ilace); + m->v.blank2e = m->v.active + m->v.blanke; + m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay; m->v.active = (m->v.active * 2) + 1; m->interlace = true; } else { @@ -2070,9 +2073,8 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) m->v.blank2s = 1; m->interlace = false; } - m->clock = mode->clock; + m->clock = mode->crtc_clock; - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); asyh->set.mode = true; } -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 02/10] drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames
The nouveau driver, in the Linux 3.7 days, used to try and set the AVI InfoFrame based on the selected display mode. These days, it uses a fixed set of InfoFrames. Start to correct that, by providing a mechanism whereby InfoFrame data may be passed to the NVKM functions that do the actual configuration. At this point, only establish the new parameters and their parsing, don't actually use the data anywhere yet (since it's not supplied anywhere). Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/include/nvif/cl5070.h | 4 +++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 9 ++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 9 ++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 9 ++++++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 9 ++++++++- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h index ae49dfd..9d46eba 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h @@ -76,7 +76,9 @@ struct nv50_disp_sor_hdmi_pwr_v0 { __u8 state; __u8 max_ac_packet; __u8 rekey; - __u8 pad04[4]; + __u8 avi_infoframe_length; + __u8 vendor_infoframe_length; + __u8 pad06[2]; }; struct nv50_disp_sor_lvds_script_v0 { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c index 1c4256e..77e5f5a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c @@ -40,7 +40,7 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -54,6 +54,13 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c index 632f02d..66ee883 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c @@ -40,7 +40,7 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -53,6 +53,13 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 4e8067d..3c8c26a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c @@ -41,7 +41,7 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -54,6 +54,13 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index f1afc16..8ed00db 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -41,7 +41,7 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) int ret = -ENOSYS; nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " "max_ac_packet %d rekey %d\n", args->v0.version, args->v0.state, @@ -55,6 +55,13 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) } else return ret; + if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) > size) + return -ENOSYS; + else if ((args->v0.avi_infoframe_length + + args->v0.vendor_infoframe_length) < size) + return -E2BIG; + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 03/10] drm/nouveau: Pass mode-dependent AVI and Vendor HDMI InfoFrames to NVKM
Now that we have mechanism by which to pass mode-dependent HDMI InfoFrames to the low-level hardware driver, it is incumbent upon us to do so. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nv50_display.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index c901f32..93149ca 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -23,6 +23,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -31,6 +32,7 @@ #include <drm/drm_dp_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_edid.h> #include <nvif/class.h> #include <nvif/cl0002.h> @@ -2717,6 +2719,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) 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, @@ -2728,17 +2731,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) }; struct nouveau_connector *nv_connector; u32 max_ac_packet; + union hdmi_infoframe avi_frame; + union hdmi_infoframe vendor_frame; + int ret; + int size; nv_connector = nouveau_encoder_connector_get(nv_encoder); if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode); + if (!ret) { + /* 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, 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); + } + max_ac_packet = mode->htotal - mode->hdisplay; max_ac_packet -= args.pwr.rekey; max_ac_packet -= 18; /* constant from tegra */ args.pwr.max_ac_packet = max_ac_packet / 32; - nvif_mthd(disp->disp, 0, &args, sizeof(args)); + size = sizeof(args.base) + + sizeof(args.pwr) + + args.pwr.avi_infoframe_length + + args.pwr.vendor_infoframe_length; + nvif_mthd(disp->disp, 0, &args, size); nv50_audio_enable(encoder, mode); } -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 04/10] drm/nouveau: Add mechanism to convert HDMI InfoFrames to hardware format
HDMI InfoFrames are passed to NVKM as bags of bytes, but the hardware needs them to be packed into words. Rather than having four (or more) copies of the packing logic introduce a single copy now, in a central place. We currently need these for AVI and Vendor InfoFrames, but we may also expect to need them for Audio InfoFrames at some point. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 1 + .../drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c | 66 ++++++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h | 11 ++++ 3 files changed, 78 insertions(+) create mode 100644 drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index fa05d16..65ae870 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -29,6 +29,7 @@ nvkm-y += nvkm/engine/disp/conn.o nvkm-y += nvkm/engine/disp/hdagt215.o nvkm-y += nvkm/engine/disp/hdagf119.o +nvkm-y += nvkm/engine/disp/hdmi_infoframe.o nvkm-y += nvkm/engine/disp/hdmig84.o nvkm-y += nvkm/engine/disp/hdmigt215.o nvkm-y += nvkm/engine/disp/hdmigf119.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c new file mode 100644 index 0000000..e04f2e8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi_infoframe.c @@ -0,0 +1,66 @@ +#include "nv50.h" + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len) +{ + u32 header = 0; + u32 subpack0_low = 0; + u32 subpack0_high = 0; + u32 subpack1_low = 0; + u32 subpack1_high = 0; + + switch (len) { + /* + * "When in doubt, use brute force." + * -- Ken Thompson. + */ + default: + /* + * We presume that no valid frame is longer than 17 + * octets, including header... And truncate to that + * if it's longer. + */ + case 17: + subpack1_high = (raw_frame[16] << 16); + case 16: + subpack1_high |= (raw_frame[15] << 8); + case 15: + subpack1_high |= raw_frame[14]; + case 14: + subpack1_low = (raw_frame[13] << 24); + case 13: + subpack1_low |= (raw_frame[12] << 16); + case 12: + subpack1_low |= (raw_frame[11] << 8); + case 11: + subpack1_low |= raw_frame[10]; + case 10: + subpack0_high = (raw_frame[9] << 16); + case 9: + subpack0_high |= (raw_frame[8] << 8); + case 8: + subpack0_high |= raw_frame[7]; + case 7: + subpack0_low = (raw_frame[6] << 24); + case 6: + subpack0_low |= (raw_frame[5] << 16); + case 5: + subpack0_low |= (raw_frame[4] << 8); + case 4: + subpack0_low |= raw_frame[3]; + case 3: + header = (raw_frame[2] << 16); + case 2: + header |= (raw_frame[1] << 8); + case 1: + header |= raw_frame[0]; + case 0: + break; + } + + packed_frame->header = header; + packed_frame->subpack0_low = subpack0_low; + packed_frame->subpack0_high = subpack0_high; + packed_frame->subpack1_low = subpack1_low; + packed_frame->subpack1_high = subpack1_high; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h index 1e1de6b..37ec2a1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h @@ -40,6 +40,17 @@ int nv50_dac_sense(NV50_DISP_MTHD_V1); int gt215_hda_eld(NV50_DISP_MTHD_V1); int gf119_hda_eld(NV50_DISP_MTHD_V1); +struct packed_hdmi_infoframe { + u32 header; + u32 subpack0_low; + u32 subpack0_high; + u32 subpack1_low; + u32 subpack1_high; +}; + +void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, + u8 *raw_frame, ssize_t len); + int g84_hdmi_ctrl(NV50_DISP_MTHD_V1); int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1); int gf119_hdmi_ctrl(NV50_DISP_MTHD_V1); -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 05/10] drm/nouveau: Use supplied HDMI InfoFrames on G84 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic"?) InfoFrame. Also don't enable any AVI or Vendor InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Ignore the Audio InfoFrame: We don't supply it, and altering HDMI audio semantics (for better or worse) on this hardware is out of scope for me at this time. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | 37 ++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c index 77e5f5a..139344e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c @@ -36,6 +36,8 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -61,8 +63,17 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61653c + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); return 0; @@ -70,12 +81,14 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x616528 + hoff, 0x000d0282); - nvkm_wr32(device, 0x61652c + hoff, 0x0000006f); - nvkm_wr32(device, 0x616530 + hoff, 0x00000000); - nvkm_wr32(device, 0x616534 + hoff, 0x00000000); - nvkm_wr32(device, 0x616538 + hoff, 0x00000000); - nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x616528 + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x61652c + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616530 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616534 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x616538 + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); + } /* Audio InfoFrame */ nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); @@ -84,6 +97,18 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) nvkm_wr32(device, 0x616510 + hoff, 0x00000000); nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001); + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000); + if (args->v0.vendor_infoframe_length) { + nvkm_wr32(device, 0x616544 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x616548 + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61654c + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x616550 + hoff, vendor_infoframe->subpack1_low); */ + /* nvkm_wr32(device, 0x616554 + hoff, vendor_infoframe->subpack1_high); */ + nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001); + } + nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 06/10] drm/nouveau: Use supplied HDMI InfoFrames on GT215 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic") InfoFrame. Also don't enable any AVI or Vendor InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Ignore the Audio InfoFrame: We don't supply it, and altering HDMI audio semantics (for better or worse) on this hardware is out of scope for me at this time. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | 37 ++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index 8ed00db..257f7c7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c @@ -37,6 +37,8 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -62,8 +64,17 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); return 0; @@ -71,12 +82,14 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61c528 + soff, 0x000d0282); - nvkm_wr32(device, 0x61c52c + soff, 0x0000006f); - nvkm_wr32(device, 0x61c530 + soff, 0x00000000); - nvkm_wr32(device, 0x61c534 + soff, 0x00000000); - nvkm_wr32(device, 0x61c538 + soff, 0x00000000); - nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x61c528 + soff, avi_infoframe.header); + nvkm_wr32(device, 0x61c52c + soff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c530 + soff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x61c534 + soff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61c538 + soff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); + } /* Audio InfoFrame */ nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); @@ -85,6 +98,18 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) nvkm_wr32(device, 0x61c510 + soff, 0x00000000); nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); + /* Vendor InfoFrame */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000); + if (args->v0.vendor_infoframe_length) { + nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe.header); + nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe.subpack0_high); + /* Is there a second (or up to fourth?) set of subpack registers here? */ + /* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe.subpack1_low); */ + /* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe.subpack1_high); */ + nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001); + } + nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 07/10] drm/nouveau: Use supplied HDMI InfoFrames on GF119 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic"?) InfoFrame. Also don't enable any InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | 40 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c index 66ee883..d80e86c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c @@ -36,6 +36,8 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -60,8 +62,17 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); return 0; @@ -69,12 +80,29 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x61671c + hoff, 0x000d0282); - nvkm_wr32(device, 0x616720 + hoff, 0x0000006f); - nvkm_wr32(device, 0x616724 + hoff, 0x00000000); - nvkm_wr32(device, 0x616728 + hoff, 0x00000000); - nvkm_wr32(device, 0x61672c + hoff, 0x00000000); - nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x61671c + hoff, avi_infoframe.header); + nvkm_wr32(device, 0x616720 + hoff, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x616724 + hoff, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x616728 + hoff, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x61672c + hoff, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000); + if (args->v0.vendor_infoframe_length) { + /* + * These appear to be the audio infoframe registers, + * but no other set of infoframe registers has yet + * been found. + */ + nvkm_wr32(device, 0x616738 + hoff, vendor_infoframe.header); + nvkm_wr32(device, 0x61673c + hoff, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x616740 + hoff, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001); + } /* ??? InfoFrame? */ nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 08/10] drm/nouveau: Use supplied HDMI InfoFrames on GK104 hardware
Now that we have the InfoFrame data being provided, for the most part, program the hardware to use it. While we're here, and since the functionality will come in handy for supporting 3D stereoscopy, implement setting the Vendor ("generic"?) InfoFrame. Also don't enable any InfoFrame that is not provided, and disable the Vendor InfoFrame when disabling the output. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- .../gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | 36 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 3c8c26a..99d2731 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c @@ -37,6 +37,8 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) union { struct nv50_disp_sor_hdmi_pwr_v0 v0; } *args = data; + struct packed_hdmi_infoframe avi_infoframe; + struct packed_hdmi_infoframe vendor_infoframe; u32 ctrl; int ret = -ENOSYS; @@ -61,8 +63,17 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) + args->v0.vendor_infoframe_length) < size) return -E2BIG; + pack_hdmi_infoframe(&avi_infoframe, + data, + args->v0.avi_infoframe_length); + + pack_hdmi_infoframe(&vendor_infoframe, + data + args->v0.avi_infoframe_length, + args->v0.vendor_infoframe_length); + if (!(ctrl & 0x40000000)) { nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); return 0; @@ -70,12 +81,25 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) /* AVI InfoFrame */ nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x690008 + hdmi, 0x000d0282); - nvkm_wr32(device, 0x69000c + hdmi, 0x0000006f); - nvkm_wr32(device, 0x690010 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690014 + hdmi, 0x00000000); - nvkm_wr32(device, 0x690018 + hdmi, 0x00000000); - nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + if (args->v0.avi_infoframe_length) { + nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe.header); + nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe.subpack0_low); + nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe.subpack0_high); + nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe.subpack1_low); + nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe.subpack1_high); + nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); + } + + /* GENERIC(?) / Vendor InfoFrame? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000); + if (args->v0.vendor_infoframe_length) { + nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe.header); + nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe.subpack0_low); + nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe.subpack0_high); + /* Is there a second (or further?) set of subpack registers here? */ + nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001); + } + /* ??? InfoFrame? */ nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 09/10] drm/nouveau: Handle frame-packing mode geometry and timing effects
Frame-packing modes add an extra vtotal raster lines to each frame above and beyond what the basic mode description calls for. Account for this during scaler configuration (possibly a bit of a hack), during CRTC configuration (clearly not a hack), and when checking that a mode is valid for a given connector (cribbed from the i915 driver). Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_connector.c | 3 +++ drivers/gpu/drm/nouveau/nv50_display.c | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f802bcd..9a91e79 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1045,6 +1045,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; } + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + if (clock < min_clock) return MODE_CLOCK_LOW; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 93149ca..c98ed9a 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1966,6 +1966,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, struct drm_display_mode *umode = &asyh->state.mode; int mode = asyc->scaler.mode; struct edid *edid; + int umode_vdisplay, omode_hdisplay, omode_vdisplay; if (connector->edid_blob_ptr) edid = (struct edid *)connector->edid_blob_ptr->data; @@ -1980,12 +1981,18 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, mode = DRM_MODE_SCALE_FULLSCREEN; } + /* For the user-specified mode, we must ignore doublescan and + * the like, but honor frame packing. + */ + umode_vdisplay = umode->vdisplay; + if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + umode_vdisplay += umode->vtotal; asyh->view.iW = umode->hdisplay; - asyh->view.iH = umode->vdisplay; - asyh->view.oW = omode->hdisplay; - asyh->view.oH = omode->vdisplay; - if (omode->flags & DRM_MODE_FLAG_DBLSCAN) - asyh->view.oH *= 2; + asyh->view.iH = umode_vdisplay; + /* For the output mode, we can just use the stock helper. */ + drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay); + asyh->view.oW = omode_hdisplay; + asyh->view.oH = omode_vdisplay; /* Add overscan compensation if necessary, will keep the aspect * ratio the same as the backend mode unless overridden by the @@ -2015,7 +2022,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, switch (mode) { case DRM_MODE_SCALE_CENTER: asyh->view.oW = min((u16)umode->hdisplay, asyh->view.oW); - asyh->view.oH = min((u16)umode->vdisplay, asyh->view.oH); + asyh->view.oH = min((u16)umode_vdisplay, asyh->view.oH); /* fall-through */ case DRM_MODE_SCALE_ASPECT: if (asyh->view.oH < asyh->view.oW) { @@ -2040,7 +2047,7 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) struct nv50_head_mode *m = &asyh->mode; u32 blankus; - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); /* * DRM modes are defined in terms of a repeating interval -- 2.10.2
Alastair Bridgewater
2017-Apr-11 17:11 UTC
[Nouveau] [PATCH v3 10/10] drm/nouveau: Enable stereoscopic 3D output over HDMI
Enable stereoscopic output for HDMI and DisplayPort connectors on NV50+ (G80+) hardware. We do not enable stereoscopy on older hardware in case there is some older board that still has HDMI output but for which we have no logic for setting the Vendor InfoFrame. With this, I get an obvious 3D output when using the "testdisplay" program from intel-gpu-tools with the "-3" parameter and outputting to a 3D-capable HDMI display, for all available 3D modes (be they TB, SBSH, or FP) on all four G80+ DISPs. Signed-off-by: Alastair Bridgewater <alastair.bridgewater at gmail.com> --- drivers/gpu/drm/nouveau/nouveau_connector.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 9a91e79..ff188c7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1324,6 +1324,13 @@ nouveau_connector_create(struct drm_device *dev, int index) break; } + /* HDMI 3D support */ + if ((disp->disp.oclass >= NV50_DISP) + && ((type == DRM_MODE_CONNECTOR_DisplayPort) + || (type == DRM_MODE_CONNECTOR_eDP) + || (type == DRM_MODE_CONNECTOR_HDMIA))) + connector->stereo_allowed = true; + /* defaults, will get overridden in detect() */ connector->interlace_allowed = false; connector->doublescan_allowed = false; -- 2.10.2
Maybe Matching Threads
- [PATCH 0/5] drm/nouveau: add basic HDMI 2.0 support
- [PATCH] drm/nouveau: Fix HDA ELD handling (thus, HDMI audio) on gt215
- [PATCH 1/6] drm/nouveau: Extend NVKM HDMI power control method to set InfoFrames
- [PATCH] drm/nouveau/nvkm: mark expected switch fall-throughs
- [Bug 92274] New: nouveau black screen and errors with two monitors attached