--- /dev/null
+From 13a86519202c5d119d83640d6f781f3181205d2c Mon Sep 17 00:00:00 2001
+From: Ben Skeggs <bskeggs@redhat.com>
+Date: Wed, 19 Jul 2017 16:49:59 +1000
+Subject: drm/nouveau/i2c/gf119-: add support for address-only transactions
+
+From: Ben Skeggs <bskeggs@redhat.com>
+
+commit 13a86519202c5d119d83640d6f781f3181205d2c upstream.
+
+Since switching the I2C-over-AUX helpers, there have been regressions on
+some display combinations due to us not having support for "address only"
+transactions.
+
+This commits enables support for them for GF119 and newer.
+
+Earlier GPUs have been reverted to a custom I2C-over-AUX algorithm.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+Cc: Ilia Mirkin <imirkin@alum.mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/gpu/drm/nouveau/nouveau_connector.c | 2 -
+ drivers/gpu/drm/nouveau/nv50_display.c | 13 ++++++-
+ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild | 1
+ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c | 4 ++
+ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h | 6 +++
+ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c | 30 +++++++++++-------
+ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c | 35 +++++++++++++++++++++
+ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c | 5 +--
+ drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c | 4 +-
+ 9 files changed, 81 insertions(+), 19 deletions(-)
+
+--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
+@@ -1155,8 +1155,6 @@ nouveau_connector_aux_xfer(struct drm_dp
+ return -ENODEV;
+ if (WARN_ON(msg->size > 16))
+ return -E2BIG;
+- if (msg->size == 0)
+- return msg->size;
+
+ ret = nvkm_i2c_aux_acquire(aux);
+ if (ret)
+--- a/drivers/gpu/drm/nouveau/nv50_display.c
++++ b/drivers/gpu/drm/nouveau/nv50_display.c
+@@ -3618,15 +3618,24 @@ nv50_sor_create(struct drm_connector *co
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ if (dcbe->type == DCB_OUTPUT_DP) {
++ struct nv50_disp *disp = nv50_disp(encoder->dev);
+ struct nvkm_i2c_aux *aux =
+ nvkm_i2c_aux_find(i2c, dcbe->i2c_index);
+ if (aux) {
+- nv_encoder->i2c = &nv_connector->aux.ddc;
++ if (disp->disp->oclass < GF110_DISP) {
++ /* HW has no support for address-only
++ * transactions, so we're required to
++ * use custom I2C-over-AUX code.
++ */
++ nv_encoder->i2c = &aux->i2c;
++ } else {
++ nv_encoder->i2c = &nv_connector->aux.ddc;
++ }
+ nv_encoder->aux = aux;
+ }
+
+ /*TODO: Use DP Info Table to check for support. */
+- if (nv50_disp(encoder->dev)->disp->oclass >= GF110_DISP) {
++ if (disp->disp->oclass >= GF110_DISP) {
+ ret = nv50_mstm_new(nv_encoder, &nv_connector->aux, 16,
+ nv_connector->base.base.id,
+ &nv_encoder->dp.mstm);
+--- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild
++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild
+@@ -25,6 +25,7 @@ nvkm-y += nvkm/subdev/i2c/bit.o
+
+ nvkm-y += nvkm/subdev/i2c/aux.o
+ nvkm-y += nvkm/subdev/i2c/auxg94.o
++nvkm-y += nvkm/subdev/i2c/auxgf119.o
+ nvkm-y += nvkm/subdev/i2c/auxgm200.o
+
+ nvkm-y += nvkm/subdev/i2c/anx9805.o
+--- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c
++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c
+@@ -117,6 +117,10 @@ int
+ nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type,
+ u32 addr, u8 *data, u8 *size)
+ {
++ if (!*size && !aux->func->address_only) {
++ AUX_ERR(aux, "address-only transaction dropped");
++ return -ENOSYS;
++ }
+ return aux->func->xfer(aux, retry, type, addr, data, size);
+ }
+
+--- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h
++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h
+@@ -3,6 +3,7 @@
+ #include "pad.h"
+
+ struct nvkm_i2c_aux_func {
++ bool address_only;
+ int (*xfer)(struct nvkm_i2c_aux *, bool retry, u8 type,
+ u32 addr, u8 *data, u8 *size);
+ int (*lnk_ctl)(struct nvkm_i2c_aux *, int link_nr, int link_bw,
+@@ -17,7 +18,12 @@ void nvkm_i2c_aux_del(struct nvkm_i2c_au
+ int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *, bool retry, u8 type,
+ u32 addr, u8 *data, u8 *size);
+
++int g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *,
++ int, u8, struct nvkm_i2c_aux **);
++
+ int g94_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **);
++int g94_i2c_aux_xfer(struct nvkm_i2c_aux *, bool, u8, u32, u8 *, u8 *);
++int gf119_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **);
+ int gm200_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **);
+
+ #define AUX_MSG(b,l,f,a...) do { \
+--- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c
++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c
+@@ -72,7 +72,7 @@ g94_i2c_aux_init(struct g94_i2c_aux *aux
+ return 0;
+ }
+
+-static int
++int
+ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
+ u8 type, u32 addr, u8 *data, u8 *size)
+ {
+@@ -105,9 +105,9 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *ob
+ }
+
+ ctrl = nvkm_rd32(device, 0x00e4e4 + base);
+- ctrl &= ~0x0001f0ff;
++ ctrl &= ~0x0001f1ff;
+ ctrl |= type << 12;
+- ctrl |= *size - 1;
++ ctrl |= (*size ? (*size - 1) : 0x00000100);
+ nvkm_wr32(device, 0x00e4e0 + base, addr);
+
+ /* (maybe) retry transaction a number of times on failure... */
+@@ -160,14 +160,10 @@ out:
+ return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
+ }
+
+-static const struct nvkm_i2c_aux_func
+-g94_i2c_aux_func = {
+- .xfer = g94_i2c_aux_xfer,
+-};
+-
+ int
+-g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive,
+- struct nvkm_i2c_aux **paux)
++g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *func,
++ struct nvkm_i2c_pad *pad, int index, u8 drive,
++ struct nvkm_i2c_aux **paux)
+ {
+ struct g94_i2c_aux *aux;
+
+@@ -175,8 +171,20 @@ g94_i2c_aux_new(struct nvkm_i2c_pad *pad
+ return -ENOMEM;
+ *paux = &aux->base;
+
+- nvkm_i2c_aux_ctor(&g94_i2c_aux_func, pad, index, &aux->base);
++ nvkm_i2c_aux_ctor(func, pad, index, &aux->base);
+ aux->ch = drive;
+ aux->base.intr = 1 << aux->ch;
+ return 0;
+ }
++
++static const struct nvkm_i2c_aux_func
++g94_i2c_aux = {
++ .xfer = g94_i2c_aux_xfer,
++};
++
++int
++g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive,
++ struct nvkm_i2c_aux **paux)
++{
++ return g94_i2c_aux_new_(&g94_i2c_aux, pad, index, drive, paux);
++}
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c
+@@ -0,0 +1,35 @@
++/*
++ * Copyright 2017 Red Hat Inc.
++ *
++ * 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.
++ */
++#include "aux.h"
++
++static const struct nvkm_i2c_aux_func
++gf119_i2c_aux = {
++ .address_only = true,
++ .xfer = g94_i2c_aux_xfer,
++};
++
++int
++gf119_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive,
++ struct nvkm_i2c_aux **paux)
++{
++ return g94_i2c_aux_new_(&gf119_i2c_aux, pad, index, drive, paux);
++}
+--- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c
++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c
+@@ -105,9 +105,9 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *
+ }
+
+ ctrl = nvkm_rd32(device, 0x00d954 + base);
+- ctrl &= ~0x0001f0ff;
++ ctrl &= ~0x0001f1ff;
+ ctrl |= type << 12;
+- ctrl |= *size - 1;
++ ctrl |= (*size ? (*size - 1) : 0x00000100);
+ nvkm_wr32(device, 0x00d950 + base, addr);
+
+ /* (maybe) retry transaction a number of times on failure... */
+@@ -162,6 +162,7 @@ out:
+
+ static const struct nvkm_i2c_aux_func
+ gm200_i2c_aux_func = {
++ .address_only = true,
+ .xfer = gm200_i2c_aux_xfer,
+ };
+
+--- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c
++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c
+@@ -28,7 +28,7 @@
+ static const struct nvkm_i2c_pad_func
+ gf119_i2c_pad_s_func = {
+ .bus_new_4 = gf119_i2c_bus_new,
+- .aux_new_6 = g94_i2c_aux_new,
++ .aux_new_6 = gf119_i2c_aux_new,
+ .mode = g94_i2c_pad_mode,
+ };
+
+@@ -41,7 +41,7 @@ gf119_i2c_pad_s_new(struct nvkm_i2c *i2c
+ static const struct nvkm_i2c_pad_func
+ gf119_i2c_pad_x_func = {
+ .bus_new_4 = gf119_i2c_bus_new,
+- .aux_new_6 = g94_i2c_aux_new,
++ .aux_new_6 = gf119_i2c_aux_new,
+ };
+
+ int