18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/nvidia/nvidia-i2c.c - nVidia i2c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2004 Antonino A. Daplas <adaplas @pol.net> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on rivafb-i2c.c 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 98c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 108c2ecf20Sopenharmony_ci * for more details. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/gfp.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/fb.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/io.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "nv_type.h" 238c2ecf20Sopenharmony_ci#include "nv_local.h" 248c2ecf20Sopenharmony_ci#include "nv_proto.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "../edid.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void nvidia_gpio_setscl(void *data, int state) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct nvidia_i2c_chan *chan = data; 318c2ecf20Sopenharmony_ci struct nvidia_par *par = chan->par; 328c2ecf20Sopenharmony_ci u32 val; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (state) 378c2ecf20Sopenharmony_ci val |= 0x20; 388c2ecf20Sopenharmony_ci else 398c2ecf20Sopenharmony_ci val &= ~0x20; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void nvidia_gpio_setsda(void *data, int state) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct nvidia_i2c_chan *chan = data; 478c2ecf20Sopenharmony_ci struct nvidia_par *par = chan->par; 488c2ecf20Sopenharmony_ci u32 val; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (state) 538c2ecf20Sopenharmony_ci val |= 0x10; 548c2ecf20Sopenharmony_ci else 558c2ecf20Sopenharmony_ci val &= ~0x10; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int nvidia_gpio_getscl(void *data) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct nvidia_i2c_chan *chan = data; 638c2ecf20Sopenharmony_ci struct nvidia_par *par = chan->par; 648c2ecf20Sopenharmony_ci u32 val = 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (NVReadCrtc(par, chan->ddc_base) & 0x04) 678c2ecf20Sopenharmony_ci val = 1; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return val; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int nvidia_gpio_getsda(void *data) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct nvidia_i2c_chan *chan = data; 758c2ecf20Sopenharmony_ci struct nvidia_par *par = chan->par; 768c2ecf20Sopenharmony_ci u32 val = 0; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (NVReadCrtc(par, chan->ddc_base) & 0x08) 798c2ecf20Sopenharmony_ci val = 1; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return val; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name, 858c2ecf20Sopenharmony_ci unsigned int i2c_class) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci int rc; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci strscpy(chan->adapter.name, name, sizeof(chan->adapter.name)); 908c2ecf20Sopenharmony_ci chan->adapter.owner = THIS_MODULE; 918c2ecf20Sopenharmony_ci chan->adapter.class = i2c_class; 928c2ecf20Sopenharmony_ci chan->adapter.algo_data = &chan->algo; 938c2ecf20Sopenharmony_ci chan->adapter.dev.parent = &chan->par->pci_dev->dev; 948c2ecf20Sopenharmony_ci chan->algo.setsda = nvidia_gpio_setsda; 958c2ecf20Sopenharmony_ci chan->algo.setscl = nvidia_gpio_setscl; 968c2ecf20Sopenharmony_ci chan->algo.getsda = nvidia_gpio_getsda; 978c2ecf20Sopenharmony_ci chan->algo.getscl = nvidia_gpio_getscl; 988c2ecf20Sopenharmony_ci chan->algo.udelay = 40; 998c2ecf20Sopenharmony_ci chan->algo.timeout = msecs_to_jiffies(2); 1008c2ecf20Sopenharmony_ci chan->algo.data = chan; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci i2c_set_adapdata(&chan->adapter, chan); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Raise SCL and SDA */ 1058c2ecf20Sopenharmony_ci nvidia_gpio_setsda(chan, 1); 1068c2ecf20Sopenharmony_ci nvidia_gpio_setscl(chan, 1); 1078c2ecf20Sopenharmony_ci udelay(20); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci rc = i2c_bit_add_bus(&chan->adapter); 1108c2ecf20Sopenharmony_ci if (rc == 0) 1118c2ecf20Sopenharmony_ci dev_dbg(&chan->par->pci_dev->dev, 1128c2ecf20Sopenharmony_ci "I2C bus %s registered.\n", name); 1138c2ecf20Sopenharmony_ci else { 1148c2ecf20Sopenharmony_ci dev_warn(&chan->par->pci_dev->dev, 1158c2ecf20Sopenharmony_ci "Failed to register I2C bus %s.\n", name); 1168c2ecf20Sopenharmony_ci chan->par = NULL; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return rc; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_civoid nvidia_create_i2c_busses(struct nvidia_par *par) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci par->chan[0].par = par; 1258c2ecf20Sopenharmony_ci par->chan[1].par = par; 1268c2ecf20Sopenharmony_ci par->chan[2].par = par; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci par->chan[0].ddc_base = (par->reverse_i2c) ? 0x36 : 0x3e; 1298c2ecf20Sopenharmony_ci nvidia_setup_i2c_bus(&par->chan[0], "nvidia #0", 1308c2ecf20Sopenharmony_ci (par->reverse_i2c) ? I2C_CLASS_HWMON : 0); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci par->chan[1].ddc_base = (par->reverse_i2c) ? 0x3e : 0x36; 1338c2ecf20Sopenharmony_ci nvidia_setup_i2c_bus(&par->chan[1], "nvidia #1", 1348c2ecf20Sopenharmony_ci (par->reverse_i2c) ? 0 : I2C_CLASS_HWMON); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci par->chan[2].ddc_base = 0x50; 1378c2ecf20Sopenharmony_ci nvidia_setup_i2c_bus(&par->chan[2], "nvidia #2", 0); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_civoid nvidia_delete_i2c_busses(struct nvidia_par *par) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int i; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 1458c2ecf20Sopenharmony_ci if (!par->chan[i].par) 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci i2c_del_adapter(&par->chan[i].adapter); 1488c2ecf20Sopenharmony_ci par->chan[i].par = NULL; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciint nvidia_probe_i2c_connector(struct fb_info *info, int conn, u8 **out_edid) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct nvidia_par *par = info->par; 1558c2ecf20Sopenharmony_ci u8 *edid = NULL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (par->chan[conn - 1].par) 1588c2ecf20Sopenharmony_ci edid = fb_ddc_read(&par->chan[conn - 1].adapter); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!edid && conn == 1) { 1618c2ecf20Sopenharmony_ci /* try to get from firmware */ 1628c2ecf20Sopenharmony_ci const u8 *e = fb_firmware_edid(info->device); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (e != NULL) 1658c2ecf20Sopenharmony_ci edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci *out_edid = edid; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return (edid) ? 0 : 1; 1718c2ecf20Sopenharmony_ci} 172