diff options
Diffstat (limited to 'drivers/video/tdfxfb.c')
| -rw-r--r-- | drivers/video/tdfxfb.c | 267 |
1 files changed, 262 insertions, 5 deletions
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index ee64771fbe3d..89f231dc443f 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 |
| @@ -481,6 +487,12 @@ static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | |||
| 481 | return -EINVAL; | 487 | return -EINVAL; |
| 482 | } | 488 | } |
| 483 | 489 | ||
| 490 | if (info->monspecs.hfmax && info->monspecs.vfmax && | ||
| 491 | info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) { | ||
| 492 | DPRINTK("mode outside monitor's specs\n"); | ||
| 493 | return -EINVAL; | ||
| 494 | } | ||
| 495 | |||
| 484 | var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */ | 496 | var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */ |
| 485 | lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3); | 497 | lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3); |
| 486 | 498 | ||
| @@ -1167,6 +1179,207 @@ static struct fb_ops tdfxfb_ops = { | |||
| 1167 | #endif | 1179 | #endif |
| 1168 | }; | 1180 | }; |
| 1169 | 1181 | ||
| 1182 | #ifdef CONFIG_FB_3DFX_I2C | ||
| 1183 | /* The voo GPIO registers don't have individual masks for each bit | ||
| 1184 | so we always have to read before writing. */ | ||
| 1185 | |||
| 1186 | static void tdfxfb_i2c_setscl(void *data, int val) | ||
| 1187 | { | ||
| 1188 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1189 | struct tdfx_par *par = chan->par; | ||
| 1190 | unsigned int r; | ||
| 1191 | |||
| 1192 | r = tdfx_inl(par, VIDSERPARPORT); | ||
| 1193 | if (val) | ||
| 1194 | r |= I2C_SCL_OUT; | ||
| 1195 | else | ||
| 1196 | r &= ~I2C_SCL_OUT; | ||
| 1197 | tdfx_outl(par, VIDSERPARPORT, r); | ||
| 1198 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | static void tdfxfb_i2c_setsda(void *data, int val) | ||
| 1202 | { | ||
| 1203 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1204 | struct tdfx_par *par = chan->par; | ||
| 1205 | unsigned int r; | ||
| 1206 | |||
| 1207 | r = tdfx_inl(par, VIDSERPARPORT); | ||
| 1208 | if (val) | ||
| 1209 | r |= I2C_SDA_OUT; | ||
| 1210 | else | ||
| 1211 | r &= ~I2C_SDA_OUT; | ||
| 1212 | tdfx_outl(par, VIDSERPARPORT, r); | ||
| 1213 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | /* The GPIO pins are open drain, so the pins always remain outputs. | ||
| 1217 | We rely on the i2c-algo-bit routines to set the pins high before | ||
| 1218 | reading the input from other chips. */ | ||
| 1219 | |||
| 1220 | static int tdfxfb_i2c_getscl(void *data) | ||
| 1221 | { | ||
| 1222 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1223 | struct tdfx_par *par = chan->par; | ||
| 1224 | |||
| 1225 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SCL_IN)); | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | static int tdfxfb_i2c_getsda(void *data) | ||
| 1229 | { | ||
| 1230 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1231 | struct tdfx_par *par = chan->par; | ||
| 1232 | |||
| 1233 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SDA_IN)); | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | static void tdfxfb_ddc_setscl(void *data, int val) | ||
| 1237 | { | ||
| 1238 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1239 | struct tdfx_par *par = chan->par; | ||
| 1240 | unsigned int r; | ||
| 1241 | |||
| 1242 | r = tdfx_inl(par, VIDSERPARPORT); | ||
| 1243 | if (val) | ||
| 1244 | r |= DDC_SCL_OUT; | ||
| 1245 | else | ||
| 1246 | r &= ~DDC_SCL_OUT; | ||
| 1247 | tdfx_outl(par, VIDSERPARPORT, r); | ||
| 1248 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | static void tdfxfb_ddc_setsda(void *data, int val) | ||
| 1252 | { | ||
| 1253 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1254 | struct tdfx_par *par = chan->par; | ||
| 1255 | unsigned int r; | ||
| 1256 | |||
| 1257 | r = tdfx_inl(par, VIDSERPARPORT); | ||
| 1258 | if (val) | ||
| 1259 | r |= DDC_SDA_OUT; | ||
| 1260 | else | ||
| 1261 | r &= ~DDC_SDA_OUT; | ||
| 1262 | tdfx_outl(par, VIDSERPARPORT, r); | ||
| 1263 | tdfx_inl(par, VIDSERPARPORT); /* flush posted write */ | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | static int tdfxfb_ddc_getscl(void *data) | ||
| 1267 | { | ||
| 1268 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1269 | struct tdfx_par *par = chan->par; | ||
| 1270 | |||
| 1271 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SCL_IN)); | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | static int tdfxfb_ddc_getsda(void *data) | ||
| 1275 | { | ||
| 1276 | struct tdfxfb_i2c_chan *chan = data; | ||
| 1277 | struct tdfx_par *par = chan->par; | ||
| 1278 | |||
| 1279 | return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SDA_IN)); | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | static int __devinit tdfxfb_setup_ddc_bus(struct tdfxfb_i2c_chan *chan, | ||
| 1283 | const char *name, struct device *dev) | ||
| 1284 | { | ||
| 1285 | int rc; | ||
| 1286 | |||
| 1287 | strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name)); | ||
| 1288 | chan->adapter.owner = THIS_MODULE; | ||
| 1289 | chan->adapter.class = I2C_CLASS_DDC; | ||
| 1290 | chan->adapter.algo_data = &chan->algo; | ||
| 1291 | chan->adapter.dev.parent = dev; | ||
| 1292 | chan->algo.setsda = tdfxfb_ddc_setsda; | ||
| 1293 | chan->algo.setscl = tdfxfb_ddc_setscl; | ||
| 1294 | chan->algo.getsda = tdfxfb_ddc_getsda; | ||
| 1295 | chan->algo.getscl = tdfxfb_ddc_getscl; | ||
| 1296 | chan->algo.udelay = 10; | ||
| 1297 | chan->algo.timeout = msecs_to_jiffies(500); | ||
| 1298 | chan->algo.data = chan; | ||
| 1299 | |||
| 1300 | i2c_set_adapdata(&chan->adapter, chan); | ||
| 1301 | |||
| 1302 | rc = i2c_bit_add_bus(&chan->adapter); | ||
| 1303 | if (rc == 0) | ||
| 1304 | DPRINTK("I2C bus %s registered.\n", name); | ||
| 1305 | else | ||
| 1306 | chan->par = NULL; | ||
| 1307 | |||
| 1308 | return rc; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | static int __devinit tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan, | ||
| 1312 | const char *name, struct device *dev) | ||
| 1313 | { | ||
| 1314 | int rc; | ||
| 1315 | |||
| 1316 | strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name)); | ||
| 1317 | chan->adapter.owner = THIS_MODULE; | ||
| 1318 | chan->adapter.class = I2C_CLASS_TV_ANALOG; | ||
| 1319 | chan->adapter.algo_data = &chan->algo; | ||
| 1320 | chan->adapter.dev.parent = dev; | ||
| 1321 | chan->algo.setsda = tdfxfb_i2c_setsda; | ||
| 1322 | chan->algo.setscl = tdfxfb_i2c_setscl; | ||
| 1323 | chan->algo.getsda = tdfxfb_i2c_getsda; | ||
| 1324 | chan->algo.getscl = tdfxfb_i2c_getscl; | ||
| 1325 | chan->algo.udelay = 10; | ||
| 1326 | chan->algo.timeout = msecs_to_jiffies(500); | ||
| 1327 | chan->algo.data = chan; | ||
| 1328 | |||
| 1329 | i2c_set_adapdata(&chan->adapter, chan); | ||
| 1330 | |||
| 1331 | rc = i2c_bit_add_bus(&chan->adapter); | ||
| 1332 | if (rc == 0) | ||
| 1333 | DPRINTK("I2C bus %s registered.\n", name); | ||
| 1334 | else | ||
| 1335 | chan->par = NULL; | ||
| 1336 | |||
| 1337 | return rc; | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | static void __devinit tdfxfb_create_i2c_busses(struct fb_info *info) | ||
| 1341 | { | ||
| 1342 | struct tdfx_par *par = info->par; | ||
| 1343 | |||
| 1344 | tdfx_outl(par, VIDINFORMAT, 0x8160); | ||
| 1345 | tdfx_outl(par, VIDSERPARPORT, 0xcffc0020); | ||
| 1346 | |||
| 1347 | par->chan[0].par = par; | ||
| 1348 | par->chan[1].par = par; | ||
| 1349 | |||
| 1350 | tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->dev); | ||
| 1351 | tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->dev); | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | static void tdfxfb_delete_i2c_busses(struct tdfx_par *par) | ||
| 1355 | { | ||
| 1356 | if (par->chan[0].par) | ||
| 1357 | i2c_del_adapter(&par->chan[0].adapter); | ||
| 1358 | par->chan[0].par = NULL; | ||
| 1359 | |||
| 1360 | if (par->chan[1].par) | ||
| 1361 | i2c_del_adapter(&par->chan[1].adapter); | ||
| 1362 | par->chan[1].par = NULL; | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | static int tdfxfb_probe_i2c_connector(struct tdfx_par *par, | ||
| 1366 | struct fb_monspecs *specs) | ||
| 1367 | { | ||
| 1368 | u8 *edid = NULL; | ||
| 1369 | |||
| 1370 | DPRINTK("Probe DDC Bus\n"); | ||
| 1371 | if (par->chan[0].par) | ||
| 1372 | edid = fb_ddc_read(&par->chan[0].adapter); | ||
| 1373 | |||
| 1374 | if (edid) { | ||
| 1375 | fb_edid_to_monspecs(edid, specs); | ||
| 1376 | kfree(edid); | ||
| 1377 | return 0; | ||
| 1378 | } | ||
| 1379 | return 1; | ||
| 1380 | } | ||
| 1381 | #endif /* CONFIG_FB_3DFX_I2C */ | ||
| 1382 | |||
| 1170 | /** | 1383 | /** |
| 1171 | * tdfxfb_probe - Device Initializiation | 1384 | * tdfxfb_probe - Device Initializiation |
| 1172 | * | 1385 | * |
| @@ -1182,6 +1395,8 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev, | |||
| 1182 | struct tdfx_par *default_par; | 1395 | struct tdfx_par *default_par; |
| 1183 | struct fb_info *info; | 1396 | struct fb_info *info; |
| 1184 | int err, lpitch; | 1397 | int err, lpitch; |
| 1398 | struct fb_monspecs *specs; | ||
| 1399 | bool found; | ||
| 1185 | 1400 | ||
| 1186 | err = pci_enable_device(pdev); | 1401 | err = pci_enable_device(pdev); |
| 1187 | if (err) { | 1402 | if (err) { |
| @@ -1284,13 +1499,49 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev, | |||
| 1284 | if (hwcursor) | 1499 | if (hwcursor) |
| 1285 | info->fix.smem_len = (info->fix.smem_len - 1024) & | 1500 | info->fix.smem_len = (info->fix.smem_len - 1024) & |
| 1286 | (PAGE_MASK << 1); | 1501 | (PAGE_MASK << 1); |
| 1287 | 1502 | specs = &info->monspecs; | |
| 1288 | if (!mode_option) | 1503 | found = false; |
| 1504 | info->var.bits_per_pixel = 8; | ||
| 1505 | #ifdef CONFIG_FB_3DFX_I2C | ||
| 1506 | tdfxfb_create_i2c_busses(info); | ||
| 1507 | err = tdfxfb_probe_i2c_connector(default_par, specs); | ||
| 1508 | |||
| 1509 | if (!err) { | ||
| 1510 | if (specs->modedb == NULL) | ||
| 1511 | DPRINTK("Unable to get Mode Database\n"); | ||
| 1512 | else { | ||
| 1513 | const struct fb_videomode *m; | ||
| 1514 | |||
| 1515 | fb_videomode_to_modelist(specs->modedb, | ||
| 1516 | specs->modedb_len, | ||
| 1517 | &info->modelist); | ||
| 1518 | m = fb_find_best_display(specs, &info->modelist); | ||
| 1519 | if (m) { | ||
| 1520 | fb_videomode_to_var(&info->var, m); | ||
| 1521 | /* fill all other info->var's fields */ | ||
| 1522 | if (tdfxfb_check_var(&info->var, info) < 0) | ||
| 1523 | info->var = tdfx_var; | ||
| 1524 | else | ||
| 1525 | found = true; | ||
| 1526 | } | ||
| 1527 | } | ||
| 1528 | } | ||
| 1529 | #endif | ||
| 1530 | if (!mode_option && !found) | ||
| 1289 | mode_option = "640x480@60"; | 1531 | mode_option = "640x480@60"; |
| 1290 | 1532 | ||
| 1291 | err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); | 1533 | if (mode_option) { |
| 1292 | if (!err || err == 4) | 1534 | err = fb_find_mode(&info->var, info, mode_option, |
| 1293 | info->var = tdfx_var; | 1535 | specs->modedb, specs->modedb_len, |
| 1536 | NULL, info->var.bits_per_pixel); | ||
| 1537 | if (!err || err == 4) | ||
| 1538 | info->var = tdfx_var; | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | if (found) { | ||
| 1542 | fb_destroy_modedb(specs->modedb); | ||
| 1543 | specs->modedb = NULL; | ||
| 1544 | } | ||
| 1294 | 1545 | ||
| 1295 | /* maximize virtual vertical length */ | 1546 | /* maximize virtual vertical length */ |
| 1296 | lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3); | 1547 | lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3); |
| @@ -1315,6 +1566,9 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev, | |||
| 1315 | return 0; | 1566 | return 0; |
| 1316 | 1567 | ||
| 1317 | out_err_iobase: | 1568 | out_err_iobase: |
| 1569 | #ifdef CONFIG_FB_3DFX_I2C | ||
| 1570 | tdfxfb_delete_i2c_busses(default_par); | ||
| 1571 | #endif | ||
| 1318 | if (default_par->mtrr_handle >= 0) | 1572 | if (default_par->mtrr_handle >= 0) |
| 1319 | mtrr_del(default_par->mtrr_handle, info->fix.smem_start, | 1573 | mtrr_del(default_par->mtrr_handle, info->fix.smem_start, |
| 1320 | info->fix.smem_len); | 1574 | info->fix.smem_len); |
| @@ -1379,6 +1633,9 @@ static void __devexit tdfxfb_remove(struct pci_dev *pdev) | |||
| 1379 | struct tdfx_par *par = info->par; | 1633 | struct tdfx_par *par = info->par; |
| 1380 | 1634 | ||
| 1381 | unregister_framebuffer(info); | 1635 | unregister_framebuffer(info); |
| 1636 | #ifdef CONFIG_FB_3DFX_I2C | ||
| 1637 | tdfxfb_delete_i2c_busses(par); | ||
| 1638 | #endif | ||
| 1382 | if (par->mtrr_handle >= 0) | 1639 | if (par->mtrr_handle >= 0) |
| 1383 | mtrr_del(par->mtrr_handle, info->fix.smem_start, | 1640 | mtrr_del(par->mtrr_handle, info->fix.smem_start, |
| 1384 | info->fix.smem_len); | 1641 | info->fix.smem_len); |
