123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved.
- * Copyright 2009 Jonathan Corbet <corbet@lwn.net>
- */
- /*
- * Core code for the Via multifunction framebuffer device.
- */
- #include "via-core.h"
- #include "via_i2c.h"
- #include "via-gpio.h"
- #include "global.h"
- #include <linux/module.h>
- #include <linux/platform_device.h>
- /*
- * The default port config.
- */
- static struct via_port_cfg adap_configs[] = {
- [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_OFF, VIASR, 0x26 },
- [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 },
- [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 },
- [VIA_PORT_2C] = { VIA_PORT_GPIO, VIA_MODE_I2C, VIASR, 0x2c },
- [VIA_PORT_3D] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d },
- { 0, 0, 0, 0 }
- };
- /*
- * We currently only support one viafb device (will there ever be
- * more than one?), so just declare it globally here.
- */
- static struct viafb_dev global_dev;
- /*
- * Figure out how big our framebuffer memory is. Kind of ugly,
- * but evidently we can't trust the information found in the
- * fbdev configuration area.
- */
- static u16 via_function3[] = {
- CLE266_FUNCTION3, KM400_FUNCTION3, CN400_FUNCTION3, CN700_FUNCTION3,
- CX700_FUNCTION3, KM800_FUNCTION3, KM890_FUNCTION3, P4M890_FUNCTION3,
- P4M900_FUNCTION3, VX800_FUNCTION3, VX855_FUNCTION3,
- };
- /* Get the BIOS-configured framebuffer size from PCI configuration space
- * of function 3 in the respective chipset */
- static int viafb_get_fb_size_from_pci(int chip_type)
- {
- int i;
- u8 offset = 0;
- u32 FBSize;
- u32 VideoMemSize;
- /* search for the "FUNCTION3" device in this chipset */
- for (i = 0; i < ARRAY_SIZE(via_function3); i++) {
- struct pci_dev *pdev;
- pdev = pci_get_device(PCI_VENDOR_ID_VIA, via_function3[i],
- NULL);
- if (!pdev)
- continue;
- DEBUG_MSG(KERN_INFO "Device ID = %x\n", pdev->device);
- switch (pdev->device) {
- case CLE266_FUNCTION3:
- case KM400_FUNCTION3:
- offset = 0xE0;
- break;
- case CN400_FUNCTION3:
- case CN700_FUNCTION3:
- case CX700_FUNCTION3:
- case KM800_FUNCTION3:
- case KM890_FUNCTION3:
- case P4M890_FUNCTION3:
- case P4M900_FUNCTION3:
- case VX800_FUNCTION3:
- case VX855_FUNCTION3:
- /*case CN750_FUNCTION3: */
- offset = 0xA0;
- break;
- }
- if (!offset)
- break;
- pci_read_config_dword(pdev, offset, &FBSize);
- pci_dev_put(pdev);
- }
- if (!offset) {
- printk(KERN_ERR "cannot determine framebuffer size\n");
- return -EIO;
- }
- FBSize = FBSize & 0x00007000;
- DEBUG_MSG(KERN_INFO "FB Size = %x\n", FBSize);
- if (chip_type < UNICHROME_CX700) {
- switch (FBSize) {
- case 0x00004000:
- VideoMemSize = (16 << 20); /*16M */
- break;
- case 0x00005000:
- VideoMemSize = (32 << 20); /*32M */
- break;
- case 0x00006000:
- VideoMemSize = (64 << 20); /*64M */
- break;
- default:
- VideoMemSize = (32 << 20); /*32M */
- break;
- }
- } else {
- switch (FBSize) {
- case 0x00001000:
- VideoMemSize = (8 << 20); /*8M */
- break;
- case 0x00002000:
- VideoMemSize = (16 << 20); /*16M */
- break;
- case 0x00003000:
- VideoMemSize = (32 << 20); /*32M */
- break;
- case 0x00004000:
- VideoMemSize = (64 << 20); /*64M */
- break;
- case 0x00005000:
- VideoMemSize = (128 << 20); /*128M */
- break;
- case 0x00006000:
- VideoMemSize = (256 << 20); /*256M */
- break;
- case 0x00007000: /* Only on VX855/875 */
- VideoMemSize = (512 << 20); /*512M */
- break;
- default:
- VideoMemSize = (32 << 20); /*32M */
- break;
- }
- }
- return VideoMemSize;
- }
- /*
- * Figure out and map our MMIO regions.
- */
- static int __devinit via_pci_setup_mmio(struct viafb_dev *vdev)
- {
- /*
- * Hook up to the device registers.
- */
- vdev->engine_start = pci_resource_start(vdev->pdev, 1);
- vdev->engine_len = pci_resource_len(vdev->pdev, 1);
- /* If this fails, others will notice later */
- vdev->engine_mmio = ioremap_nocache(vdev->engine_start,
- vdev->engine_len);
- /*
- * Likewise with I/O memory.
- */
- vdev->fbmem_start = pci_resource_start(vdev->pdev, 0);
- vdev->fbmem_len = viafb_get_fb_size_from_pci(vdev->chip_type);
- if (vdev->fbmem_len < 0)
- return vdev->fbmem_len;
- vdev->fbmem = ioremap_nocache(vdev->fbmem_start, vdev->fbmem_len);
- if (vdev->fbmem == NULL)
- return -ENOMEM;
- return 0;
- }
- static void __devexit via_pci_teardown_mmio(struct viafb_dev *vdev)
- {
- iounmap(vdev->fbmem);
- iounmap(vdev->engine_mmio);
- }
- /*
- * Create our subsidiary devices.
- */
- static struct viafb_subdev_info {
- char *name;
- struct platform_device *platdev;
- } viafb_subdevs[] = {
- {
- .name = "viafb-gpio",
- },
- {
- .name = "viafb-i2c",
- }
- };
- #define N_SUBDEVS ARRAY_SIZE(viafb_subdevs)
- static int __devinit via_create_subdev(struct viafb_dev *vdev,
- struct viafb_subdev_info *info)
- {
- int ret;
- info->platdev = platform_device_alloc(info->name, -1);
- if (!info->platdev) {
- dev_err(&vdev->pdev->dev, "Unable to allocate pdev %s\n",
- info->name);
- return -ENOMEM;
- }
- info->platdev->dev.parent = &vdev->pdev->dev;
- info->platdev->dev.platform_data = vdev;
- ret = platform_device_add(info->platdev);
- if (ret) {
- dev_err(&vdev->pdev->dev, "Unable to add pdev %s\n",
- info->name);
- platform_device_put(info->platdev);
- info->platdev = NULL;
- }
- return ret;
- }
- static int __devinit via_setup_subdevs(struct viafb_dev *vdev)
- {
- int i;
- /*
- * Ignore return values. Even if some of the devices
- * fail to be created, we'll still be able to use some
- * of the rest.
- */
- for (i = 0; i < N_SUBDEVS; i++)
- via_create_subdev(vdev, viafb_subdevs + i);
- return 0;
- }
- static void __devexit via_teardown_subdevs(void)
- {
- int i;
- for (i = 0; i < N_SUBDEVS; i++)
- if (viafb_subdevs[i].platdev) {
- viafb_subdevs[i].platdev->dev.platform_data = NULL;
- platform_device_unregister(viafb_subdevs[i].platdev);
- }
- }
- static int __devinit via_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
- {
- int ret;
- ret = pci_enable_device(pdev);
- if (ret)
- return ret;
- /*
- * Global device initialization.
- */
- memset(&global_dev, 0, sizeof(global_dev));
- global_dev.pdev = pdev;
- global_dev.chip_type = ent->driver_data;
- global_dev.port_cfg = adap_configs;
- spin_lock_init(&global_dev.reg_lock);
- ret = via_pci_setup_mmio(&global_dev);
- if (ret)
- goto out_disable;
- /*
- * Create our subdevices. Continue even if some things fail.
- */
- via_setup_subdevs(&global_dev);
- /*
- * Set up the framebuffer.
- */
- ret = via_fb_pci_probe(&global_dev);
- if (ret)
- goto out_subdevs;
- return 0;
- out_subdevs:
- via_teardown_subdevs();
- via_pci_teardown_mmio(&global_dev);
- out_disable:
- pci_disable_device(pdev);
- return ret;
- }
- static void __devexit via_pci_remove(struct pci_dev *pdev)
- {
- via_teardown_subdevs();
- via_fb_pci_remove(pdev);
- via_pci_teardown_mmio(&global_dev);
- pci_disable_device(pdev);
- }
- static struct pci_device_id via_pci_table[] __devinitdata = {
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID),
- .driver_data = UNICHROME_CLE266 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID),
- .driver_data = UNICHROME_PM800 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID),
- .driver_data = UNICHROME_K400 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID),
- .driver_data = UNICHROME_K800 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID),
- .driver_data = UNICHROME_CN700 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID),
- .driver_data = UNICHROME_K8M890 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID),
- .driver_data = UNICHROME_CX700 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID),
- .driver_data = UNICHROME_P4M900 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID),
- .driver_data = UNICHROME_CN750 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID),
- .driver_data = UNICHROME_VX800 },
- { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID),
- .driver_data = UNICHROME_VX855 },
- { }
- };
- MODULE_DEVICE_TABLE(pci, via_pci_table);
- static struct pci_driver via_driver = {
- .name = "viafb",
- .id_table = via_pci_table,
- .probe = via_pci_probe,
- .remove = __devexit_p(via_pci_remove),
- };
- static int __init via_core_init(void)
- {
- int ret;
- ret = viafb_init();
- if (ret)
- return ret;
- viafb_i2c_init();
- viafb_gpio_init();
- return pci_register_driver(&via_driver);
- }
- static void __exit via_core_exit(void)
- {
- pci_unregister_driver(&via_driver);
- viafb_gpio_exit();
- viafb_i2c_exit();
- viafb_exit();
- }
- module_init(via_core_init);
- module_exit(via_core_exit);
|