freebsd-skq/sys/dev/drm2/i915/intel_iic.c
dumbbell ca453b4fd5 drm/i915: Update to match Linux 3.8.13
This update brings initial support for Haswell GPUs.

Tested by:	Many users of FreeBSD, PC-BSD and HardenedBSD
Relnotes:	yes
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D5554
2016-03-08 20:33:02 +00:00

787 lines
20 KiB
C

/*
* Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
* Copyright © 2006-2008,2010 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
*
* 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 (including the next
* paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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:
* Eric Anholt <eric@anholt.net>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <dev/drm2/drmP.h>
#include <dev/drm2/i915/intel_drv.h>
#include <dev/drm2/i915/i915_drm.h>
#include <dev/drm2/i915/i915_drv.h>
#include <dev/iicbus/iic.h>
#include <dev/iicbus/iiconf.h>
#include <dev/iicbus/iicbus.h>
#include "iicbus_if.h"
#include "iicbb_if.h"
struct gmbus_port {
const char *name;
int reg;
};
static const struct gmbus_port gmbus_ports[] = {
{ "ssc", GPIOB },
{ "vga", GPIOA },
{ "panel", GPIOC },
{ "dpc", GPIOD },
{ "dpb", GPIOE },
{ "dpd", GPIOF },
};
/* Intel GPIO access functions */
#define I2C_RISEFALL_TIME 10
/*
* FIXME Linux<->FreeBSD: dvo_ns2501.C wants the struct intel_gmbus
* below but it just has the device_t at hand. It still uses
* device_get_softc(), thus expects struct intel_gmbus to remain the
* first member.
*/
struct intel_iic_softc {
struct intel_gmbus *bus;
device_t iic_dev;
char name[32];
};
static inline struct intel_gmbus *
to_intel_gmbus(device_t i2c)
{
struct intel_iic_softc *sc;
sc = device_get_softc(i2c);
return sc->bus;
}
bool intel_gmbus_is_forced_bit(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
return bus->force_bit;
}
void
intel_i2c_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
}
static int
intel_iicbus_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr)
{
struct intel_iic_softc *sc;
struct drm_device *dev;
sc = device_get_softc(idev);
dev = sc->bus->dev_priv->dev;
intel_i2c_reset(dev);
return (0);
}
static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
{
u32 val;
/* When using bit bashing for I2C, this bit needs to be set to 1 */
if (!IS_PINEVIEW(dev_priv->dev))
return;
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);
}
static u32 get_reserved(struct intel_gmbus *bus)
{
struct drm_i915_private *dev_priv = bus->dev_priv;
struct drm_device *dev = dev_priv->dev;
u32 reserved = 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);
return reserved;
}
static int get_clock(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
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;
}
static int get_data(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
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(device_t adapter, int state_high)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
u32 reserved = get_reserved(bus);
u32 clock_bits;
if (state_high)
clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
else
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
GPIO_CLOCK_VAL_MASK;
I915_WRITE_NOTRACE(bus->gpio_reg, reserved | clock_bits);
POSTING_READ(bus->gpio_reg);
}
static void set_data(device_t adapter, int state_high)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
u32 reserved = get_reserved(bus);
u32 data_bits;
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;
I915_WRITE_NOTRACE(bus->gpio_reg, reserved | data_bits);
POSTING_READ(bus->gpio_reg);
}
static int
intel_gpio_pre_xfer(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
intel_i2c_reset(dev_priv->dev);
intel_i2c_quirk_set(dev_priv, true);
IICBB_SETSDA(adapter, 1);
IICBB_SETSCL(adapter, 1);
udelay(I2C_RISEFALL_TIME);
return 0;
}
static void
intel_gpio_post_xfer(device_t adapter)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
IICBB_SETSDA(adapter, 1);
IICBB_SETSCL(adapter, 1);
intel_i2c_quirk_set(dev_priv, false);
}
static void
intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
{
struct drm_i915_private *dev_priv = bus->dev_priv;
/* -1 to map pin pair to gmbus index */
bus->gpio_reg = dev_priv->gpio_mmio_base + gmbus_ports[pin - 1].reg;
}
static int
gmbus_xfer_read(struct drm_i915_private *dev_priv, struct iic_msg *msg,
u32 gmbus1_index)
{
int reg_offset = dev_priv->gpio_mmio_base;
u16 len = msg->len;
u8 *buf = msg->buf;
I915_WRITE(GMBUS1 + reg_offset,
gmbus1_index |
GMBUS_CYCLE_WAIT |
(len << GMBUS_BYTE_COUNT_SHIFT) |
(msg->slave << (GMBUS_SLAVE_ADDR_SHIFT - 1)) |
GMBUS_SLAVE_READ | GMBUS_SW_RDY);
while (len) {
int ret;
u32 val, loop = 0;
u32 gmbus2;
ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_RDY),
50);
if (ret)
return -ETIMEDOUT;
if (gmbus2 & GMBUS_SATOER)
return -ENXIO;
val = I915_READ(GMBUS3 + reg_offset);
do {
*buf++ = val & 0xff;
val >>= 8;
} while (--len && ++loop < 4);
}
return 0;
}
static int
gmbus_xfer_write(struct drm_i915_private *dev_priv, struct iic_msg *msg)
{
int reg_offset = dev_priv->gpio_mmio_base;
u16 len = msg->len;
u8 *buf = msg->buf;
u32 val, loop;
val = loop = 0;
while (len && loop < 4) {
val |= *buf++ << (8 * loop++);
len -= 1;
}
I915_WRITE(GMBUS3 + reg_offset, val);
I915_WRITE(GMBUS1 + reg_offset,
GMBUS_CYCLE_WAIT |
(msg->len << GMBUS_BYTE_COUNT_SHIFT) |
(msg->slave << (GMBUS_SLAVE_ADDR_SHIFT - 1)) |
GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
while (len) {
int ret;
u32 gmbus2;
val = loop = 0;
do {
val |= *buf++ << (8 * loop);
} while (--len && ++loop < 4);
I915_WRITE(GMBUS3 + reg_offset, val);
ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_RDY),
50);
if (ret)
return -ETIMEDOUT;
if (gmbus2 & GMBUS_SATOER)
return -ENXIO;
}
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 iic_msg *msgs, int i, int num)
{
return (i + 1 < num &&
!(msgs[i].flags & I2C_M_RD) && msgs[i].len <= 2 &&
(msgs[i + 1].flags & I2C_M_RD));
}
static int
gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct iic_msg *msgs)
{
int reg_offset = dev_priv->gpio_mmio_base;
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 + reg_offset, gmbus5);
ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index);
/* Clear GMBUS5 after each index transfer */
if (gmbus5)
I915_WRITE(GMBUS5 + reg_offset, 0);
return ret;
}
static int
gmbus_xfer(device_t adapter,
struct iic_msg *msgs,
uint32_t num)
{
struct intel_iic_softc *sc = device_get_softc(adapter);
struct intel_gmbus *bus = sc->bus;
struct drm_i915_private *dev_priv = bus->dev_priv;
int i, reg_offset;
int ret = 0;
sx_xlock(&dev_priv->gmbus_mutex);
if (bus->force_bit) {
ret = -IICBUS_TRANSFER(bus->bbbus, msgs, num);
goto out;
}
reg_offset = dev_priv->gpio_mmio_base;
I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
for (i = 0; i < num; i++) {
u32 gmbus2;
if (gmbus_is_index_read(msgs, i, num)) {
ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
i += 1; /* set i to the index of the read xfer */
} 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 = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) &
(GMBUS_SATOER | GMBUS_HW_WAIT_PHASE),
50);
if (ret)
goto timeout;
if (gmbus2 & GMBUS_SATOER)
goto clear_err;
}
/* 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 + reg_offset, 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 (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
10)) {
DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n",
device_get_desc(adapter));
ret = -ETIMEDOUT;
}
I915_WRITE(GMBUS0 + reg_offset, 0);
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 (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0,
10)) {
DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n",
device_get_desc(adapter));
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 + reg_offset, GMBUS_SW_CLR_INT);
I915_WRITE(GMBUS1 + reg_offset, 0);
I915_WRITE(GMBUS0 + reg_offset, 0);
DRM_DEBUG_KMS("GMBUS [%s] NAK for addr: %04x %c(%d)\n",
device_get_desc(adapter), msgs[i].slave >> 1,
(msgs[i].flags & I2C_M_RD) ? 'r' : 'w', msgs[i].len);
goto out;
timeout:
DRM_INFO("GMBUS [%s] timed out, falling back to bit banging on pin %d\n",
device_get_desc(adapter), bus->reg0 & 0xff);
I915_WRITE(GMBUS0 + reg_offset, 0);
/* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
bus->force_bit = 1;
ret = -IICBUS_TRANSFER(bus->bbbus, msgs, num);
out:
sx_xunlock(&dev_priv->gmbus_mutex);
return -ret;
}
static int
intel_gmbus_probe(device_t dev)
{
return (BUS_PROBE_SPECIFIC);
}
static int
intel_gmbus_attach(device_t idev)
{
struct intel_iic_softc *sc;
struct drm_device *dev;
struct drm_i915_private *dev_priv;
int pin, port;
sc = device_get_softc(idev);
pin = device_get_unit(idev);
port = pin + 1; /* +1 to map gmbus index to pin pair */
snprintf(sc->name, sizeof(sc->name), "i915 gmbus %s",
intel_gmbus_is_port_valid(port) ? gmbus_ports[pin].name :
"reserved");
device_set_desc(idev, sc->name);
dev = device_get_softc(device_get_parent(idev));
dev_priv = dev->dev_private;
sc->bus = &dev_priv->gmbus[pin];
/* add bus interface device */
sc->iic_dev = device_add_child(idev, "iicbus", -1);
if (sc->iic_dev == NULL)
return (ENXIO);
device_quiet(sc->iic_dev);
bus_generic_attach(idev);
return (0);
}
static int
intel_gmbus_detach(device_t idev)
{
bus_generic_detach(idev);
device_delete_children(idev);
return (0);
}
static device_method_t intel_gmbus_methods[] = {
DEVMETHOD(device_probe, intel_gmbus_probe),
DEVMETHOD(device_attach, intel_gmbus_attach),
DEVMETHOD(device_detach, intel_gmbus_detach),
DEVMETHOD(iicbus_reset, intel_iicbus_reset),
DEVMETHOD(iicbus_transfer, gmbus_xfer),
DEVMETHOD_END
};
static driver_t intel_gmbus_driver = {
"intel_gmbus",
intel_gmbus_methods,
sizeof(struct intel_iic_softc)
};
static devclass_t intel_gmbus_devclass;
DRIVER_MODULE_ORDERED(intel_gmbus, drmn, intel_gmbus_driver,
intel_gmbus_devclass, 0, 0, SI_ORDER_FIRST);
DRIVER_MODULE(iicbus, intel_gmbus, iicbus_driver, iicbus_devclass, 0, 0);
static int
intel_iicbb_probe(device_t dev)
{
return (BUS_PROBE_DEFAULT);
}
static int
intel_iicbb_attach(device_t idev)
{
struct intel_iic_softc *sc;
struct drm_device *dev;
struct drm_i915_private *dev_priv;
int pin, port;
sc = device_get_softc(idev);
pin = device_get_unit(idev);
port = pin + 1;
snprintf(sc->name, sizeof(sc->name), "i915 iicbb %s",
intel_gmbus_is_port_valid(port) ? gmbus_ports[pin].name :
"reserved");
device_set_desc(idev, sc->name);
dev = device_get_softc(device_get_parent(idev));
dev_priv = dev->dev_private;
sc->bus = &dev_priv->gmbus[pin];
/* add generic bit-banging code */
sc->iic_dev = device_add_child(idev, "iicbb", -1);
if (sc->iic_dev == NULL)
return (ENXIO);
device_quiet(sc->iic_dev);
bus_generic_attach(idev);
iicbus_set_nostop(idev, true);
return (0);
}
static int
intel_iicbb_detach(device_t idev)
{
bus_generic_detach(idev);
device_delete_children(idev);
return (0);
}
static device_method_t intel_iicbb_methods[] = {
DEVMETHOD(device_probe, intel_iicbb_probe),
DEVMETHOD(device_attach, intel_iicbb_attach),
DEVMETHOD(device_detach, intel_iicbb_detach),
DEVMETHOD(bus_add_child, bus_generic_add_child),
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(iicbb_callback, iicbus_null_callback),
DEVMETHOD(iicbb_reset, intel_iicbus_reset),
DEVMETHOD(iicbb_setsda, set_data),
DEVMETHOD(iicbb_setscl, set_clock),
DEVMETHOD(iicbb_getsda, get_data),
DEVMETHOD(iicbb_getscl, get_clock),
DEVMETHOD(iicbb_pre_xfer, intel_gpio_pre_xfer),
DEVMETHOD(iicbb_post_xfer, intel_gpio_post_xfer),
DEVMETHOD_END
};
static driver_t intel_iicbb_driver = {
"intel_iicbb",
intel_iicbb_methods,
sizeof(struct intel_iic_softc)
};
static devclass_t intel_iicbb_devclass;
DRIVER_MODULE_ORDERED(intel_iicbb, drmn, intel_iicbb_driver,
intel_iicbb_devclass, 0, 0, SI_ORDER_FIRST);
DRIVER_MODULE(iicbb, intel_iicbb, iicbb_driver, iicbb_devclass, 0, 0);
/**
* 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;
device_t iic_dev;
int ret, i;
if (HAS_PCH_SPLIT(dev))
dev_priv->gpio_mmio_base = PCH_GPIOA - GPIOA;
else
dev_priv->gpio_mmio_base = 0;
sx_init(&dev_priv->gmbus_mutex, "gmbus");
/*
* The Giant there is recursed, most likely. Normally, the
* intel_setup_gmbus() is called from the attach method of the
* driver.
*/
mtx_lock(&Giant);
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
u32 port = i + 1; /* +1 to map gmbus index to pin pair */
bus->dev_priv = dev_priv;
/* By default use a conservative clock rate */
bus->reg0 = port | GMBUS_RATE_100KHZ;
/* gmbus seems to be broken on i830 */
if (IS_I830(dev))
bus->force_bit = 1;
intel_gpio_setup(bus, port);
/*
* bbbus_bridge
*
* Initialized bbbus_bridge before gmbus_bridge, since
* gmbus may decide to force quirk transfer in the
* attachment code.
*/
bus->bbbus_bridge = device_add_child(dev->dev,
"intel_iicbb", i);
if (bus->bbbus_bridge == NULL) {
DRM_ERROR("bbbus bridge %d creation failed\n", i);
ret = -ENXIO;
goto err;
}
device_quiet(bus->bbbus_bridge);
ret = -device_probe_and_attach(bus->bbbus_bridge);
if (ret != 0) {
DRM_ERROR("bbbus bridge %d attach failed, %d\n", i,
ret);
goto err;
}
/* bbbus */
iic_dev = device_find_child(bus->bbbus_bridge,
"iicbb", -1);
if (iic_dev == NULL) {
DRM_ERROR("bbbus bridge doesn't have iicbb child\n");
goto err;
}
iic_dev = device_find_child(iic_dev, "iicbus", -1);
if (iic_dev == NULL) {
DRM_ERROR(
"bbbus bridge doesn't have iicbus grandchild\n");
goto err;
}
bus->bbbus = iic_dev;
/* gmbus_bridge */
bus->gmbus_bridge = device_add_child(dev->dev,
"intel_gmbus", i);
if (bus->gmbus_bridge == NULL) {
DRM_ERROR("gmbus bridge %d creation failed\n", i);
ret = -ENXIO;
goto err;
}
device_quiet(bus->gmbus_bridge);
ret = -device_probe_and_attach(bus->gmbus_bridge);
if (ret != 0) {
DRM_ERROR("gmbus bridge %d attach failed, %d\n", i,
ret);
ret = -ENXIO;
goto err;
}
/* gmbus */
iic_dev = device_find_child(bus->gmbus_bridge,
"iicbus", -1);
if (iic_dev == NULL) {
DRM_ERROR("gmbus bridge doesn't have iicbus child\n");
goto err;
}
bus->gmbus = iic_dev;
}
mtx_unlock(&Giant);
intel_i2c_reset(dev_priv->dev);
return 0;
err:
while (--i) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
if (bus->gmbus_bridge != NULL)
device_delete_child(dev->dev, bus->gmbus_bridge);
if (bus->bbbus_bridge != NULL)
device_delete_child(dev->dev, bus->bbbus_bridge);
}
mtx_unlock(&Giant);
sx_destroy(&dev_priv->gmbus_mutex);
return ret;
}
device_t intel_gmbus_get_adapter(struct drm_i915_private *dev_priv,
unsigned port)
{
WARN_ON(!intel_gmbus_is_port_valid(port));
/* -1 to map pin pair to gmbus index */
return (intel_gmbus_is_port_valid(port)) ?
dev_priv->gmbus[port - 1].gmbus : NULL;
}
void intel_gmbus_set_speed(device_t adapter, int speed)
{
struct intel_gmbus *bus = to_intel_gmbus(adapter);
bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed;
}
void intel_gmbus_force_bit(device_t adapter, bool force_bit)
{
struct intel_gmbus *bus = to_intel_gmbus(adapter);
bus->force_bit += force_bit ? 1 : -1;
DRM_DEBUG_KMS("%sabling bit-banging on %s. force bit now %d\n",
force_bit ? "en" : "dis", device_get_desc(adapter),
bus->force_bit);
}
void intel_teardown_gmbus(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
int ret;
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
mtx_lock(&Giant);
ret = device_delete_child(dev->dev, bus->gmbus_bridge);
mtx_unlock(&Giant);
KASSERT(ret == 0, ("unable to detach iic gmbus %s: %d",
device_get_desc(bus->gmbus_bridge), ret));
mtx_lock(&Giant);
ret = device_delete_child(dev->dev, bus->bbbus_bridge);
mtx_unlock(&Giant);
KASSERT(ret == 0, ("unable to detach iic bbbus %s: %d",
device_get_desc(bus->bbbus_bridge), ret));
}
sx_destroy(&dev_priv->gmbus_mutex);
}