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); |