3c03ec209a
There are a few nvidia hardware where the i2c port assignments are reversed, i.e., the 1st i2c port is assigned to the secondary display and the 2nd i2c port to the primary display. In most cases, if only 1 display is attached, or if only 1 flatpanel and 1 analog display is attached, the port reversal is of no consequence. However if 2 flatpanels are attached, it can cause display problems. There is no sane way of determining if the hardware reversed the i2c port assignment, so the simplest fix is to add a boot/module option, "reverse_i2c to explicitly reverse the i2c port. This also restores i2c ordering back to the pre-2.6.22 state. Signed-off-by: Antonino Daplas <adaplas@gail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
170 lines
3.6 KiB
C
170 lines
3.6 KiB
C
/*
|
|
* linux/drivers/video/nvidia/nvidia-i2c.c - nVidia i2c
|
|
*
|
|
* Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
|
|
*
|
|
* Based on rivafb-i2c.c
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file COPYING in the main directory of this archive
|
|
* for more details.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/fb.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include "nv_type.h"
|
|
#include "nv_local.h"
|
|
#include "nv_proto.h"
|
|
|
|
#include "../edid.h"
|
|
|
|
static void nvidia_gpio_setscl(void *data, int state)
|
|
{
|
|
struct nvidia_i2c_chan *chan = data;
|
|
struct nvidia_par *par = chan->par;
|
|
u32 val;
|
|
|
|
val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0;
|
|
|
|
if (state)
|
|
val |= 0x20;
|
|
else
|
|
val &= ~0x20;
|
|
|
|
NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01);
|
|
}
|
|
|
|
static void nvidia_gpio_setsda(void *data, int state)
|
|
{
|
|
struct nvidia_i2c_chan *chan = data;
|
|
struct nvidia_par *par = chan->par;
|
|
u32 val;
|
|
|
|
val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0;
|
|
|
|
if (state)
|
|
val |= 0x10;
|
|
else
|
|
val &= ~0x10;
|
|
|
|
NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01);
|
|
}
|
|
|
|
static int nvidia_gpio_getscl(void *data)
|
|
{
|
|
struct nvidia_i2c_chan *chan = data;
|
|
struct nvidia_par *par = chan->par;
|
|
u32 val = 0;
|
|
|
|
if (NVReadCrtc(par, chan->ddc_base) & 0x04)
|
|
val = 1;
|
|
|
|
return val;
|
|
}
|
|
|
|
static int nvidia_gpio_getsda(void *data)
|
|
{
|
|
struct nvidia_i2c_chan *chan = data;
|
|
struct nvidia_par *par = chan->par;
|
|
u32 val = 0;
|
|
|
|
if (NVReadCrtc(par, chan->ddc_base) & 0x08)
|
|
val = 1;
|
|
|
|
return val;
|
|
}
|
|
|
|
static int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name,
|
|
unsigned int i2c_class)
|
|
{
|
|
int rc;
|
|
|
|
strcpy(chan->adapter.name, name);
|
|
chan->adapter.owner = THIS_MODULE;
|
|
chan->adapter.id = I2C_HW_B_NVIDIA;
|
|
chan->adapter.class = i2c_class;
|
|
chan->adapter.algo_data = &chan->algo;
|
|
chan->adapter.dev.parent = &chan->par->pci_dev->dev;
|
|
chan->algo.setsda = nvidia_gpio_setsda;
|
|
chan->algo.setscl = nvidia_gpio_setscl;
|
|
chan->algo.getsda = nvidia_gpio_getsda;
|
|
chan->algo.getscl = nvidia_gpio_getscl;
|
|
chan->algo.udelay = 40;
|
|
chan->algo.timeout = msecs_to_jiffies(2);
|
|
chan->algo.data = chan;
|
|
|
|
i2c_set_adapdata(&chan->adapter, chan);
|
|
|
|
/* Raise SCL and SDA */
|
|
nvidia_gpio_setsda(chan, 1);
|
|
nvidia_gpio_setscl(chan, 1);
|
|
udelay(20);
|
|
|
|
rc = i2c_bit_add_bus(&chan->adapter);
|
|
if (rc == 0)
|
|
dev_dbg(&chan->par->pci_dev->dev,
|
|
"I2C bus %s registered.\n", name);
|
|
else {
|
|
dev_warn(&chan->par->pci_dev->dev,
|
|
"Failed to register I2C bus %s.\n", name);
|
|
chan->par = NULL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void nvidia_create_i2c_busses(struct nvidia_par *par)
|
|
{
|
|
par->chan[0].par = par;
|
|
par->chan[1].par = par;
|
|
par->chan[2].par = par;
|
|
|
|
par->chan[0].ddc_base = (par->reverse_i2c) ? 0x36 : 0x3e;
|
|
nvidia_setup_i2c_bus(&par->chan[0], "nvidia #0", 0);
|
|
|
|
par->chan[1].ddc_base = (par->reverse_i2c) ? 0x3e : 0x36;
|
|
nvidia_setup_i2c_bus(&par->chan[1], "nvidia #1", I2C_CLASS_HWMON);
|
|
|
|
par->chan[2].ddc_base = 0x50;
|
|
nvidia_setup_i2c_bus(&par->chan[2], "nvidia #2", 0);
|
|
}
|
|
|
|
void nvidia_delete_i2c_busses(struct nvidia_par *par)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (!par->chan[i].par)
|
|
continue;
|
|
i2c_del_adapter(&par->chan[i].adapter);
|
|
par->chan[i].par = NULL;
|
|
}
|
|
}
|
|
|
|
int nvidia_probe_i2c_connector(struct fb_info *info, int conn, u8 **out_edid)
|
|
{
|
|
struct nvidia_par *par = info->par;
|
|
u8 *edid = NULL;
|
|
|
|
if (par->chan[conn - 1].par)
|
|
edid = fb_ddc_read(&par->chan[conn - 1].adapter);
|
|
|
|
if (!edid && conn == 1) {
|
|
/* try to get from firmware */
|
|
const u8 *e = fb_firmware_edid(info->device);
|
|
|
|
if (e != NULL)
|
|
edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL);
|
|
}
|
|
|
|
*out_edid = edid;
|
|
|
|
return (edid) ? 0 : 1;
|
|
}
|