From patchwork Thu Jul 30 11:31:20 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukas Wunner X-Patchwork-Id: 7000791 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B1FB3C05AE for ; Wed, 12 Aug 2015 11:40:08 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 67D49206CE for ; Wed, 12 Aug 2015 11:40:07 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 0E96F205D3 for ; Wed, 12 Aug 2015 11:40:06 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 375A86EB2D; Wed, 12 Aug 2015 04:40:05 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mailout3.hostsharing.net (mailout3.hostsharing.net [176.9.242.54]) by gabe.freedesktop.org (Postfix) with ESMTPS id 09C5C6EB30 for ; Wed, 12 Aug 2015 04:39:56 -0700 (PDT) Received: from h08.hostsharing.net (h08.hostsharing.net [83.223.95.28]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mailout3.hostsharing.net (Postfix) with ESMTPS id 7BD27101E6B35; Wed, 12 Aug 2015 13:33:46 +0200 (CEST) Received: from localhost (6-38-90-81.adsl.cmo.de [81.90.38.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA (128/128 bits)) (No client certificate requested) by h08.hostsharing.net (Postfix) with ESMTPSA id B984760423EA; Wed, 12 Aug 2015 13:33:42 +0200 (CEST) X-Mailbox-Line: From 3f6ade80a735137c732b328e65d6e38d728cdaf6 Mon Sep 17 00:00:00 2001 Message-Id: <3f6ade80a735137c732b328e65d6e38d728cdaf6.1439288957.git.lukas@wunner.de> In-Reply-To: References: <29bed586baf62f6be77b7ab0ba1b8f5cb3be3aad.1439288957.git.lukas@wunner.de> <164b43588e80baaddb7a4d1081785c4d03a89c4b.1439288957.git.lukas@wunner.de> <27944adb13aa1ab246ee4a1ebb833e397324d073.1439288957.git.lukas@wunner.de> <2ac3eca0759cedd1009221cbef908605f8d29e1e.1439288957.git.lukas@wunner.de> <832f1cfceab9d9403b541b51733b87110fd8e019.1439288957.git.lukas@wunner.de> <88b49891e95ced43bfb57cc8472bb69985827153.1439288957.git.lukas@wunner.de> <2032c8103fc62d709f10be04a54b4df5c6302ad2.1439288957.git.lukas@wunner.de> <5aa64781469028545deb0f0360f56dad5dbe0057.1439288957.git.lukas@wunner.de> <53015af5b9a91332d3d74ef54fff587395447ecf.1439288957.git.lukas@wunner.de> <3313fb587249b00537dbcde127223151652427ec.1439288957.git.lukas@wunner.de> <9eed8ede6f15a254ad578e783b050e1c585d5a15.1439288957.git.lukas@wunner.de> <38d3a35d277771b86e59879c2db25f434e7fbcb2.1439288957.git.lukas@wunner.de> From: Lukas Wunner Date: Thu, 30 Jul 2015 13:31:20 +0200 Subject: [PATCH v2 21/22 EXPERIMENTAL] drm/nouveau/i2c: Use vga_switcheroo active client as proxy when reading DDC/AUX To: dri-devel@lists.freedesktop.org Cc: Andreas Heider , Paul Hordiienko , William Brown , Bruno Bierbaumer , Matthew Garrett , Dave Airlie X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-0.8 required=5.0 tests=BAYES_00, DATE_IN_PAST_96_XX, RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The retina MacBook Pro uses an eDP panel and a gmux controller to switch the panel between its two GPUs. Unfortunately it seems that it cannot switch the AUX channel separately from the main link. But we can emulate switching of DDC/AUX in software by using the active client as a proxy to talk to the panel. Proxying of the AUX channel is facilitated by way of Thierry Reding's awesome struct drm_dp_aux abstraction (cf. c197db75ff5c, "drm/dp: Add AUX channel infrastructure"). However, as regards usage of struct drm_dp_aux, nouveau is the odd man out: A struct drm_dp_aux is defined as part of struct nouveau_connector but never used. Instead, the AUX channel is accessed directly with nv_rdaux() and nv_wraux(), even in the DRM part of the driver. To enable proxying in nouveau, inject a pointer to the struct drm_dp_aux from the DRM part of the driver into the struct nvkm_i2c_port. Modify nv_rdaux() to try drm_dp_dpcd_read() first. If that fails, fall back to accessing the AUX channel directly. Enclose in #if IS_ENABLED(CONFIG_DRM _KMS_HELPER) to keep the NVKM part of the driver portable and free of DRM symbols. Obviously this is a bit of a kludge but it seems there's no elegant way short of factoring all the AUX communication in dport.c / outpdp.c out and into the DRM part of the driver (plus the AUX initialization in VBIOS). When the driver first initializes the output with nvkm_output_dp_init(), the pointer to the struct drm_dp_aux is not yet injected into the struct nvkm_i2c_port. Thus, if the panel is not switched to the Nvidia GPU, the dpcd attribute of struct nvkm_output_dp can't be filled and the link doesn't get trained. Make up for this by checking the link training status in nouveau_dp_detect() and calling nvkm_output_dp_detect() if the link hasn't been trained yet. Modify link training itself so that it does not fail when writing to DPCD if the value to be written is identical with what's already configured in DPCD. (Proxying is currently read only for safety reasons.) Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115 Tested-by: Paul Hordiienko [MBP 6,2 2010 intel ILK + nvidia GT216 pre-retina] Tested-by: William Brown [MBP 8,2 2011 intel SNB + amd turks pre-retina] Tested-by: Lukas Wunner [MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina] Tested-by: Bruno Bierbaumer [MBP 11,3 2013 intel HSW + nvidia GK107 retina -- work in progress] Signed-off-by: Lukas Wunner --- drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h | 1 + drivers/gpu/drm/nouveau/nouveau_connector.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_dp.c | 20 +++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c | 6 +++++- drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c | 2 +- drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h | 1 + drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c | 24 +++++++++++++++++++++++ 7 files changed, 54 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h index a2e3373..9fa95fb 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h @@ -37,6 +37,7 @@ struct nvkm_i2c_port { struct list_head head; u8 index; int aux; + void *drm_dp_aux; const struct nvkm_i2c_func *func; }; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 1e5224f..159df7f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -144,8 +144,8 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) nv_encoder = nouveau_encoder(encoder); if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { - int ret = nouveau_dp_detect(nv_encoder); - if (ret == 0) + nv_encoder->i2c->drm_dp_aux = &nv_connector->aux; + if (nouveau_dp_detect(nv_encoder) == 0) break; } else if (nv_encoder->i2c) { diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index c3ef30b..317d6b1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -30,6 +30,25 @@ #include "nouveau_encoder.h" #include "nouveau_crtc.h" +#include +#include + +static void +nouveau_dp_check_link_training(struct nouveau_encoder *nv_encoder) +{ + struct nvkm_disp *disp = nvkm_disp(nv_encoder->i2c); + struct nvkm_output *outp; + struct nvkm_output_dp *outpdp; + + list_for_each_entry(outp, &disp->outp, head) + if (outp->info.index == nv_encoder->dcb->index) + break; + + outpdp = (struct nvkm_output_dp *)outp; + if (!atomic_read(&outpdp->lt.done)) + nvkm_output_dp_detect(outpdp); +} + static void nouveau_dp_probe_oui(struct drm_device *dev, struct nvkm_i2c_port *auxch, u8 *dpcd) @@ -85,5 +104,6 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); nouveau_dp_probe_oui(dev, auxch, dpcd); + nouveau_dp_check_link_training(nv_encoder); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c index 6834766..5257e4c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c @@ -61,7 +61,7 @@ dp_set_link_config(struct dp_state *dp) .execute = 1, }; u32 lnkcmp; - u8 sink[2]; + u8 sink[2], sink_rd[2]; int ret; DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); @@ -98,6 +98,10 @@ dp_set_link_config(struct dp_state *dp) if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; + if (nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, sink_rd, 2) == 0 && + memcmp(sink, sink_rd, 2) == 0) + return 0; + return nv_wraux(outp->base.edid, DPCD_LC00_LINK_BW_SET, sink, 2); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c index 0bde0fa..b95373b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c @@ -122,7 +122,7 @@ nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present) } } -static void +void nvkm_output_dp_detect(struct nvkm_output_dp *outp) { struct nvkm_i2c_port *port = outp->base.edid; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h index 70c77ae..0bd4dcb 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h @@ -58,4 +58,5 @@ struct nvkm_output_dp_impl { }; int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait); +void nvkm_output_dp_detect(struct nvkm_output_dp *); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c index 1c18860..16ec3cc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c @@ -23,10 +23,34 @@ */ #include "priv.h" +#if IS_ENABLED(CONFIG_DRM_KMS_HELPER) +#include +#endif + +static int +drm_rdaux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size) +{ +#if IS_ENABLED(CONFIG_DRM_KMS_HELPER) + if (port->drm_dp_aux) { + nv_debug(port, "Try reading DPCD with KMS helper: addr=0x%x size=%d\n", + addr, size); + return !(drm_dp_dpcd_read(port->drm_dp_aux, addr, data, size) + == size); + } +#endif + return -ENODEV; +} + int nv_rdaux(struct nvkm_i2c_port *port, u32 addr, u8 *data, u8 size) { struct nvkm_i2c *i2c = nvkm_i2c(port); + + if (drm_rdaux(port, addr, data, size) == 0) + return 0; + + nv_debug(port, "Try reading DPCD directly: addr=0x%x size=%d\n", + addr, size); if (port->func->aux) { int ret = i2c->acquire(port, 0); if (ret == 0) {