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