diff options
-rw-r--r-- | drivers/video/Kconfig | 9 | ||||
-rw-r--r-- | drivers/video/tdfxfb.c | 200 | ||||
-rw-r--r-- | include/video/tdfx.h | 26 |
3 files changed, 234 insertions, 1 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ffe2f2796e29..7826bdce4bbe 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -1550,6 +1550,7 @@ config FB_3DFX | |||
1550 | select FB_CFB_IMAGEBLIT | 1550 | select FB_CFB_IMAGEBLIT |
1551 | select FB_CFB_FILLRECT | 1551 | select FB_CFB_FILLRECT |
1552 | select FB_CFB_COPYAREA | 1552 | select FB_CFB_COPYAREA |
1553 | select FB_MODE_HELPERS | ||
1553 | help | 1554 | help |
1554 | This driver supports graphics boards with the 3Dfx Banshee, | 1555 | This driver supports graphics boards with the 3Dfx Banshee, |
1555 | Voodoo3 or VSA-100 (aka Voodoo4/5) chips. Say Y if you have | 1556 | Voodoo3 or VSA-100 (aka Voodoo4/5) chips. Say Y if you have |
@@ -1565,6 +1566,14 @@ config FB_3DFX_ACCEL | |||
1565 | This will compile the 3Dfx Banshee/Voodoo3/VSA-100 frame buffer | 1566 | This will compile the 3Dfx Banshee/Voodoo3/VSA-100 frame buffer |
1566 | device driver with acceleration functions. | 1567 | device driver with acceleration functions. |
1567 | 1568 | ||
1569 | config FB_3DFX_I2C | ||
1570 | bool "Enable DDC/I2C support" | ||
1571 | depends on FB_3DFX && EXPERIMENTAL | ||
1572 | select FB_DDC | ||
1573 | default y | ||
1574 | help | ||
1575 | Say Y here if you want DDC/I2C support for your 3dfx Voodoo3. | ||
1576 | |||
1568 | config FB_VOODOO1 | 1577 | config FB_VOODOO1 |
1569 | tristate "3Dfx Voodoo Graphics (sst1) support" | 1578 | tristate "3Dfx Voodoo Graphics (sst1) support" |
1570 | depends on FB && PCI | 1579 | depends on FB && PCI |
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index ee64771fbe3d..bc6f916c53be 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c | |||
@@ -10,6 +10,12 @@ | |||
10 | * Created : Thu Sep 23 18:17:43 1999, hmallat | 10 | * Created : Thu Sep 23 18:17:43 1999, hmallat |
11 | * Last modified: Tue Nov 2 21:19:47 1999, hmallat | 11 | * Last modified: Tue Nov 2 21:19:47 1999, hmallat |
12 | * | 12 | * |
13 | * I2C part copied from the i2c-voodoo3.c driver by: | ||
14 | * Frodo Looijaard <frodol@dds.nl>, | ||
15 | * Philip Edelbrock <phil@netroedge.com>, | ||
16 | * Ralph Metzler <rjkm@thp.uni-koeln.de>, and | ||
17 | * Mark D. Studebaker <mdsxyz123@yahoo.com> | ||
18 | * | ||
13 | * Lots of the information here comes from the Daryll Strauss' Banshee | 19 | * Lots of the information here comes from the Daryll Strauss' Banshee |
14 | * patches to the XF86 server, and the rest comes from the 3dfx | 20 | * patches to the XF86 server, and the rest comes from the 3dfx |
15 | * Banshee specification. I'm very much indebted to Daryll for his | 21 | * Banshee specification. I'm very much indebted to Daryll for his |
@@ -1167,6 +1173,190 @@ static struct fb_ops tdfxfb_ops = { | |||
1167 | #endif | 1173 | #endif |
1168 | }; | 1174 | }; |
1169 | 1175 | ||
1176 | #ifdef CONFIG_FB_3DFX_I2C | ||
1177 | /* The voo GPIO registers don't have individual masks for each bit | ||
1178 | so we always have to read before writing. */ | ||
1179 | |||
1180 | static void tdfxfb_i2c_setscl(void *data, int val) | ||
1181 | { | ||
1182 | struct tdfxfb_i2c_chan *chan = data; | ||
1183 | struct tdfx_par *par = chan->par; | ||
1184 | unsigned int r; | ||
1185 | |||
1186 | r = tdfx_inl(par, VIDSERPARPORT); | ||
1187 | if (val) | ||
1188 | r |= I2C_SCL_OUT; | ||
1189 | else | ||
1190 | r &= ~I2C_SCL_OUT; | ||
1191 | tdfx_outl(par, VIDSERPARPORT, r); | ||
1192 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
1193 | } | ||
1194 | |||
1195 | static void tdfxfb_i2c_setsda(void *data, int val) | ||
1196 | { | ||
1197 | struct tdfxfb_i2c_chan *chan = data; | ||
1198 | struct tdfx_par *par = chan->par; | ||
1199 | unsigned int r; | ||
1200 | |||
1201 | r = tdfx_inl(par, VIDSERPARPORT); | ||
1202 | if (val) | ||
1203 | r |= I2C_SDA_OUT; | ||
1204 | else | ||
1205 | r &= ~I2C_SDA_OUT; | ||
1206 | tdfx_outl(par, VIDSERPARPORT, r); | ||
1207 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
1208 | } | ||
1209 | |||
1210 | /* The GPIO pins are open drain, so the pins always remain outputs. | ||
1211 | We rely on the i2c-algo-bit routines to set the pins high before | ||
1212 | reading the input from other chips. */ | ||
1213 | |||
1214 | static int tdfxfb_i2c_getscl(void *data) | ||
1215 | { | ||
1216 | struct tdfxfb_i2c_chan *chan = data; | ||
1217 | struct tdfx_par *par = chan->par; | ||
1218 | |||
1219 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SCL_IN)); | ||
1220 | } | ||
1221 | |||
1222 | static int tdfxfb_i2c_getsda(void *data) | ||
1223 | { | ||
1224 | struct tdfxfb_i2c_chan *chan = data; | ||
1225 | struct tdfx_par *par = chan->par; | ||
1226 | |||
1227 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SDA_IN)); | ||
1228 | } | ||
1229 | |||
1230 | static void tdfxfb_ddc_setscl(void *data, int val) | ||
1231 | { | ||
1232 | struct tdfxfb_i2c_chan *chan = data; | ||
1233 | struct tdfx_par *par = chan->par; | ||
1234 | unsigned int r; | ||
1235 | |||
1236 | r = tdfx_inl(par, VIDSERPARPORT); | ||
1237 | if (val) | ||
1238 | r |= DDC_SCL_OUT; | ||
1239 | else | ||
1240 | r &= ~DDC_SCL_OUT; | ||
1241 | tdfx_outl(par, VIDSERPARPORT, r); | ||
1242 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
1243 | } | ||
1244 | |||
1245 | static void tdfxfb_ddc_setsda(void *data, int val) | ||
1246 | { | ||
1247 | struct tdfxfb_i2c_chan *chan = data; | ||
1248 | struct tdfx_par *par = chan->par; | ||
1249 | unsigned int r; | ||
1250 | |||
1251 | r = tdfx_inl(par, VIDSERPARPORT); | ||
1252 | if (val) | ||
1253 | r |= DDC_SDA_OUT; | ||
1254 | else | ||
1255 | r &= ~DDC_SDA_OUT; | ||
1256 | tdfx_outl(par, VIDSERPARPORT, r); | ||
1257 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
1258 | } | ||
1259 | |||
1260 | static int tdfxfb_ddc_getscl(void *data) | ||
1261 | { | ||
1262 | struct tdfxfb_i2c_chan *chan = data; | ||
1263 | struct tdfx_par *par = chan->par; | ||
1264 | |||
1265 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SCL_IN)); | ||
1266 | } | ||
1267 | |||
1268 | static int tdfxfb_ddc_getsda(void *data) | ||
1269 | { | ||
1270 | struct tdfxfb_i2c_chan *chan = data; | ||
1271 | struct tdfx_par *par = chan->par; | ||
1272 | |||
1273 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SDA_IN)); | ||
1274 | } | ||
1275 | |||
1276 | static int __devinit tdfxfb_setup_ddc_bus(struct tdfxfb_i2c_chan *chan, | ||
1277 | const char *name, struct device *dev) | ||
1278 | { | ||
1279 | int rc; | ||
1280 | |||
1281 | strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name)); | ||
1282 | chan->adapter.owner = THIS_MODULE; | ||
1283 | chan->adapter.class = I2C_CLASS_DDC; | ||
1284 | chan->adapter.algo_data = &chan->algo; | ||
1285 | chan->adapter.dev.parent = dev; | ||
1286 | chan->algo.setsda = tdfxfb_ddc_setsda; | ||
1287 | chan->algo.setscl = tdfxfb_ddc_setscl; | ||
1288 | chan->algo.getsda = tdfxfb_ddc_getsda; | ||
1289 | chan->algo.getscl = tdfxfb_ddc_getscl; | ||
1290 | chan->algo.udelay = 10; | ||
1291 | chan->algo.timeout = msecs_to_jiffies(500); | ||
1292 | chan->algo.data = chan; | ||
1293 | |||
1294 | i2c_set_adapdata(&chan->adapter, chan); | ||
1295 | |||
1296 | rc = i2c_bit_add_bus(&chan->adapter); | ||
1297 | if (rc == 0) | ||
1298 | DPRINTK("I2C bus %s registered.\n", name); | ||
1299 | else | ||
1300 | chan->par = NULL; | ||
1301 | |||
1302 | return rc; | ||
1303 | } | ||
1304 | |||
1305 | static int __devinit tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan, | ||
1306 | const char *name, struct device *dev) | ||
1307 | { | ||
1308 | int rc; | ||
1309 | |||
1310 | strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name)); | ||
1311 | chan->adapter.owner = THIS_MODULE; | ||
1312 | chan->adapter.class = I2C_CLASS_TV_ANALOG; | ||
1313 | chan->adapter.algo_data = &chan->algo; | ||
1314 | chan->adapter.dev.parent = dev; | ||
1315 | chan->algo.setsda = tdfxfb_i2c_setsda; | ||
1316 | chan->algo.setscl = tdfxfb_i2c_setscl; | ||
1317 | chan->algo.getsda = tdfxfb_i2c_getsda; | ||
1318 | chan->algo.getscl = tdfxfb_i2c_getscl; | ||
1319 | chan->algo.udelay = 10; | ||
1320 | chan->algo.timeout = msecs_to_jiffies(500); | ||
1321 | chan->algo.data = chan; | ||
1322 | |||
1323 | i2c_set_adapdata(&chan->adapter, chan); | ||
1324 | |||
1325 | rc = i2c_bit_add_bus(&chan->adapter); | ||
1326 | if (rc == 0) | ||
1327 | DPRINTK("I2C bus %s registered.\n", name); | ||
1328 | else | ||
1329 | chan->par = NULL; | ||
1330 | |||
1331 | return rc; | ||
1332 | } | ||
1333 | |||
1334 | static void __devinit tdfxfb_create_i2c_busses(struct fb_info *info) | ||
1335 | { | ||
1336 | struct tdfx_par *par = info->par; | ||
1337 | |||
1338 | tdfx_outl(par, VIDINFORMAT, 0x8160); | ||
1339 | tdfx_outl(par, VIDSERPARPORT, 0xcffc0020); | ||
1340 | |||
1341 | par->chan[0].par = par; | ||
1342 | par->chan[1].par = par; | ||
1343 | |||
1344 | tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->dev); | ||
1345 | tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->dev); | ||
1346 | } | ||
1347 | |||
1348 | static void tdfxfb_delete_i2c_busses(struct tdfx_par *par) | ||
1349 | { | ||
1350 | if (par->chan[0].par) | ||
1351 | i2c_del_adapter(&par->chan[0].adapter); | ||
1352 | par->chan[0].par = NULL; | ||
1353 | |||
1354 | if (par->chan[1].par) | ||
1355 | i2c_del_adapter(&par->chan[1].adapter); | ||
1356 | par->chan[1].par = NULL; | ||
1357 | } | ||
1358 | #endif /* CONFIG_FB_3DFX_I2C */ | ||
1359 | |||
1170 | /** | 1360 | /** |
1171 | * tdfxfb_probe - Device Initializiation | 1361 | * tdfxfb_probe - Device Initializiation |
1172 | * | 1362 | * |
@@ -1284,7 +1474,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev, | |||
1284 | if (hwcursor) | 1474 | if (hwcursor) |
1285 | info->fix.smem_len = (info->fix.smem_len - 1024) & | 1475 | info->fix.smem_len = (info->fix.smem_len - 1024) & |
1286 | (PAGE_MASK << 1); | 1476 | (PAGE_MASK << 1); |
1287 | 1477 | #ifdef CONFIG_FB_3DFX_I2C | |
1478 | tdfxfb_create_i2c_busses(info); | ||
1479 | #endif | ||
1288 | if (!mode_option) | 1480 | if (!mode_option) |
1289 | mode_option = "640x480@60"; | 1481 | mode_option = "640x480@60"; |
1290 | 1482 | ||
@@ -1315,6 +1507,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev, | |||
1315 | return 0; | 1507 | return 0; |
1316 | 1508 | ||
1317 | out_err_iobase: | 1509 | out_err_iobase: |
1510 | #ifdef CONFIG_FB_3DFX_I2C | ||
1511 | tdfxfb_delete_i2c_busses(default_par); | ||
1512 | #endif | ||
1318 | if (default_par->mtrr_handle >= 0) | 1513 | if (default_par->mtrr_handle >= 0) |
1319 | mtrr_del(default_par->mtrr_handle, info->fix.smem_start, | 1514 | mtrr_del(default_par->mtrr_handle, info->fix.smem_start, |
1320 | info->fix.smem_len); | 1515 | info->fix.smem_len); |
@@ -1379,6 +1574,9 @@ static void __devexit tdfxfb_remove(struct pci_dev *pdev) | |||
1379 | struct tdfx_par *par = info->par; | 1574 | struct tdfx_par *par = info->par; |
1380 | 1575 | ||
1381 | unregister_framebuffer(info); | 1576 | unregister_framebuffer(info); |
1577 | #ifdef CONFIG_FB_3DFX_I2C | ||
1578 | tdfxfb_delete_i2c_busses(par); | ||
1579 | #endif | ||
1382 | if (par->mtrr_handle >= 0) | 1580 | if (par->mtrr_handle >= 0) |
1383 | mtrr_del(par->mtrr_handle, info->fix.smem_start, | 1581 | mtrr_del(par->mtrr_handle, info->fix.smem_start, |
1384 | info->fix.smem_len); | 1582 | info->fix.smem_len); |
diff --git a/include/video/tdfx.h b/include/video/tdfx.h index 7431d9681e57..befbaf0a92d8 100644 --- a/include/video/tdfx.h +++ b/include/video/tdfx.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef _TDFX_H | 1 | #ifndef _TDFX_H |
2 | #define _TDFX_H | 2 | #define _TDFX_H |
3 | 3 | ||
4 | #include <linux/i2c.h> | ||
5 | #include <linux/i2c-algo-bit.h> | ||
6 | |||
4 | /* membase0 register offsets */ | 7 | /* membase0 register offsets */ |
5 | #define STATUS 0x00 | 8 | #define STATUS 0x00 |
6 | #define PCIINIT0 0x04 | 9 | #define PCIINIT0 0x04 |
@@ -123,6 +126,18 @@ | |||
123 | #define VIDCFG_PIXFMT_SHIFT 18 | 126 | #define VIDCFG_PIXFMT_SHIFT 18 |
124 | #define DACMODE_2X BIT(0) | 127 | #define DACMODE_2X BIT(0) |
125 | 128 | ||
129 | /* I2C bit locations in the VIDSERPARPORT register */ | ||
130 | #define DDC_ENAB 0x00040000 | ||
131 | #define DDC_SCL_OUT 0x00080000 | ||
132 | #define DDC_SDA_OUT 0x00100000 | ||
133 | #define DDC_SCL_IN 0x00200000 | ||
134 | #define DDC_SDA_IN 0x00400000 | ||
135 | #define I2C_ENAB 0x00800000 | ||
136 | #define I2C_SCL_OUT 0x01000000 | ||
137 | #define I2C_SDA_OUT 0x02000000 | ||
138 | #define I2C_SCL_IN 0x04000000 | ||
139 | #define I2C_SDA_IN 0x08000000 | ||
140 | |||
126 | /* VGA rubbish, need to change this for multihead support */ | 141 | /* VGA rubbish, need to change this for multihead support */ |
127 | #define MISC_W 0x3c2 | 142 | #define MISC_W 0x3c2 |
128 | #define MISC_R 0x3cc | 143 | #define MISC_R 0x3cc |
@@ -168,12 +183,23 @@ struct banshee_reg { | |||
168 | unsigned long miscinit0; | 183 | unsigned long miscinit0; |
169 | }; | 184 | }; |
170 | 185 | ||
186 | struct tdfx_par; | ||
187 | |||
188 | struct tdfxfb_i2c_chan { | ||
189 | struct tdfx_par *par; | ||
190 | struct i2c_adapter adapter; | ||
191 | struct i2c_algo_bit_data algo; | ||
192 | }; | ||
193 | |||
171 | struct tdfx_par { | 194 | struct tdfx_par { |
172 | u32 max_pixclock; | 195 | u32 max_pixclock; |
173 | u32 palette[16]; | 196 | u32 palette[16]; |
174 | void __iomem *regbase_virt; | 197 | void __iomem *regbase_virt; |
175 | unsigned long iobase; | 198 | unsigned long iobase; |
176 | int mtrr_handle; | 199 | int mtrr_handle; |
200 | #ifdef CONFIG_FB_3DFX_I2C | ||
201 | struct tdfxfb_i2c_chan chan[2]; | ||
202 | #endif | ||
177 | }; | 203 | }; |
178 | 204 | ||
179 | #endif /* __KERNEL__ */ | 205 | #endif /* __KERNEL__ */ |