// SPDX-License-Identifier: GPL-2.0-only /************************************************************************** * Copyright (c) 2007-2011, Intel Corporation. * All Rights Reserved. * Copyright (c) 2008, Tungsten Graphics, Inc. Cedar Park, TX., USA. * All Rights Reserved. * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "framebuffer.h" #include "gem.h" #include "intel_bios.h" #include "mid_bios.h" #include "power.h" #include "psb_drv.h" #include "psb_intel_reg.h" #include "psb_irq.h" #include "psb_reg.h" static const struct drm_driver driver; static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); /* * The table below contains a mapping of the PCI vendor ID and the PCI Device ID * to the different groups of PowerVR 5-series chip designs * * 0x8086 = Intel Corporation * * PowerVR SGX535 - Poulsbo - Intel GMA 500, Intel Atom Z5xx * PowerVR SGX535 - Moorestown - Intel GMA 600 * PowerVR SGX535 - Oaktrail - Intel GMA 600, Intel Atom Z6xx, E6xx * PowerVR SGX545 - Cedartrail - Intel GMA 3600, Intel Atom D2500, N2600 * PowerVR SGX545 - Cedartrail - Intel GMA 3650, Intel Atom D2550, D2700, * N2800 */ static const struct pci_device_id pciidlist[] = { /* Poulsbo */ { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, /* Oak Trail */ { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, /* Cedar Trail */ { 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, { 0, } }; MODULE_DEVICE_TABLE(pci, pciidlist); /* * Standard IOCTLs. */ static const struct drm_ioctl_desc psb_ioctls[] = { }; /** * psb_spank - reset the 2D engine * @dev_priv: our PSB DRM device * * Soft reset the graphics engine and then reload the necessary registers. */ static void psb_spank(struct drm_psb_private *dev_priv) { PSB_WSGX32(_PSB_CS_RESET_BIF_RESET | _PSB_CS_RESET_DPM_RESET | _PSB_CS_RESET_TA_RESET | _PSB_CS_RESET_USE_RESET | _PSB_CS_RESET_ISP_RESET | _PSB_CS_RESET_TSP_RESET | _PSB_CS_RESET_TWOD_RESET, PSB_CR_SOFT_RESET); PSB_RSGX32(PSB_CR_SOFT_RESET); msleep(1); PSB_WSGX32(0, PSB_CR_SOFT_RESET); wmb(); PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_CB_CTRL_CLEAR_FAULT, PSB_CR_BIF_CTRL); wmb(); (void) PSB_RSGX32(PSB_CR_BIF_CTRL); msleep(1); PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_CB_CTRL_CLEAR_FAULT, PSB_CR_BIF_CTRL); (void) PSB_RSGX32(PSB_CR_BIF_CTRL); PSB_WSGX32(dev_priv->gtt.gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); } static int psb_do_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct psb_gtt *pg = &dev_priv->gtt; uint32_t stolen_gtt; if (pg->mmu_gatt_start & 0x0FFFFFFF) { dev_err(dev->dev, "Gatt must be 256M aligned. This is a bug.\n"); return -EINVAL; } stolen_gtt = (pg->stolen_size >> PAGE_SHIFT) * 4; stolen_gtt = (stolen_gtt + PAGE_SIZE - 1) >> PAGE_SHIFT; stolen_gtt = (stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages; dev_priv->gatt_free_offset = pg->mmu_gatt_start + (stolen_gtt << PAGE_SHIFT) * 1024; spin_lock_init(&dev_priv->irqmask_lock); PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0); PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1); PSB_RSGX32(PSB_CR_BIF_BANK1); /* Do not bypass any MMU access, let them pagefault instead */ PSB_WSGX32((PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_MMU_ER_MASK), PSB_CR_BIF_CTRL); PSB_RSGX32(PSB_CR_BIF_CTRL); psb_spank(dev_priv); /* mmu_gatt ?? */ PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); PSB_RSGX32(PSB_CR_BIF_TWOD_REQ_BASE); /* Post */ return 0; } static void psb_driver_unload(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); /* TODO: Kill vblank etc here */ if (dev_priv->backlight_device) gma_backlight_exit(dev); psb_modeset_cleanup(dev); gma_irq_uninstall(dev); if (dev_priv->ops->chip_teardown) dev_priv->ops->chip_teardown(dev); psb_intel_opregion_fini(dev); if (dev_priv->pf_pd) { psb_mmu_free_pagedir(dev_priv->pf_pd); dev_priv->pf_pd = NULL; } if (dev_priv->mmu) { struct psb_gtt *pg = &dev_priv->gtt; psb_mmu_remove_pfn_sequence( psb_mmu_get_default_pd (dev_priv->mmu), pg->mmu_gatt_start, dev_priv->vram_stolen_size >> PAGE_SHIFT); psb_mmu_driver_takedown(dev_priv->mmu); dev_priv->mmu = NULL; } psb_gem_mm_fini(dev); psb_gtt_fini(dev); if (dev_priv->scratch_page) { set_pages_wb(dev_priv->scratch_page, 1); __free_page(dev_priv->scratch_page); dev_priv->scratch_page = NULL; } if (dev_priv->vdc_reg) { iounmap(dev_priv->vdc_reg); dev_priv->vdc_reg = NULL; } if (dev_priv->sgx_reg) { iounmap(dev_priv->sgx_reg); dev_priv->sgx_reg = NULL; } if (dev_priv->aux_reg) { iounmap(dev_priv->aux_reg); dev_priv->aux_reg = NULL; } pci_dev_put(dev_priv->aux_pdev); pci_dev_put(dev_priv->lpc_pdev); /* Destroy VBT data */ psb_intel_destroy_bios(dev); gma_power_uninit(dev); } static void psb_device_release(void *data) { struct drm_device *dev = data; psb_driver_unload(dev); } static int psb_driver_load(struct drm_device *dev, unsigned long flags) { struct pci_dev *pdev = to_pci_dev(dev->dev); struct drm_psb_private *dev_priv = to_drm_psb_private(dev); unsigned long resource_start, resource_len; unsigned long irqflags; struct drm_connector_list_iter conn_iter; struct drm_connector *connector; struct gma_encoder *gma_encoder; struct psb_gtt *pg; int ret = -ENOMEM; /* initializing driver private data */ dev_priv->ops = (struct psb_ops *)flags; pg = &dev_priv->gtt; pci_set_master(pdev); dev_priv->num_pipe = dev_priv->ops->pipes; resource_start = pci_resource_start(pdev, PSB_MMIO_RESOURCE); dev_priv->vdc_reg = ioremap(resource_start + PSB_VDC_OFFSET, PSB_VDC_SIZE); if (!dev_priv->vdc_reg) goto out_err; dev_priv->sgx_reg = ioremap(resource_start + dev_priv->ops->sgx_offset, PSB_SGX_SIZE); if (!dev_priv->sgx_reg) goto out_err; if (IS_MRST(dev)) { int domain = pci_domain_nr(pdev->bus); dev_priv->aux_pdev = pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(3, 0)); if (dev_priv->aux_pdev) { resource_start = pci_resource_start(dev_priv->aux_pdev, PSB_AUX_RESOURCE); resource_len = pci_resource_len(dev_priv->aux_pdev, PSB_AUX_RESOURCE); dev_priv->aux_reg = ioremap(resource_start, resource_len); if (!dev_priv->aux_reg) goto out_err; DRM_DEBUG_KMS("Found aux vdc"); } else { /* Couldn't find the aux vdc so map to primary vdc */ dev_priv->aux_reg = dev_priv->vdc_reg; DRM_DEBUG_KMS("Couldn't find aux pci device"); } dev_priv->gmbus_reg = dev_priv->aux_reg; dev_priv->lpc_pdev = pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(31, 0)); if (dev_priv->lpc_pdev) { pci_read_config_word(dev_priv->lpc_pdev, PSB_LPC_GBA, &dev_priv->lpc_gpio_base); pci_write_config_dword(dev_priv->lpc_pdev, PSB_LPC_GBA, (u32)dev_priv->lpc_gpio_base | (1L<<31)); pci_read_config_word(dev_priv->lpc_pdev, PSB_LPC_GBA, &dev_priv->lpc_gpio_base); dev_priv->lpc_gpio_base &= 0xffc0; if (dev_priv->lpc_gpio_base) DRM_DEBUG_KMS("Found LPC GPIO at 0x%04x\n", dev_priv->lpc_gpio_base); else { pci_dev_put(dev_priv->lpc_pdev); dev_priv->lpc_pdev = NULL; } } } else { dev_priv->gmbus_reg = dev_priv->vdc_reg; } psb_intel_opregion_setup(dev); ret = dev_priv->ops->chip_setup(dev); if (ret) goto out_err; /* Init OSPM support */ gma_power_init(dev); ret = -ENOMEM; dev_priv->scratch_page = alloc_page(GFP_DMA32 | __GFP_ZERO); if (!dev_priv->scratch_page) goto out_err; set_pages_uc(dev_priv->scratch_page, 1); ret = psb_gtt_init(dev); if (ret) goto out_err; ret = psb_gem_mm_init(dev); if (ret) goto out_err; ret = -ENOMEM; dev_priv->mmu = psb_mmu_driver_init(dev, 1, 0, NULL); if (!dev_priv->mmu) goto out_err; dev_priv->pf_pd = psb_mmu_alloc_pd(dev_priv->mmu, 1, 0); if (!dev_priv->pf_pd) goto out_err; ret = psb_do_init(dev); if (ret) return ret; /* Add stolen memory to SGX MMU */ ret = psb_mmu_insert_pfn_sequence(psb_mmu_get_default_pd(dev_priv->mmu), dev_priv->stolen_base >> PAGE_SHIFT, pg->gatt_start, pg->stolen_size >> PAGE_SHIFT, 0); psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0); psb_mmu_set_pd_context(dev_priv->pf_pd, 1); PSB_WSGX32(0x20000000, PSB_CR_PDS_EXEC_BASE); PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE); acpi_video_register(); /* Setup vertical blanking handling */ ret = drm_vblank_init(dev, dev_priv->num_pipe); if (ret) goto out_err; /* * Install interrupt handlers prior to powering off SGX or else we will * crash. */ dev_priv->vdc_irq_mask = 0; dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; dev_priv->pipestat[2] = 0; spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); gma_irq_install(dev); dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ psb_modeset_init(dev); psb_fbdev_init(dev); drm_kms_helper_poll_init(dev); /* Only add backlight support if we have LVDS or MIPI output */ drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { gma_encoder = gma_attached_encoder(connector); if (gma_encoder->type == INTEL_OUTPUT_LVDS || gma_encoder->type == INTEL_OUTPUT_MIPI) { ret = gma_backlight_init(dev); break; } } drm_connector_list_iter_end(&conn_iter); if (ret) return ret; psb_intel_opregion_enable_asle(dev); #if 0 /* Enable runtime pm at last */ pm_runtime_enable(dev->dev); pm_runtime_set_active(dev->dev); #endif return devm_add_action_or_reset(dev->dev, psb_device_release, dev); out_err: psb_driver_unload(dev); return ret; } static inline void get_brightness(struct backlight_device *bd) { #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE if (bd) { bd->props.brightness = bd->ops->get_brightness(bd); backlight_update_status(bd); } #endif } static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct drm_file *file_priv = filp->private_data; struct drm_device *dev = file_priv->minor->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); static unsigned int runtime_allowed; if (runtime_allowed == 1 && dev_priv->is_lvds_on) { runtime_allowed++; pm_runtime_allow(dev->dev); dev_priv->rpm_enabled = 1; } return drm_ioctl(filp, cmd, arg); /* FIXME: do we need to wrap the other side of this */ } static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct drm_psb_private *dev_priv; struct drm_device *dev; int ret; /* * We cannot yet easily find the framebuffer's location in memory. So * remove all framebuffers here. * * TODO: Refactor psb_driver_load() to map vdc_reg earlier. Then we * might be able to read the framebuffer range from the device. */ ret = drm_aperture_remove_framebuffers(true, &driver); if (ret) return ret; ret = pcim_enable_device(pdev); if (ret) return ret; dev_priv = devm_drm_dev_alloc(&pdev->dev, &driver, struct drm_psb_private, dev); if (IS_ERR(dev_priv)) return PTR_ERR(dev_priv); dev = &dev_priv->dev; pci_set_drvdata(pdev, dev); ret = psb_driver_load(dev, ent->driver_data); if (ret) return ret; ret = drm_dev_register(dev, ent->driver_data); if (ret) return ret; return 0; } static void psb_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); drm_dev_unregister(dev); } static const struct dev_pm_ops psb_pm_ops = { .resume = gma_power_resume, .suspend = gma_power_suspend, .thaw = gma_power_thaw, .freeze = gma_power_freeze, .restore = gma_power_restore, .runtime_suspend = psb_runtime_suspend, .runtime_resume = psb_runtime_resume, .runtime_idle = psb_runtime_idle, }; static const struct file_operations psb_gem_fops = { .owner = THIS_MODULE, .open = drm_open, .release = drm_release, .unlocked_ioctl = psb_unlocked_ioctl, .compat_ioctl = drm_compat_ioctl, .mmap = drm_gem_mmap, .poll = drm_poll, .read = drm_read, }; static const struct drm_driver driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM, .lastclose = drm_fb_helper_lastclose, .num_ioctls = ARRAY_SIZE(psb_ioctls), .dumb_create = psb_gem_dumb_create, .ioctls = psb_ioctls, .fops = &psb_gem_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL }; static struct pci_driver psb_pci_driver = { .name = DRIVER_NAME, .id_table = pciidlist, .probe = psb_pci_probe, .remove = psb_pci_remove, .driver.pm = &psb_pm_ops, }; static int __init psb_init(void) { if (drm_firmware_drivers_only()) return -ENODEV; return pci_register_driver(&psb_pci_driver); } static void __exit psb_exit(void) { pci_unregister_driver(&psb_pci_driver); } late_initcall(psb_init); module_exit(psb_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL");