162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * linux/drivers/video/nvidia/nvidia-i2c.c - nVidia i2c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Based on rivafb-i2c.c
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
962306a36Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive
1062306a36Sopenharmony_ci * for more details.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/gfp.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci#include <linux/fb.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <asm/io.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "nv_type.h"
2362306a36Sopenharmony_ci#include "nv_local.h"
2462306a36Sopenharmony_ci#include "nv_proto.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "../edid.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void nvidia_gpio_setscl(void *data, int state)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct nvidia_i2c_chan *chan = data;
3162306a36Sopenharmony_ci	struct nvidia_par *par = chan->par;
3262306a36Sopenharmony_ci	u32 val;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (state)
3762306a36Sopenharmony_ci		val |= 0x20;
3862306a36Sopenharmony_ci	else
3962306a36Sopenharmony_ci		val &= ~0x20;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void nvidia_gpio_setsda(void *data, int state)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct nvidia_i2c_chan *chan = data;
4762306a36Sopenharmony_ci	struct nvidia_par *par = chan->par;
4862306a36Sopenharmony_ci	u32 val;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (state)
5362306a36Sopenharmony_ci		val |= 0x10;
5462306a36Sopenharmony_ci	else
5562306a36Sopenharmony_ci		val &= ~0x10;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int nvidia_gpio_getscl(void *data)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct nvidia_i2c_chan *chan = data;
6362306a36Sopenharmony_ci	struct nvidia_par *par = chan->par;
6462306a36Sopenharmony_ci	u32 val = 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (NVReadCrtc(par, chan->ddc_base) & 0x04)
6762306a36Sopenharmony_ci		val = 1;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return val;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int nvidia_gpio_getsda(void *data)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct nvidia_i2c_chan *chan = data;
7562306a36Sopenharmony_ci	struct nvidia_par *par = chan->par;
7662306a36Sopenharmony_ci	u32 val = 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (NVReadCrtc(par, chan->ddc_base) & 0x08)
7962306a36Sopenharmony_ci		val = 1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return val;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name,
8562306a36Sopenharmony_ci				unsigned int i2c_class)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	int rc;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	strscpy(chan->adapter.name, name, sizeof(chan->adapter.name));
9062306a36Sopenharmony_ci	chan->adapter.owner = THIS_MODULE;
9162306a36Sopenharmony_ci	chan->adapter.class = i2c_class;
9262306a36Sopenharmony_ci	chan->adapter.algo_data = &chan->algo;
9362306a36Sopenharmony_ci	chan->adapter.dev.parent = &chan->par->pci_dev->dev;
9462306a36Sopenharmony_ci	chan->algo.setsda = nvidia_gpio_setsda;
9562306a36Sopenharmony_ci	chan->algo.setscl = nvidia_gpio_setscl;
9662306a36Sopenharmony_ci	chan->algo.getsda = nvidia_gpio_getsda;
9762306a36Sopenharmony_ci	chan->algo.getscl = nvidia_gpio_getscl;
9862306a36Sopenharmony_ci	chan->algo.udelay = 40;
9962306a36Sopenharmony_ci	chan->algo.timeout = msecs_to_jiffies(2);
10062306a36Sopenharmony_ci	chan->algo.data = chan;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	i2c_set_adapdata(&chan->adapter, chan);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Raise SCL and SDA */
10562306a36Sopenharmony_ci	nvidia_gpio_setsda(chan, 1);
10662306a36Sopenharmony_ci	nvidia_gpio_setscl(chan, 1);
10762306a36Sopenharmony_ci	udelay(20);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	rc = i2c_bit_add_bus(&chan->adapter);
11062306a36Sopenharmony_ci	if (rc == 0)
11162306a36Sopenharmony_ci		dev_dbg(&chan->par->pci_dev->dev,
11262306a36Sopenharmony_ci			"I2C bus %s registered.\n", name);
11362306a36Sopenharmony_ci	else {
11462306a36Sopenharmony_ci		dev_warn(&chan->par->pci_dev->dev,
11562306a36Sopenharmony_ci			 "Failed to register I2C bus %s.\n", name);
11662306a36Sopenharmony_ci		chan->par = NULL;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return rc;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid nvidia_create_i2c_busses(struct nvidia_par *par)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	par->chan[0].par = par;
12562306a36Sopenharmony_ci	par->chan[1].par = par;
12662306a36Sopenharmony_ci	par->chan[2].par = par;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	par->chan[0].ddc_base = (par->reverse_i2c) ? 0x36 : 0x3e;
12962306a36Sopenharmony_ci 	nvidia_setup_i2c_bus(&par->chan[0], "nvidia #0",
13062306a36Sopenharmony_ci			     (par->reverse_i2c) ? I2C_CLASS_HWMON : 0);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	par->chan[1].ddc_base = (par->reverse_i2c) ? 0x3e : 0x36;
13362306a36Sopenharmony_ci 	nvidia_setup_i2c_bus(&par->chan[1], "nvidia #1",
13462306a36Sopenharmony_ci			     (par->reverse_i2c) ? 0 : I2C_CLASS_HWMON);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	par->chan[2].ddc_base = 0x50;
13762306a36Sopenharmony_ci 	nvidia_setup_i2c_bus(&par->chan[2], "nvidia #2", 0);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_civoid nvidia_delete_i2c_busses(struct nvidia_par *par)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	int i;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
14562306a36Sopenharmony_ci		if (!par->chan[i].par)
14662306a36Sopenharmony_ci			continue;
14762306a36Sopenharmony_ci		i2c_del_adapter(&par->chan[i].adapter);
14862306a36Sopenharmony_ci		par->chan[i].par = NULL;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciint nvidia_probe_i2c_connector(struct fb_info *info, int conn, u8 **out_edid)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct nvidia_par *par = info->par;
15562306a36Sopenharmony_ci	u8 *edid = NULL;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (par->chan[conn - 1].par)
15862306a36Sopenharmony_ci		edid = fb_ddc_read(&par->chan[conn - 1].adapter);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (!edid && conn == 1) {
16162306a36Sopenharmony_ci		/* try to get from firmware */
16262306a36Sopenharmony_ci		const u8 *e = fb_firmware_edid(info->device);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		if (e != NULL)
16562306a36Sopenharmony_ci			edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL);
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	*out_edid = edid;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return (edid) ? 0 : 1;
17162306a36Sopenharmony_ci}
172