The diff below adopts more of the Linux code to manage i2c
transactions on hardware supported by inteldrm(4).  The i2c stuff is
reponsible for detecting panels and monitors, so it is somewhat
important that this works right.  And the Linux code developed some
quirks over the years that my rewrite of the code to use OpenBSD APIs
didn't have.

So I'm looking for testers.  I'm especially interested in tests of
external displays on all sorts of connector types (VGA, DVI, HDMI,
DP).  It would be really great to get some tests on older stuff with
(S)DVO.  Please let me know if there are regressions or if this fixes
things that are currently broken.  But all reports are welcome.
Please include a dmesg and some information about the display and
connector type.


Index: dev/pci/drm/drm_linux.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/drm_linux.c,v
retrieving revision 1.17
diff -u -p -r1.17 drm_linux.c
--- dev/pci/drm/drm_linux.c     13 Jan 2018 13:03:42 -0000      1.17
+++ dev/pci/drm/drm_linux.c     14 Jan 2018 23:40:01 -0000
@@ -552,16 +552,13 @@ sg_copy_from_buffer(struct scatterlist *
 }
 
 int
-i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
        void *cmd = NULL;
        int cmdlen = 0;
        int err, ret = 0;
        int op;
 
-       if (adap->algo)
-               return adap->algo->master_xfer(adap, msgs, num);
-
        iic_acquire_bus(&adap->ic, 0);
 
        while (num > 2) {
@@ -585,8 +582,10 @@ i2c_transfer(struct i2c_adapter *adap, s
                ret++;
        }
 
-       op = (msgs->flags & I2C_M_RD) ? I2C_OP_READ_WITH_STOP : 
I2C_OP_WRITE_WITH_STOP;
-       err = iic_exec(&adap->ic, op, msgs->addr, cmd, cmdlen, msgs->buf, 
msgs->len, 0);
+       op = (msgs->flags & I2C_M_RD) ?
+           I2C_OP_READ_WITH_STOP : I2C_OP_WRITE_WITH_STOP;
+       err = iic_exec(&adap->ic, op, msgs->addr, cmd, cmdlen,
+           msgs->buf, msgs->len, 0);
        if (err) {
                ret = -err;
                goto fail;
@@ -599,6 +598,38 @@ fail:
 
        return ret;
 }
+
+int
+i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       if (adap->algo)
+               return adap->algo->master_xfer(adap, msgs, num);
+
+       return i2c_master_xfer(adap, msgs, num);
+}
+
+int
+i2c_bb_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+       struct i2c_algo_bit_data *algo = adap->algo_data;
+       struct i2c_adapter bb;
+
+       memset(&bb, 0, sizeof(bb));
+       bb.ic = algo->ic;
+       bb.retries = adap->retries;
+       return i2c_master_xfer(&bb, msgs, num);
+}
+
+uint32_t
+i2c_bb_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+struct i2c_algorithm i2c_bit_algo = {
+       .master_xfer = i2c_bb_master_xfer,
+       .functionality = i2c_bb_functionality
+};
 
 #if defined(__amd64__) || defined(__i386__)
 
Index: dev/pci/drm/drm_linux.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/drm_linux.h,v
retrieving revision 1.66
diff -u -p -r1.66 drm_linux.h
--- dev/pci/drm/drm_linux.h     13 Jan 2018 14:15:07 -0000      1.66
+++ dev/pci/drm/drm_linux.h     14 Jan 2018 23:40:01 -0000
@@ -1199,6 +1199,11 @@ finish_wait(wait_queue_head_t *wq, wait_
 static inline long
 schedule_timeout(long timeout, wait_queue_head_t **wait)
 {
+       if (cold) {
+               delay((timeout * 1000000) / hz);
+               return -ETIMEDOUT;
+       }
+
        return -msleep(*wait, &(*wait)->lock, PZERO, "schto", timeout);
 }
 
@@ -1609,8 +1614,14 @@ struct i2c_msg {
 #define I2C_M_NOSTART  0x0002
 
 struct i2c_algorithm {
-       u32 (*functionality)(struct i2c_adapter *);
        int (*master_xfer)(struct i2c_adapter *, struct i2c_msg *, int);
+       u32 (*functionality)(struct i2c_adapter *);
+};
+
+extern struct i2c_algorithm i2c_bit_algo;
+
+struct i2c_algo_bit_data {
+       struct i2c_controller ic;
 };
 
 int i2c_transfer(struct i2c_adapter *, struct i2c_msg *, int);
Index: dev/pci/drm/i915/i915_drv.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_drv.h,v
retrieving revision 1.78
diff -u -p -r1.78 i915_drv.h
--- dev/pci/drm/i915/i915_drv.h 30 Sep 2017 07:36:56 -0000      1.78
+++ dev/pci/drm/i915/i915_drv.h 14 Jan 2018 23:40:01 -0000
@@ -1102,9 +1102,7 @@ struct intel_gmbus {
        u32 force_bit;
        u32 reg0;
        u32 gpio_reg;
-#ifdef __linux__
        struct i2c_algo_bit_data bit_algo;
-#endif
        struct drm_i915_private *dev_priv;
 };
 
Index: dev/pci/drm/i915/intel_i2c.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/intel_i2c.c,v
retrieving revision 1.12
diff -u -p -r1.12 intel_i2c.c
--- dev/pci/drm/i915/intel_i2c.c        30 Sep 2017 07:36:56 -0000      1.12
+++ dev/pci/drm/i915/intel_i2c.c        14 Jan 2018 23:40:01 -0000
@@ -1,19 +1,3 @@
-/*     $OpenBSD: intel_i2c.c,v 1.12 2017/09/30 07:36:56 robert Exp $   */
-/*
- * Copyright (c) 2012, 2013 Mark Kettenis <kette...@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
 /*
  * Copyright (c) 2006 Dave Airlie <airl...@linux.ie>
  * Copyright © 2006-2008,2010 Intel Corporation
@@ -42,36 +26,19 @@
  *     Eric Anholt <e...@anholt.net>
  *     Chris Wilson <ch...@chris-wilson.co.uk>
  */
-
+#ifdef __linux__
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/export.h>
+#endif
 #include <dev/pci/drm/drmP.h>
-#include <dev/pci/drm/drm.h>
 #include "intel_drv.h"
+#include <dev/pci/drm/i915_drm.h>
 #include "i915_drv.h"
-#include "i915_reg.h"
 
 #include <dev/i2c/i2cvar.h>
 #include <dev/i2c/i2c_bitbang.h>
 
-int    intel_gmbus_acquire_bus(void *, int);
-void   intel_gmbus_release_bus(void *, int);
-int    intel_gmbus_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
-           void *buf, size_t, int);
-
-void   intel_gpio_bb_set_bits(void *, uint32_t);
-void   intel_gpio_bb_set_dir(void *, uint32_t);
-uint32_t intel_gpio_bb_read_bits(void *);
-
-int    intel_gpio_send_start(void *, int);
-int    intel_gpio_send_stop(void *, int);
-int    intel_gpio_initiate_xfer(void *, i2c_addr_t, int);
-int    intel_gpio_read_byte(void *, u_int8_t *, int);
-int    intel_gpio_write_byte(void *, u_int8_t, int);
-
-enum disp_clk {
-       CDCLK,
-       CZCLK
-};
-
 struct gmbus_pin {
        const char *name;
        int reg;
@@ -156,242 +123,620 @@ intel_i2c_reset(struct drm_device *dev)
        I915_WRITE(GMBUS4, 0);
 }
 
-int
-intel_gmbus_acquire_bus(void *cookie, int flags)
+static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
 {
-       struct intel_gmbus *bus = cookie;
-       struct inteldrm_softc *dev_priv = bus->dev_priv;
-
-       intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+       u32 val;
 
-       I915_WRITE(GMBUS0, bus->reg0);
+       /* When using bit bashing for I2C, this bit needs to be set to 1 */
+       if (!IS_PINEVIEW(dev_priv->dev))
+               return;
 
-       return (0);
+       val = I915_READ(DSPCLK_GATE_D);
+       if (enable)
+               val |= DPCUNIT_CLOCK_GATE_DISABLE;
+       else
+               val &= ~DPCUNIT_CLOCK_GATE_DISABLE;
+       I915_WRITE(DSPCLK_GATE_D, val);
 }
 
-void
-intel_gmbus_release_bus(void *cookie, int flags)
+static u32 get_reserved(struct intel_gmbus *bus)
 {
-       struct intel_gmbus *bus = cookie;
-       struct inteldrm_softc *dev_priv = bus->dev_priv;
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       struct drm_device *dev = dev_priv->dev;
+       u32 reserved = 0;
 
-       I915_WRITE(GMBUS0, 0);
+       /* On most chips, these bits must be preserved in software. */
+       if (!IS_I830(dev) && !IS_845G(dev))
+               reserved = I915_READ_NOTRACE(bus->gpio_reg) &
+                                            (GPIO_DATA_PULLUP_DISABLE |
+                                             GPIO_CLOCK_PULLUP_DISABLE);
 
-       intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+       return reserved;
 }
 
-int
-intel_gmbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
-    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
+static int get_clock(void *data)
 {
-       struct intel_gmbus *bus = cookie;
-       struct inteldrm_softc *dev_priv = bus->dev_priv;
-       uint32_t reg, st, val;
-       uint8_t *b;
-       int i, retries;
-       uint16_t rem = len;
-       int bus_err = 0;
-
-       if (cmdlen > 1)
-               return (EOPNOTSUPP);
-
-       if (I2C_OP_WRITE_P(op)) {
-               val = 0;        
-
-               b = buf;
-               for (i = 0; i < 4 && rem > 0; i++, rem--) {
-                       val |= *b++ << (8 * i);
-               }
+       struct intel_gmbus *bus = data;
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       u32 reserved = get_reserved(bus);
+       I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_CLOCK_DIR_MASK);
+       I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+       return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_CLOCK_VAL_IN) != 0;
+}
 
-               I915_WRITE(GMBUS3, val);
-       }
+static int get_data(void *data)
+{
+       struct intel_gmbus *bus = data;
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       u32 reserved = get_reserved(bus);
+       I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_DATA_DIR_MASK);
+       I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+       return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_DATA_VAL_IN) != 0;
+}
+
+static void set_clock(void *data, int state_high)
+{
+       struct intel_gmbus *bus = data;
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       u32 reserved = get_reserved(bus);
+       u32 clock_bits;
 
-       reg = 0;
-       if (len > 0)
-               reg |= GMBUS_CYCLE_WAIT;
-       if (I2C_OP_STOP_P(op))
-               reg |= GMBUS_CYCLE_STOP;
-       if (I2C_OP_READ_P(op))
-               reg |= GMBUS_SLAVE_READ;
+       if (state_high)
+               clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
        else
-               reg |= GMBUS_SLAVE_WRITE;
-       if (cmdlen > 0) {
-               reg |= GMBUS_CYCLE_INDEX;
-               b = (void *)cmdbuf;
-               reg |= (b[0] << GMBUS_SLAVE_INDEX_SHIFT);
-       }
-       reg |= (addr << GMBUS_SLAVE_ADDR_SHIFT);
-       reg |= (len << GMBUS_BYTE_COUNT_SHIFT);
-       I915_WRITE(GMBUS1, reg | GMBUS_SW_RDY);
-
-       if (I2C_OP_READ_P(op)) {
-               b = buf;
-               while (len > 0) {
-                       for (retries = 50; retries > 0; retries--) {
-                               st = I915_READ(GMBUS2);
-                               if (st & (GMBUS_SATOER | GMBUS_HW_RDY))
-                                       break;
-                               DELAY(1000);
-                       }
-                       if (st & GMBUS_SATOER) {
-                               bus_err = 1;
-                               goto out;
-                       }
-                       if ((st & GMBUS_HW_RDY) == 0)
-                               return (ETIMEDOUT);
-
-                       val = I915_READ(GMBUS3);
-                       for (i = 0; i < 4 && len > 0; i++, len--) {
-                               *b++ = val & 0xff;
-                               val >>= 8;
-                       }
-               }
-       }
+               clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
+                       GPIO_CLOCK_VAL_MASK;
 
-       if (I2C_OP_WRITE_P(op)) {
-               while (rem > 0) {
-                       for (retries = 50; retries > 0; retries--) {
-                               st = I915_READ(GMBUS2);
-                               if (st & (GMBUS_SATOER | GMBUS_HW_RDY))
-                                       break;
-                               DELAY(1000);
-                       }
-                       if (st & GMBUS_SATOER) {
-                               bus_err = 1;
-                               goto out;
-                       }
-                       if ((st & GMBUS_HW_RDY) == 0)
-                               return (ETIMEDOUT);
-
-                       val = 0;
-                       for (i = 0; i < 4 && rem > 0; i++, rem--) {
-                               val |= *b++ << (8 * i);
-                       }
-                       I915_WRITE(GMBUS3, val);
-               }
-       }
+       I915_WRITE_NOTRACE(bus->gpio_reg, reserved | clock_bits);
+       POSTING_READ(bus->gpio_reg);
+}
 
-out:
-       if (I2C_OP_STOP_P(op)) {
-               for (retries = 10; retries > 0; retries--) {
-                       st = I915_READ(GMBUS2);
-                       if (st & GMBUS_SATOER)
-                               bus_err = 1;
-                       if ((st & GMBUS_ACTIVE) == 0)
-                               break;
-                       DELAY(1000);
-               }
-               if (st & GMBUS_ACTIVE)
-                       return (ETIMEDOUT);
-       } else {
-               for (retries = 10; retries > 0; retries--) {
-                       st = I915_READ(GMBUS2);
-                       if (st & GMBUS_SATOER)
-                               bus_err = 1;
-                       if (st & GMBUS_HW_WAIT_PHASE)
-                               break;
-                       DELAY(1000);
-               }
-               if ((st & GMBUS_HW_WAIT_PHASE) == 0)
-                       return (ETIMEDOUT);
-       }
+static void set_data(void *data, int state_high)
+{
+       struct intel_gmbus *bus = data;
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       u32 reserved = get_reserved(bus);
+       u32 data_bits;
 
-       /* after the bus is idle clear the bus error */
-       if (bus_err) {
-               I915_WRITE(GMBUS1, GMBUS_SW_CLR_INT);
-               I915_WRITE(GMBUS1, 0);
-               I915_WRITE(GMBUS0, 0);
-               return (ENXIO);
-       }
+       if (state_high)
+               data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
+       else
+               data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
+                       GPIO_DATA_VAL_MASK;
 
-       return (0);
+       I915_WRITE_NOTRACE(bus->gpio_reg, reserved | data_bits);
+       POSTING_READ(bus->gpio_reg);
 }
 
-struct i2c_bitbang_ops intel_gpio_bbops = {
-       intel_gpio_bb_set_bits,
-       intel_gpio_bb_set_dir,
-       intel_gpio_bb_read_bits,
-       { GPIO_DATA_VAL_IN, GPIO_CLOCK_VAL_IN,
-         GPIO_DATA_DIR_OUT, GPIO_DATA_DIR_IN }
+static int
+intel_gpio_pre_xfer(struct i2c_adapter *adapter)
+{
+       struct intel_gmbus *bus = container_of(adapter,
+                                              struct intel_gmbus,
+                                              adapter);
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+
+       intel_i2c_reset(dev_priv->dev);
+       intel_i2c_quirk_set(dev_priv, true);
+       set_data(bus, 1);
+       set_clock(bus, 1);
+       udelay(I2C_RISEFALL_TIME);
+       return 0;
+}
+
+static void
+intel_gpio_post_xfer(struct i2c_adapter *adapter)
+{
+       struct intel_gmbus *bus = container_of(adapter,
+                                              struct intel_gmbus,
+                                              adapter);
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+
+       set_data(bus, 1);
+       set_clock(bus, 1);
+       intel_i2c_quirk_set(dev_priv, false);
+}
+
+void   intel_bb_set_bits(void *, uint32_t);
+void   intel_bb_set_dir(void *, uint32_t);
+uint32_t intel_bb_read_bits(void *);
+
+int    intel_acquire_bus(void *, int);
+void   intel_release_bus(void *, int);
+int    intel_send_start(void *, int);
+int    intel_send_stop(void *, int);
+int    intel_initiate_xfer(void *, i2c_addr_t, int);
+int    intel_read_byte(void *, u_int8_t *, int);
+int    intel_write_byte(void *, u_int8_t, int);
+
+#define INTEL_BB_SDA           (1 << I2C_BIT_SDA)
+#define INTEL_BB_SCL           (1 << I2C_BIT_SCL)
+
+struct i2c_bitbang_ops intel_bbops = {
+       intel_bb_set_bits,
+       intel_bb_set_dir,
+       intel_bb_read_bits,
+       { INTEL_BB_SDA, INTEL_BB_SCL, 0, 0 }
 };
 
 void
-intel_gpio_bb_set_bits(void *cookie, uint32_t bits)
+intel_bb_set_bits(void *cookie, uint32_t bits)
 {
-       struct intel_gmbus *bus = cookie;
-       struct inteldrm_softc *dev_priv = bus->dev_priv;
-       uint32_t reg;
-
-       reg = I915_READ_NOTRACE(bus->gpio_reg);
-       reg &= (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE);
-       if (bits & GPIO_DATA_VAL_IN)
-               reg |= GPIO_DATA_VAL_OUT;
-       if (bits & GPIO_CLOCK_VAL_IN)
-               reg |= GPIO_CLOCK_VAL_OUT;
-       reg |= (GPIO_DATA_VAL_MASK | GPIO_CLOCK_VAL_MASK);
-       I915_WRITE_NOTRACE(bus->gpio_reg, reg);
+       set_clock(cookie, bits & INTEL_BB_SCL);
+       set_data(cookie, bits & INTEL_BB_SDA);
 }
 
 void
-intel_gpio_bb_set_dir(void *cookie, uint32_t bits)
+intel_bb_set_dir(void *cookie, uint32_t bits)
+{
+}
+
+uint32_t
+intel_bb_read_bits(void *cookie)
+{
+       uint32_t bits = 0;
+
+       if (get_clock(cookie))
+               bits |= INTEL_BB_SCL;
+       if (get_data(cookie))
+               bits |= INTEL_BB_SDA;
+
+       return bits;
+}
+
+int
+intel_acquire_bus(void *cookie, int flags)
 {
        struct intel_gmbus *bus = cookie;
-       struct inteldrm_softc *dev_priv = bus->dev_priv;
-       uint32_t reg;
 
-       reg = I915_READ_NOTRACE(bus->gpio_reg);
-       reg &= (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE);
-       if (bits & GPIO_DATA_DIR_OUT)
-               reg |= GPIO_DATA_DIR_OUT;
-       reg |= (GPIO_DATA_DIR_MASK | GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK);
-       I915_WRITE_NOTRACE(bus->gpio_reg, reg);
+       intel_gpio_pre_xfer(&bus->adapter);
+       return (0);
 }
 
-uint32_t
-intel_gpio_bb_read_bits(void *cookie)
+void
+intel_release_bus(void *cookie, int flags)
 {
        struct intel_gmbus *bus = cookie;
-       struct inteldrm_softc *dev_priv = bus->dev_priv;
 
-       return I915_READ_NOTRACE(bus->gpio_reg);
+       intel_gpio_post_xfer(&bus->adapter);
 }
 
 int
-intel_gpio_send_start(void *cookie, int flags)
+intel_send_start(void *cookie, int flags)
 {
-       return (i2c_bitbang_send_start(cookie, flags, &intel_gpio_bbops));
+       return (i2c_bitbang_send_start(cookie, flags, &intel_bbops));
 }
 
 int
-intel_gpio_send_stop(void *cookie, int flags)
+intel_send_stop(void *cookie, int flags)
 {
-       return (i2c_bitbang_send_stop(cookie, flags, &intel_gpio_bbops));
+       return (i2c_bitbang_send_stop(cookie, flags, &intel_bbops));
 }
 
 int
-intel_gpio_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
+intel_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
 {
-       return (i2c_bitbang_initiate_xfer(cookie, addr, flags, 
&intel_gpio_bbops));
+       return (i2c_bitbang_initiate_xfer(cookie, addr, flags, &intel_bbops));
 }
 
 int
-intel_gpio_read_byte(void *cookie, u_int8_t *bytep, int flags)
+intel_read_byte(void *cookie, u_int8_t *bytep, int flags)
 {
-       return (i2c_bitbang_read_byte(cookie, bytep, flags, &intel_gpio_bbops));
+       return (i2c_bitbang_read_byte(cookie, bytep, flags, &intel_bbops));
 }
 
 int
-intel_gpio_write_byte(void *cookie, u_int8_t byte, int flags)
+intel_write_byte(void *cookie, u_int8_t byte, int flags)
 {
-       return (i2c_bitbang_write_byte(cookie, byte, flags, &intel_gpio_bbops));
+       return (i2c_bitbang_write_byte(cookie, byte, flags, &intel_bbops));
 }
 
-int
-intel_setup_gmbus(struct drm_device *dev)
+static void
+intel_gpio_setup(struct intel_gmbus *bus, unsigned int pin)
+{
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       struct i2c_algo_bit_data *algo;
+
+       algo = &bus->bit_algo;
+
+       bus->gpio_reg = dev_priv->gpio_mmio_base +
+               get_gmbus_pin(dev_priv, pin)->reg;
+
+       bus->adapter.algo_data = algo;
+#ifdef __linux__
+       algo->setsda = set_data;
+       algo->setscl = set_clock;
+       algo->getsda = get_data;
+       algo->getscl = get_clock;
+       algo->pre_xfer = intel_gpio_pre_xfer;
+       algo->post_xfer = intel_gpio_post_xfer;
+       algo->udelay = I2C_RISEFALL_TIME;
+       algo->timeout = usecs_to_jiffies(2200);
+       algo->data = bus;
+#else
+       algo->ic.ic_cookie = bus;
+       algo->ic.ic_acquire_bus = intel_acquire_bus;
+       algo->ic.ic_release_bus = intel_release_bus;
+       algo->ic.ic_send_start = intel_send_start;
+       algo->ic.ic_send_stop = intel_send_stop;
+       algo->ic.ic_initiate_xfer = intel_initiate_xfer;
+       algo->ic.ic_read_byte = intel_read_byte;
+       algo->ic.ic_write_byte = intel_write_byte;
+#endif
+}
+
+static int
+gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
+                    u32 gmbus2_status,
+                    u32 gmbus4_irq_en)
+{
+       int i;
+       u32 gmbus2 = 0;
+       DEFINE_WAIT(wait);
+
+       if (!HAS_GMBUS_IRQ(dev_priv->dev) || cold)
+               gmbus4_irq_en = 0;
+
+       /* Important: The hw handles only the first bit, so set only one! Since
+        * we also need to check for NAKs besides the hw ready/idle signal, we
+        * need to wake up periodically and check that ourselves. */
+       I915_WRITE(GMBUS4, gmbus4_irq_en);
+
+       for (i = 0; i < msecs_to_jiffies_timeout(50); i++) {
+               prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait,
+                               TASK_UNINTERRUPTIBLE);
+
+               gmbus2 = I915_READ_NOTRACE(GMBUS2);
+               if (gmbus2 & (GMBUS_SATOER | gmbus2_status))
+                       break;
+
+               schedule_timeout(1, &wait);
+       }
+       finish_wait(&dev_priv->gmbus_wait_queue, &wait);
+
+       I915_WRITE(GMBUS4, 0);
+
+       if (gmbus2 & GMBUS_SATOER)
+               return -ENXIO;
+       if (gmbus2 & gmbus2_status)
+               return 0;
+       return -ETIMEDOUT;
+}
+
+static int
+gmbus_wait_idle(struct drm_i915_private *dev_priv)
+{
+       int ret;
+
+#define C ((I915_READ_NOTRACE(GMBUS2) & GMBUS_ACTIVE) == 0)
+
+       if (!HAS_GMBUS_IRQ(dev_priv->dev) || cold)
+               return wait_for(C, 10);
+
+       /* Important: The hw handles only the first bit, so set only one! */
+       I915_WRITE(GMBUS4, GMBUS_IDLE_EN);
+
+       ret = wait_event_timeout(dev_priv->gmbus_wait_queue, C,
+                                msecs_to_jiffies_timeout(10));
+
+       I915_WRITE(GMBUS4, 0);
+
+       if (ret)
+               return 0;
+       else
+               return -ETIMEDOUT;
+#undef C
+}
+
+static int
+gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv,
+                     unsigned short addr, u8 *buf, unsigned int len,
+                     u32 gmbus1_index)
+{
+       I915_WRITE(GMBUS1,
+                  gmbus1_index |
+                  GMBUS_CYCLE_WAIT |
+                  (len << GMBUS_BYTE_COUNT_SHIFT) |
+                  (addr << GMBUS_SLAVE_ADDR_SHIFT) |
+                  GMBUS_SLAVE_READ | GMBUS_SW_RDY);
+       while (len) {
+               int ret;
+               u32 val, loop = 0;
+
+               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
+                                          GMBUS_HW_RDY_EN);
+               if (ret)
+                       return ret;
+
+               val = I915_READ(GMBUS3);
+               do {
+                       *buf++ = val & 0xff;
+                       val >>= 8;
+               } while (--len && ++loop < 4);
+       }
+
+       return 0;
+}
+
+static int
+gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
+               u32 gmbus1_index)
+{
+       u8 *buf = msg->buf;
+       unsigned int rx_size = msg->len;
+       unsigned int len;
+       int ret;
+
+       do {
+               len = min(rx_size, GMBUS_BYTE_COUNT_MAX);
+
+               ret = gmbus_xfer_read_chunk(dev_priv, msg->addr,
+                                           buf, len, gmbus1_index);
+               if (ret)
+                       return ret;
+
+               rx_size -= len;
+               buf += len;
+       } while (rx_size != 0);
+
+       return 0;
+}
+
+static int
+gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
+                      unsigned short addr, u8 *buf, unsigned int len)
+{
+       unsigned int chunk_size = len;
+       u32 val, loop;
+
+       val = loop = 0;
+       while (len && loop < 4) {
+               val |= *buf++ << (8 * loop++);
+               len -= 1;
+       }
+
+       I915_WRITE(GMBUS3, val);
+       I915_WRITE(GMBUS1,
+                  GMBUS_CYCLE_WAIT |
+                  (chunk_size << GMBUS_BYTE_COUNT_SHIFT) |
+                  (addr << GMBUS_SLAVE_ADDR_SHIFT) |
+                  GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
+       while (len) {
+               int ret;
+
+               val = loop = 0;
+               do {
+                       val |= *buf++ << (8 * loop);
+               } while (--len && ++loop < 4);
+
+               I915_WRITE(GMBUS3, val);
+
+               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
+                                          GMBUS_HW_RDY_EN);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int
+gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
+{
+       u8 *buf = msg->buf;
+       unsigned int tx_size = msg->len;
+       unsigned int len;
+       int ret;
+
+       do {
+               len = min(tx_size, GMBUS_BYTE_COUNT_MAX);
+
+               ret = gmbus_xfer_write_chunk(dev_priv, msg->addr, buf, len);
+               if (ret)
+                       return ret;
+
+               buf += len;
+               tx_size -= len;
+       } while (tx_size != 0);
+
+       return 0;
+}
+
+/*
+ * The gmbus controller can combine a 1 or 2 byte write with a read that
+ * immediately follows it by using an "INDEX" cycle.
+ */
+static bool
+gmbus_is_index_read(struct i2c_msg *msgs, int i, int num)
+{
+       return (i + 1 < num &&
+               msgs[i].addr == msgs[i + 1].addr &&
+               !(msgs[i].flags & I2C_M_RD) &&
+               (msgs[i].len == 1 || msgs[i].len == 2) &&
+               (msgs[i + 1].flags & I2C_M_RD));
+}
+
+static int
+gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
+{
+       u32 gmbus1_index = 0;
+       u32 gmbus5 = 0;
+       int ret;
+
+       if (msgs[0].len == 2)
+               gmbus5 = GMBUS_2BYTE_INDEX_EN |
+                        msgs[0].buf[1] | (msgs[0].buf[0] << 8);
+       if (msgs[0].len == 1)
+               gmbus1_index = GMBUS_CYCLE_INDEX |
+                              (msgs[0].buf[0] << GMBUS_SLAVE_INDEX_SHIFT);
+
+       /* GMBUS5 holds 16-bit index */
+       if (gmbus5)
+               I915_WRITE(GMBUS5, gmbus5);
+
+       ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index);
+
+       /* Clear GMBUS5 after each index transfer */
+       if (gmbus5)
+               I915_WRITE(GMBUS5, 0);
+
+       return ret;
+}
+
+static int
+gmbus_xfer(struct i2c_adapter *adapter,
+          struct i2c_msg *msgs,
+          int num)
+{
+       struct intel_gmbus *bus = container_of(adapter,
+                                              struct intel_gmbus,
+                                              adapter);
+       struct drm_i915_private *dev_priv = bus->dev_priv;
+       int i = 0, inc, try = 0;
+       int ret = 0;
+
+       intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+       mutex_lock(&dev_priv->gmbus_mutex);
+
+       if (bus->force_bit) {
+               ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
+               goto out;
+       }
+
+retry:
+       I915_WRITE(GMBUS0, bus->reg0);
+
+       for (; i < num; i += inc) {
+               inc = 1;
+               if (gmbus_is_index_read(msgs, i, num)) {
+                       ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
+                       inc = 2; /* an index read is two msgs */
+               } else if (msgs[i].flags & I2C_M_RD) {
+                       ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
+               } else {
+                       ret = gmbus_xfer_write(dev_priv, &msgs[i]);
+               }
+
+               if (ret == -ETIMEDOUT)
+                       goto timeout;
+               if (ret == -ENXIO)
+                       goto clear_err;
+
+               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
+                                          GMBUS_HW_WAIT_EN);
+               if (ret == -ENXIO)
+                       goto clear_err;
+               if (ret)
+                       goto timeout;
+       }
+
+       /* Generate a STOP condition on the bus. Note that gmbus can't generata
+        * a STOP on the very first cycle. To simplify the code we
+        * unconditionally generate the STOP condition with an additional gmbus
+        * cycle. */
+       I915_WRITE(GMBUS1, GMBUS_CYCLE_STOP | GMBUS_SW_RDY);
+
+       /* Mark the GMBUS interface as disabled after waiting for idle.
+        * We will re-enable it at the start of the next xfer,
+        * till then let it sleep.
+        */
+       if (gmbus_wait_idle(dev_priv)) {
+               DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n",
+                        adapter->name);
+               ret = -ETIMEDOUT;
+       }
+       I915_WRITE(GMBUS0, 0);
+       ret = ret ?: i;
+       goto out;
+
+clear_err:
+       /*
+        * Wait for bus to IDLE before clearing NAK.
+        * If we clear the NAK while bus is still active, then it will stay
+        * active and the next transaction may fail.
+        *
+        * If no ACK is received during the address phase of a transaction, the
+        * adapter must report -ENXIO. It is not clear what to return if no ACK
+        * is received at other times. But we have to be careful to not return
+        * spurious -ENXIO because that will prevent i2c and drm edid functions
+        * from retrying. So return -ENXIO only when gmbus properly quiescents -
+        * timing out seems to happen when there _is_ a ddc chip present, but
+        * it's slow responding and only answers on the 2nd retry.
+        */
+       ret = -ENXIO;
+       if (gmbus_wait_idle(dev_priv)) {
+               DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n",
+                             adapter->name);
+               ret = -ETIMEDOUT;
+       }
+
+       /* Toggle the Software Clear Interrupt bit. This has the effect
+        * of resetting the GMBUS controller and so clearing the
+        * BUS_ERROR raised by the slave's NAK.
+        */
+       I915_WRITE(GMBUS1, GMBUS_SW_CLR_INT);
+       I915_WRITE(GMBUS1, 0);
+       I915_WRITE(GMBUS0, 0);
+
+       DRM_DEBUG_KMS("GMBUS [%s] NAK for addr: %04x %c(%d)\n",
+                        adapter->name, msgs[i].addr,
+                        (msgs[i].flags & I2C_M_RD) ? 'r' : 'w', msgs[i].len);
+
+       /*
+        * Passive adapters sometimes NAK the first probe. Retry the first
+        * message once on -ENXIO for GMBUS transfers; the bit banging algorithm
+        * has retries internally. See also the retry loop in
+        * drm_do_probe_ddc_edid, which bails out on the first -ENXIO.
+        */
+       if (ret == -ENXIO && i == 0 && try++ == 0) {
+               DRM_DEBUG_KMS("GMBUS [%s] NAK on first message, retry\n",
+                             adapter->name);
+               goto retry;
+       }
+
+       goto out;
+
+timeout:
+       DRM_INFO("GMBUS [%s] timed out, falling back to bit banging on pin 
%d\n",
+                bus->adapter.name, bus->reg0 & 0xff);
+       I915_WRITE(GMBUS0, 0);
+
+       /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging 
instead. */
+       bus->force_bit = 1;
+       ret = i2c_bit_algo.master_xfer(adapter, msgs, num);
+
+out:
+       mutex_unlock(&dev_priv->gmbus_mutex);
+
+       intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+
+       return ret;
+}
+
+static u32 gmbus_func(struct i2c_adapter *adapter)
+{
+       return i2c_bit_algo.functionality(adapter) &
+               (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+               /* I2C_FUNC_10BIT_ADDR | */
+               I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+               I2C_FUNC_SMBUS_BLOCK_PROC_CALL);
+}
+
+static const struct i2c_algorithm gmbus_algorithm = {
+       .master_xfer    = gmbus_xfer,
+       .functionality  = gmbus_func
+};
+
+/**
+ * intel_gmbus_setup - instantiate all Intel i2c GMBuses
+ * @dev: DRM device
+ */
+int intel_setup_gmbus(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_gmbus *bus;
        unsigned int pin;
+       int ret;
 
        if (HAS_PCH_NOP(dev))
                return 0;
@@ -402,35 +747,58 @@ intel_setup_gmbus(struct drm_device *dev
        else
                dev_priv->gpio_mmio_base = 0;
 
+       rw_init(&dev_priv->gmbus_mutex, "gmbus");
+       init_waitqueue_head(&dev_priv->gmbus_wait_queue);
+
        for (pin = 0; pin < ARRAY_SIZE(dev_priv->gmbus); pin++) {
                if (!intel_gmbus_is_valid_pin(dev_priv, pin))
                        continue;
 
                bus = &dev_priv->gmbus[pin];
 
+#ifdef notyet
+               bus->adapter.owner = THIS_MODULE;
+               bus->adapter.class = I2C_CLASS_DDC;
+#endif
+               snprintf(bus->adapter.name,
+                        sizeof(bus->adapter.name),
+                        "i915 gmbus %s",
+                        get_gmbus_pin(dev_priv, pin)->name);
+
+#ifdef notyet
+               bus->adapter.dev.parent = &dev->pdev->dev;
+#endif
                bus->dev_priv = dev_priv;
 
+               bus->adapter.algo = &gmbus_algorithm;
+
                /* By default use a conservative clock rate */
                bus->reg0 = pin | GMBUS_RATE_100KHZ;
 
-               bus->adapter.ic.ic_cookie = bus;
-               bus->adapter.ic.ic_acquire_bus = intel_gmbus_acquire_bus;
-               bus->adapter.ic.ic_release_bus = intel_gmbus_release_bus;
-               bus->adapter.ic.ic_exec = intel_gmbus_exec;
-
-               bus->gpio_reg = dev_priv->gpio_mmio_base +
-                       get_gmbus_pin(dev_priv, pin)->reg;
-
-               bus->adapter.ic.ic_send_start = intel_gpio_send_start;
-               bus->adapter.ic.ic_send_stop = intel_gpio_send_stop;
-               bus->adapter.ic.ic_initiate_xfer = intel_gpio_initiate_xfer;
-               bus->adapter.ic.ic_read_byte = intel_gpio_read_byte;
-               bus->adapter.ic.ic_write_byte = intel_gpio_write_byte;
+               /* gmbus seems to be broken on i830 */
+               if (IS_I830(dev))
+                       bus->force_bit = 1;
+
+               intel_gpio_setup(bus, pin);
+
+               ret = i2c_add_adapter(&bus->adapter);
+               if (ret)
+                       goto err;
        }
 
        intel_i2c_reset(dev_priv->dev);
 
-       return (0);
+       return 0;
+
+err:
+       while (pin--) {
+               if (!intel_gmbus_is_valid_pin(dev_priv, pin))
+                       continue;
+
+               bus = &dev_priv->gmbus[pin];
+               i2c_del_adapter(&bus->adapter);
+       }
+       return ret;
 }
 
 struct i2c_adapter *intel_gmbus_get_adapter(struct drm_i915_private *dev_priv,
@@ -442,25 +810,34 @@ struct i2c_adapter *intel_gmbus_get_adap
        return &dev_priv->gmbus[pin].adapter;
 }
 
-void
-intel_teardown_gmbus(struct drm_device *dev)
+void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed)
 {
+       struct intel_gmbus *bus = to_intel_gmbus(adapter);
+
+       bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed;
 }
 
-void
-intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
+void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
 {
        struct intel_gmbus *bus = to_intel_gmbus(adapter);
 
-       if (force_bit)
-               bus->force_bit++;
-       else
-               bus->force_bit--;
+       bus->force_bit += force_bit ? 1 : -1;
+       DRM_DEBUG_KMS("%sabling bit-banging on %s. force bit now %d\n",
+                     force_bit ? "en" : "dis", adapter->name,
+                     bus->force_bit);
+}
 
-       KASSERT(bus->force_bit >= 0);
+void intel_teardown_gmbus(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_gmbus *bus;
+       unsigned int pin;
 
-       if (bus->force_bit)
-               bus->adapter.ic.ic_exec = NULL;
-       else
-               bus->adapter.ic.ic_exec = intel_gmbus_exec;
+       for (pin = 0; pin < ARRAY_SIZE(dev_priv->gmbus); pin++) {
+               if (!intel_gmbus_is_valid_pin(dev_priv, pin))
+                       continue;
+
+               bus = &dev_priv->gmbus[pin];
+               i2c_del_adapter(&bus->adapter);
+       }
 }

Reply via email to