diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/engine/disp')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c | 54 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c | 36 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c | 1 |
12 files changed, 143 insertions, 23 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index 3d485dbf310a..8089ac9a12e2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -50,6 +50,7 @@ nvkm-y += nvkm/engine/disp/hdmig84.o nvkm-y += nvkm/engine/disp/hdmigt215.o nvkm-y += nvkm/engine/disp/hdmigf119.o nvkm-y += nvkm/engine/disp/hdmigk104.o +nvkm-y += nvkm/engine/disp/hdmigm200.o nvkm-y += nvkm/engine/disp/hdmigv100.o nvkm-y += nvkm/engine/disp/conn.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c index 32fa94a9773f..cbd33e87b799 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c @@ -275,6 +275,7 @@ nvkm_disp_oneinit(struct nvkm_engine *engine) struct nvkm_outp *outp, *outt, *pair; struct nvkm_conn *conn; struct nvkm_head *head; + struct nvkm_ior *ior; struct nvbios_connE connE; struct dcb_output dcbE; u8 hpd = 0, ver, hdr; @@ -399,6 +400,19 @@ nvkm_disp_oneinit(struct nvkm_engine *engine) return ret; } + /* Enforce identity-mapped SOR assignment for panels, which have + * certain bits (ie. backlight controls) wired to a specific SOR. + */ + list_for_each_entry(outp, &disp->outp, head) { + if (outp->conn->info.type == DCB_CONNECTOR_LVDS || + outp->conn->info.type == DCB_CONNECTOR_eDP) { + ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1); + if (!WARN_ON(!ior)) + ior->identity = true; + outp->identity = true; + } + } + i = 0; list_for_each_entry(head, &disp->head, head) i = max(i, head->id + 1); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index 7c5bed29ffef..5f301e632599 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -28,6 +28,7 @@ #include <subdev/bios.h> #include <subdev/bios/init.h> +#include <subdev/gpio.h> #include <subdev/i2c.h> #include <nvif/event.h> @@ -412,14 +413,10 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) } static void -nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior) +nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) { struct nvkm_dp *dp = nvkm_dp(outp); - /* Prevent link from being retrained if sink sends an IRQ. */ - atomic_set(&dp->lt.done, 0); - ior->dp.nr = 0; - /* Execute DisableLT script from DP Info Table. */ nvbios_init(&ior->disp->engine.subdev, dp->info.script[4], init.outp = &dp->outp.info; @@ -428,6 +425,16 @@ nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior) ); } +static void +nvkm_dp_release(struct nvkm_outp *outp) +{ + struct nvkm_dp *dp = nvkm_dp(outp); + + /* Prevent link from being retrained if sink sends an IRQ. */ + atomic_set(&dp->lt.done, 0); + dp->outp.ior->dp.nr = 0; +} + static int nvkm_dp_acquire(struct nvkm_outp *outp) { @@ -491,7 +498,7 @@ done: return ret; } -static void +static bool nvkm_dp_enable(struct nvkm_dp *dp, bool enable) { struct nvkm_i2c_aux *aux = dp->aux; @@ -505,7 +512,7 @@ nvkm_dp_enable(struct nvkm_dp *dp, bool enable) if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, sizeof(dp->dpcd))) - return; + return true; } if (dp->present) { @@ -515,6 +522,7 @@ nvkm_dp_enable(struct nvkm_dp *dp, bool enable) } atomic_set(&dp->lt.done, 0); + return false; } static int @@ -555,9 +563,38 @@ nvkm_dp_fini(struct nvkm_outp *outp) static void nvkm_dp_init(struct nvkm_outp *outp) { + struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; struct nvkm_dp *dp = nvkm_dp(outp); + nvkm_notify_put(&dp->outp.conn->hpd); - nvkm_dp_enable(dp, true); + + /* eDP panels need powering on by us (if the VBIOS doesn't default it + * to on) before doing any AUX channel transactions. LVDS panel power + * is handled by the SOR itself, and not required for LVDS DDC. + */ + if (dp->outp.conn->info.type == DCB_CONNECTOR_eDP) { + int power = nvkm_gpio_get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff); + if (power == 0) + nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); + + /* We delay here unconditionally, even if already powered, + * because some laptop panels having a significant resume + * delay before the panel begins responding. + * + * This is likely a bit of a hack, but no better idea for + * handling this at the moment. + */ + msleep(300); + + /* If the eDP panel can't be detected, we need to restore + * the panel power GPIO to avoid breaking another output. + */ + if (!nvkm_dp_enable(dp, true) && power == 0) + nvkm_gpio_set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 0); + } else { + nvkm_dp_enable(dp, true); + } + nvkm_notify_get(&dp->hpd); } @@ -576,6 +613,7 @@ nvkm_dp_func = { .fini = nvkm_dp_fini, .acquire = nvkm_dp_acquire, .release = nvkm_dp_release, + .disable = nvkm_dp_disable, }; static int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c new file mode 100644 index 000000000000..9b16a08eb4d9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c @@ -0,0 +1,36 @@ +/* + * Copyright 2018 Ilia Mirkin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ilia Mirkin + */ +#include "hdmi.h" + +void +gm200_hdmi_scdc(struct nvkm_ior *ior, int head, u8 scdc) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 hoff = head * 0x800; + const u32 ctrl = scdc & 0x3; + + nvkm_mask(device, 0x61c5bc + hoff, 0x00000003, ctrl); + + ior->tmds.high_speed = !!(scdc & 0x2); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index e0b4e0c5704e..0f0c86c32ec3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -16,6 +16,7 @@ struct nvkm_ior { char name[8]; struct list_head head; + bool identity; struct nvkm_ior_state { struct nvkm_outp *outp; @@ -40,6 +41,11 @@ struct nvkm_ior { u8 nr; u8 bw; } dp; + + /* Armed TMDS state. */ + struct { + bool high_speed; + } tmds; }; struct nvkm_ior_func { @@ -60,6 +66,7 @@ struct nvkm_ior_func { void (*ctrl)(struct nvkm_ior *, int head, bool enable, u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size); + void (*scdc)(struct nvkm_ior *, int head, u8 scdc); } hdmi; struct { @@ -143,6 +150,8 @@ void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); void gv100_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); +void gm200_hdmi_scdc(struct nvkm_ior *, int, u8); + void gt215_hda_hpd(struct nvkm_ior *, int, bool); void gt215_hda_eld(struct nvkm_ior *, u8 *, u8); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index f89c7b977aa5..def005dd5fda 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -501,11 +501,11 @@ nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head) nv50_disp_super_ied_off(head, ior, 2); /* If we're shutting down the OR's only active head, execute - * the output path's release function. + * the output path's disable function. */ if (ior->arm.head == (1 << head->id)) { - if ((outp = ior->arm.outp) && outp->func->release) - outp->func->release(outp, ior); + if ((outp = ior->arm.outp) && outp->func->disable) + outp->func->disable(outp, ior); } } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index be9e7f8c3b23..c62030c96fba 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -93,6 +93,8 @@ nvkm_outp_release(struct nvkm_outp *outp, u8 user) if (ior) { outp->acquired &= ~user; if (!outp->acquired) { + if (outp->func->release && outp->ior) + outp->func->release(outp); outp->ior->asy.outp = NULL; outp->ior = NULL; } @@ -127,17 +129,26 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) if (proto == UNKNOWN) return -ENOSYS; + /* Deal with panels requiring identity-mapped SOR assignment. */ + if (outp->identity) { + ior = nvkm_ior_find(outp->disp, SOR, ffs(outp->info.or) - 1); + if (WARN_ON(!ior)) + return -ENOSPC; + return nvkm_outp_acquire_ior(outp, user, ior); + } + /* First preference is to reuse the OR that is currently armed * on HW, if any, in order to prevent unnecessary switching. */ list_for_each_entry(ior, &outp->disp->ior, head) { - if (!ior->asy.outp && ior->arm.outp == outp) + if (!ior->identity && !ior->asy.outp && ior->arm.outp == outp) return nvkm_outp_acquire_ior(outp, user, ior); } /* Failing that, a completely unused OR is the next best thing. */ list_for_each_entry(ior, &outp->disp->ior, head) { - if (!ior->asy.outp && ior->type == type && !ior->arm.outp && + if (!ior->identity && + !ior->asy.outp && ior->type == type && !ior->arm.outp && (ior->func->route.set || ior->id == __ffs(outp->info.or))) return nvkm_outp_acquire_ior(outp, user, ior); } @@ -146,7 +157,7 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) * but will be released during the next modeset. */ list_for_each_entry(ior, &outp->disp->ior, head) { - if (!ior->asy.outp && ior->type == type && + if (!ior->identity && !ior->asy.outp && ior->type == type && (ior->func->route.set || ior->id == __ffs(outp->info.or))) return nvkm_outp_acquire_ior(outp, user, ior); } @@ -245,7 +256,6 @@ nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp, outp->index = index; outp->info = *dcbE; outp->i2c = nvkm_i2c_bus_find(i2c, dcbE->i2c_index); - outp->or = ffs(outp->info.or) - 1; OUTP_DBG(outp, "type %02x loc %d or %d link %d con %x " "edid %x bus %d head %x", diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index ea84d7d5741a..6c8aa5cfed9d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -13,10 +13,10 @@ struct nvkm_outp { struct dcb_output info; struct nvkm_i2c_bus *i2c; - int or; struct list_head head; struct nvkm_conn *conn; + bool identity; /* Assembly state. */ #define NVKM_OUTP_PRIV 1 @@ -41,7 +41,8 @@ struct nvkm_outp_func { void (*init)(struct nvkm_outp *); void (*fini)(struct nvkm_outp *); int (*acquire)(struct nvkm_outp *); - void (*release)(struct nvkm_outp *, struct nvkm_ior *); + void (*release)(struct nvkm_outp *); + void (*disable)(struct nvkm_outp *, struct nvkm_ior *); }; #define OUTP_MSG(o,l,f,a...) do { \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c index 3aa5a2879239..5f758948d6e1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c @@ -176,9 +176,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); 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", + "max_ac_packet %d rekey %d scdc %d\n", args->v0.version, args->v0.state, - args->v0.max_ac_packet, args->v0.rekey); + args->v0.max_ac_packet, args->v0.rekey, + args->v0.scdc); if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) return -EINVAL; if ((args->v0.avi_infoframe_length @@ -202,6 +203,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) args->v0.max_ac_packet, args->v0.rekey, avi, avi_size, vendor, vendor_size); + + if (outp->ior->func->hdmi.scdc) + outp->ior->func->hdmi.scdc( + outp->ior, hidx, args->v0.scdc); + return 0; } break; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c index e6e6dfbb1283..456a5a143522 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c @@ -120,13 +120,16 @@ void gf119_sor_clock(struct nvkm_ior *sor) { struct nvkm_device *device = sor->disp->engine.subdev.device; - const int div = sor->asy.link == 3; const u32 soff = nv50_ior_base(sor); + u32 div1 = sor->asy.link == 3; + u32 div2 = sor->asy.link == 3; if (sor->asy.proto == TMDS) { - /* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */ - nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18); + const u32 speed = sor->tmds.high_speed ? 0x14 : 0x0a; + nvkm_mask(device, 0x612300 + soff, 0x007c0000, speed << 18); + if (sor->tmds.high_speed) + div2 = 1; } - nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div); + nvkm_mask(device, 0x612300 + soff, 0x00000707, (div2 << 8) | div1); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c index d892bdf04034..384f82652bec 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c @@ -99,6 +99,7 @@ gm200_sor = { .clock = gf119_sor_clock, .hdmi = { .ctrl = gk104_hdmi_ctrl, + .scdc = gm200_hdmi_scdc, }, .dp = { .lanes = { 0, 1, 2, 3 }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c index 040db8a338de..8ba881a729ee 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgv100.c @@ -88,6 +88,7 @@ gv100_sor = { .clock = gf119_sor_clock, .hdmi = { .ctrl = gv100_hdmi_ctrl, + .scdc = gm200_hdmi_scdc, }, .dp = { .lanes = { 0, 1, 2, 3 }, |