savagefb-i2c.c (5691B)
1/* 2 * linux/drivers/video/savage/savagefb-i2c.c - S3 Savage DDC2 3 * 4 * Copyright 2004 Antonino A. Daplas <adaplas @pol.net> 5 * 6 * Based partly on rivafb-i2c.c 7 * 8 * This file is subject to the terms and conditions of the GNU General Public 9 * License. See the file COPYING in the main directory of this archive 10 * for more details. 11 */ 12 13#include <linux/module.h> 14#include <linux/kernel.h> 15#include <linux/delay.h> 16#include <linux/gfp.h> 17#include <linux/pci.h> 18#include <linux/fb.h> 19 20#include <asm/io.h> 21#include "savagefb.h" 22 23#define SAVAGE_DDC 0x50 24 25#define VGA_CR_IX 0x3d4 26#define VGA_CR_DATA 0x3d5 27 28#define CR_SERIAL1 0xa0 /* I2C serial communications interface */ 29#define MM_SERIAL1 0xff20 30#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */ 31 32/* based on vt8365 documentation */ 33#define PROSAVAGE_I2C_ENAB 0x10 34#define PROSAVAGE_I2C_SCL_OUT 0x01 35#define PROSAVAGE_I2C_SDA_OUT 0x02 36#define PROSAVAGE_I2C_SCL_IN 0x04 37#define PROSAVAGE_I2C_SDA_IN 0x08 38 39#define SAVAGE4_I2C_ENAB 0x00000020 40#define SAVAGE4_I2C_SCL_OUT 0x00000001 41#define SAVAGE4_I2C_SDA_OUT 0x00000002 42#define SAVAGE4_I2C_SCL_IN 0x00000008 43#define SAVAGE4_I2C_SDA_IN 0x00000010 44 45static void savage4_gpio_setscl(void *data, int val) 46{ 47 struct savagefb_i2c_chan *chan = data; 48 unsigned int r; 49 50 r = readl(chan->ioaddr + chan->reg); 51 if(val) 52 r |= SAVAGE4_I2C_SCL_OUT; 53 else 54 r &= ~SAVAGE4_I2C_SCL_OUT; 55 writel(r, chan->ioaddr + chan->reg); 56 readl(chan->ioaddr + chan->reg); /* flush posted write */ 57} 58 59static void savage4_gpio_setsda(void *data, int val) 60{ 61 struct savagefb_i2c_chan *chan = data; 62 63 unsigned int r; 64 r = readl(chan->ioaddr + chan->reg); 65 if(val) 66 r |= SAVAGE4_I2C_SDA_OUT; 67 else 68 r &= ~SAVAGE4_I2C_SDA_OUT; 69 writel(r, chan->ioaddr + chan->reg); 70 readl(chan->ioaddr + chan->reg); /* flush posted write */ 71} 72 73static int savage4_gpio_getscl(void *data) 74{ 75 struct savagefb_i2c_chan *chan = data; 76 77 return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SCL_IN)); 78} 79 80static int savage4_gpio_getsda(void *data) 81{ 82 struct savagefb_i2c_chan *chan = data; 83 84 return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SDA_IN)); 85} 86 87static void prosavage_gpio_setscl(void* data, int val) 88{ 89 struct savagefb_i2c_chan *chan = data; 90 u32 r; 91 92 r = VGArCR(chan->reg, chan->par); 93 r |= PROSAVAGE_I2C_ENAB; 94 if (val) { 95 r |= PROSAVAGE_I2C_SCL_OUT; 96 } else { 97 r &= ~PROSAVAGE_I2C_SCL_OUT; 98 } 99 100 VGAwCR(chan->reg, r, chan->par); 101} 102 103static void prosavage_gpio_setsda(void* data, int val) 104{ 105 struct savagefb_i2c_chan *chan = data; 106 unsigned int r; 107 108 r = VGArCR(chan->reg, chan->par); 109 r |= PROSAVAGE_I2C_ENAB; 110 if (val) { 111 r |= PROSAVAGE_I2C_SDA_OUT; 112 } else { 113 r &= ~PROSAVAGE_I2C_SDA_OUT; 114 } 115 116 VGAwCR(chan->reg, r, chan->par); 117} 118 119static int prosavage_gpio_getscl(void* data) 120{ 121 struct savagefb_i2c_chan *chan = data; 122 123 return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SCL_IN) ? 1 : 0; 124} 125 126static int prosavage_gpio_getsda(void* data) 127{ 128 struct savagefb_i2c_chan *chan = data; 129 130 return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SDA_IN) ? 1 : 0; 131} 132 133static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan, 134 const char *name) 135{ 136 int rc = 0; 137 138 if (chan->par) { 139 strcpy(chan->adapter.name, name); 140 chan->adapter.owner = THIS_MODULE; 141 chan->adapter.algo_data = &chan->algo; 142 chan->adapter.dev.parent = &chan->par->pcidev->dev; 143 chan->algo.udelay = 10; 144 chan->algo.timeout = 20; 145 chan->algo.data = chan; 146 147 i2c_set_adapdata(&chan->adapter, chan); 148 149 /* Raise SCL and SDA */ 150 chan->algo.setsda(chan, 1); 151 chan->algo.setscl(chan, 1); 152 udelay(20); 153 154 rc = i2c_bit_add_bus(&chan->adapter); 155 156 if (rc == 0) 157 dev_dbg(&chan->par->pcidev->dev, 158 "I2C bus %s registered.\n", name); 159 else 160 dev_warn(&chan->par->pcidev->dev, 161 "Failed to register I2C bus %s.\n", name); 162 } 163 164 return rc; 165} 166 167void savagefb_create_i2c_busses(struct fb_info *info) 168{ 169 struct savagefb_par *par = info->par; 170 par->chan.par = par; 171 172 switch (par->chip) { 173 case S3_PROSAVAGE: 174 case S3_PROSAVAGEDDR: 175 case S3_TWISTER: 176 par->chan.reg = CR_SERIAL2; 177 par->chan.ioaddr = par->mmio.vbase; 178 par->chan.algo.setsda = prosavage_gpio_setsda; 179 par->chan.algo.setscl = prosavage_gpio_setscl; 180 par->chan.algo.getsda = prosavage_gpio_getsda; 181 par->chan.algo.getscl = prosavage_gpio_getscl; 182 break; 183 case S3_SAVAGE4: 184 par->chan.reg = CR_SERIAL1; 185 if (par->pcidev->revision > 1 && !(VGArCR(0xa6, par) & 0x40)) 186 par->chan.reg = CR_SERIAL2; 187 par->chan.ioaddr = par->mmio.vbase; 188 par->chan.algo.setsda = prosavage_gpio_setsda; 189 par->chan.algo.setscl = prosavage_gpio_setscl; 190 par->chan.algo.getsda = prosavage_gpio_getsda; 191 par->chan.algo.getscl = prosavage_gpio_getscl; 192 break; 193 case S3_SAVAGE2000: 194 par->chan.reg = MM_SERIAL1; 195 par->chan.ioaddr = par->mmio.vbase; 196 par->chan.algo.setsda = savage4_gpio_setsda; 197 par->chan.algo.setscl = savage4_gpio_setscl; 198 par->chan.algo.getsda = savage4_gpio_getsda; 199 par->chan.algo.getscl = savage4_gpio_getscl; 200 break; 201 default: 202 par->chan.par = NULL; 203 } 204 205 savage_setup_i2c_bus(&par->chan, "SAVAGE DDC2"); 206} 207 208void savagefb_delete_i2c_busses(struct fb_info *info) 209{ 210 struct savagefb_par *par = info->par; 211 212 if (par->chan.par) 213 i2c_del_adapter(&par->chan.adapter); 214 215 par->chan.par = NULL; 216} 217 218int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid) 219{ 220 struct savagefb_par *par = info->par; 221 u8 *edid; 222 223 if (par->chan.par) 224 edid = fb_ddc_read(&par->chan.adapter); 225 else 226 edid = NULL; 227 228 if (!edid) { 229 /* try to get from firmware */ 230 const u8 *e = fb_firmware_edid(info->device); 231 232 if (e) 233 edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL); 234 } 235 236 *out_edid = edid; 237 238 return (edid) ? 0 : 1; 239} 240 241MODULE_LICENSE("GPL");