diff options
Diffstat (limited to 'drivers/video/tdfxfb.c')
-rw-r--r-- | drivers/video/tdfxfb.c | 200 |
1 files changed, 199 insertions, 1 deletions
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); |