/************************************************************************** * Copyright (c) 2007, Intel Corporation. * All Rights Reserved. * Copyright (c) 2008, Tungsten Graphics, Inc. Cedar Park, TX., USA. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * **************************************************************************/ #include <drm/drmP.h> #include <drm/drm.h> #include "psb_drm.h" #include "psb_drv.h" #include "psb_fb.h" #include "psb_reg.h" #include "psb_intel_reg.h" #include "psb_intel_bios.h" #include <drm/drm_pciids.h> #include "psb_powermgmt.h" #include <linux/cpu.h> #include <linux/notifier.h> #include <linux/spinlock.h> #include <linux/pm_runtime.h> #include <acpi/video.h> int drm_psb_debug; static int drm_psb_trap_pagefaults; int drm_psb_disable_vsync = 1; int drm_psb_no_fb; int gfxrtdelay = 2 * 1000; static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent); MODULE_PARM_DESC(debug, "Enable debug output"); MODULE_PARM_DESC(no_fb, "Disable FBdev"); MODULE_PARM_DESC(trap_pagefaults, "Error and reset on MMU pagefaults"); MODULE_PARM_DESC(disable_vsync, "Disable vsync interrupts"); MODULE_PARM_DESC(force_pipeb, "Forces PIPEB to become primary fb"); MODULE_PARM_DESC(ta_mem_size, "TA memory size in kiB"); MODULE_PARM_DESC(ospm, "switch for ospm support"); MODULE_PARM_DESC(rtpm, "Specifies Runtime PM delay for GFX"); MODULE_PARM_DESC(hdmi_edid, "EDID info for HDMI monitor"); module_param_named(debug, drm_psb_debug, int, 0600); module_param_named(no_fb, drm_psb_no_fb, int, 0600); module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600); module_param_named(rtpm, gfxrtdelay, int, 0600); static struct pci_device_id pciidlist[] = { { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8108 }, { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PSB_8109 }, { 0, 0, 0} }; MODULE_DEVICE_TABLE(pci, pciidlist); /* * Standard IOCTLs. */ #define DRM_IOCTL_PSB_KMS_OFF \ DRM_IO(DRM_PSB_KMS_OFF + DRM_COMMAND_BASE) #define DRM_IOCTL_PSB_KMS_ON \ DRM_IO(DRM_PSB_KMS_ON + DRM_COMMAND_BASE) #define DRM_IOCTL_PSB_VT_LEAVE \ DRM_IO(DRM_PSB_VT_LEAVE + DRM_COMMAND_BASE) #define DRM_IOCTL_PSB_VT_ENTER \ DRM_IO(DRM_PSB_VT_ENTER + DRM_COMMAND_BASE) #define DRM_IOCTL_PSB_SIZES \ DRM_IOR(DRM_PSB_SIZES + DRM_COMMAND_BASE, \ struct drm_psb_sizes_arg) #define DRM_IOCTL_PSB_FUSE_REG \ DRM_IOWR(DRM_PSB_FUSE_REG + DRM_COMMAND_BASE, uint32_t) #define DRM_IOCTL_PSB_DC_STATE \ DRM_IOW(DRM_PSB_DC_STATE + DRM_COMMAND_BASE, \ struct drm_psb_dc_state_arg) #define DRM_IOCTL_PSB_ADB \ DRM_IOWR(DRM_PSB_ADB + DRM_COMMAND_BASE, uint32_t) #define DRM_IOCTL_PSB_MODE_OPERATION \ DRM_IOWR(DRM_PSB_MODE_OPERATION + DRM_COMMAND_BASE, \ struct drm_psb_mode_operation_arg) #define DRM_IOCTL_PSB_STOLEN_MEMORY \ DRM_IOWR(DRM_PSB_STOLEN_MEMORY + DRM_COMMAND_BASE, \ struct drm_psb_stolen_memory_arg) #define DRM_IOCTL_PSB_REGISTER_RW \ DRM_IOWR(DRM_PSB_REGISTER_RW + DRM_COMMAND_BASE, \ struct drm_psb_register_rw_arg) #define DRM_IOCTL_PSB_GTT_MAP \ DRM_IOWR(DRM_PSB_GTT_MAP + DRM_COMMAND_BASE, \ struct psb_gtt_mapping_arg) #define DRM_IOCTL_PSB_GTT_UNMAP \ DRM_IOW(DRM_PSB_GTT_UNMAP + DRM_COMMAND_BASE, \ struct psb_gtt_mapping_arg) #define DRM_IOCTL_PSB_GETPAGEADDRS \ DRM_IOWR(DRM_COMMAND_BASE + DRM_PSB_GETPAGEADDRS,\ struct drm_psb_getpageaddrs_arg) #define DRM_IOCTL_PSB_UPDATE_GUARD \ DRM_IOWR(DRM_PSB_UPDATE_GUARD + DRM_COMMAND_BASE, \ uint32_t) #define DRM_IOCTL_PSB_DPST \ DRM_IOWR(DRM_PSB_DPST + DRM_COMMAND_BASE, \ uint32_t) #define DRM_IOCTL_PSB_GAMMA \ DRM_IOWR(DRM_PSB_GAMMA + DRM_COMMAND_BASE, \ struct drm_psb_dpst_lut_arg) #define DRM_IOCTL_PSB_DPST_BL \ DRM_IOWR(DRM_PSB_DPST_BL + DRM_COMMAND_BASE, \ uint32_t) #define DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID \ DRM_IOWR(DRM_PSB_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \ struct drm_psb_get_pipe_from_crtc_id_arg) /* * TTM execbuf extension. */ #define DRM_PSB_CMDBUF 0x23 #define DRM_PSB_SCENE_UNREF 0x24 #define DRM_IOCTL_PSB_KMS_OFF DRM_IO(DRM_PSB_KMS_OFF + DRM_COMMAND_BASE) #define DRM_IOCTL_PSB_KMS_ON DRM_IO(DRM_PSB_KMS_ON + DRM_COMMAND_BASE) /* * TTM placement user extension. */ #define DRM_PSB_PLACEMENT_OFFSET (DRM_PSB_SCENE_UNREF + 1) #define DRM_PSB_TTM_PL_CREATE (TTM_PL_CREATE + DRM_PSB_PLACEMENT_OFFSET) #define DRM_PSB_TTM_PL_REFERENCE (TTM_PL_REFERENCE + DRM_PSB_PLACEMENT_OFFSET) #define DRM_PSB_TTM_PL_UNREF (TTM_PL_UNREF + DRM_PSB_PLACEMENT_OFFSET) #define DRM_PSB_TTM_PL_SYNCCPU (TTM_PL_SYNCCPU + DRM_PSB_PLACEMENT_OFFSET) #define DRM_PSB_TTM_PL_WAITIDLE (TTM_PL_WAITIDLE + DRM_PSB_PLACEMENT_OFFSET) #define DRM_PSB_TTM_PL_SETSTATUS (TTM_PL_SETSTATUS + DRM_PSB_PLACEMENT_OFFSET) #define DRM_PSB_TTM_PL_CREATE_UB (TTM_PL_CREATE_UB + DRM_PSB_PLACEMENT_OFFSET) /* * TTM fence extension. */ #define DRM_PSB_FENCE_OFFSET (DRM_PSB_TTM_PL_CREATE_UB + 1) #define DRM_PSB_TTM_FENCE_SIGNALED (TTM_FENCE_SIGNALED + DRM_PSB_FENCE_OFFSET) #define DRM_PSB_TTM_FENCE_FINISH (TTM_FENCE_FINISH + DRM_PSB_FENCE_OFFSET) #define DRM_PSB_TTM_FENCE_UNREF (TTM_FENCE_UNREF + DRM_PSB_FENCE_OFFSET) #define DRM_PSB_FLIP (DRM_PSB_TTM_FENCE_UNREF + 1) /*20*/ #define DRM_IOCTL_PSB_TTM_PL_CREATE \ DRM_IOWR(DRM_COMMAND_BASE + DRM_PSB_TTM_PL_CREATE,\ union ttm_pl_create_arg) #define DRM_IOCTL_PSB_TTM_PL_REFERENCE \ DRM_IOWR(DRM_COMMAND_BASE + DRM_PSB_TTM_PL_REFERENCE,\ union ttm_pl_reference_arg) #define DRM_IOCTL_PSB_TTM_PL_UNREF \ DRM_IOW(DRM_COMMAND_BASE + DRM_PSB_TTM_PL_UNREF,\ struct ttm_pl_reference_req) #define DRM_IOCTL_PSB_TTM_PL_SYNCCPU \ DRM_IOW(DRM_COMMAND_BASE + DRM_PSB_TTM_PL_SYNCCPU,\ struct ttm_pl_synccpu_arg) #define DRM_IOCTL_PSB_TTM_PL_WAITIDLE \ DRM_IOW(DRM_COMMAND_BASE + DRM_PSB_TTM_PL_WAITIDLE,\ struct ttm_pl_waitidle_arg) #define DRM_IOCTL_PSB_TTM_PL_SETSTATUS \ DRM_IOWR(DRM_COMMAND_BASE + DRM_PSB_TTM_PL_SETSTATUS,\ union ttm_pl_setstatus_arg) #define DRM_IOCTL_PSB_TTM_PL_CREATE_UB \ DRM_IOWR(DRM_COMMAND_BASE + DRM_PSB_TTM_PL_CREATE_UB,\ union ttm_pl_create_ub_arg) #define DRM_IOCTL_PSB_TTM_FENCE_SIGNALED \ DRM_IOWR(DRM_COMMAND_BASE + DRM_PSB_TTM_FENCE_SIGNALED, \ union ttm_fence_signaled_arg) #define DRM_IOCTL_PSB_TTM_FENCE_FINISH \ DRM_IOWR(DRM_COMMAND_BASE + DRM_PSB_TTM_FENCE_FINISH, \ union ttm_fence_finish_arg) #define DRM_IOCTL_PSB_TTM_FENCE_UNREF \ DRM_IOW(DRM_COMMAND_BASE + DRM_PSB_TTM_FENCE_UNREF, \ struct ttm_fence_unref_arg) static int psb_vt_leave_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_vt_enter_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_sizes_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_dc_state_ioctl(struct drm_device *dev, void * data, struct drm_file *file_priv); static int psb_adb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_register_rw_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_dpst_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_gamma_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); #define PSB_IOCTL_DEF(ioctl, func, flags) \ [DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func} static struct drm_ioctl_desc psb_ioctls[] = { PSB_IOCTL_DEF(DRM_IOCTL_PSB_KMS_OFF, psbfb_kms_off_ioctl, DRM_ROOT_ONLY), PSB_IOCTL_DEF(DRM_IOCTL_PSB_KMS_ON, psbfb_kms_on_ioctl, DRM_ROOT_ONLY), PSB_IOCTL_DEF(DRM_IOCTL_PSB_VT_LEAVE, psb_vt_leave_ioctl, DRM_ROOT_ONLY), PSB_IOCTL_DEF(DRM_IOCTL_PSB_VT_ENTER, psb_vt_enter_ioctl, DRM_ROOT_ONLY), PSB_IOCTL_DEF(DRM_IOCTL_PSB_SIZES, psb_sizes_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_DC_STATE, psb_dc_state_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_ADB, psb_adb_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_MODE_OPERATION, psb_mode_operation_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_STOLEN_MEMORY, psb_stolen_memory_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_REGISTER_RW, psb_register_rw_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_GTT_MAP, psb_gtt_map_meminfo_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_GTT_UNMAP, psb_gtt_unmap_meminfo_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_GETPAGEADDRS, psb_getpageaddrs_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST, psb_dpst_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_GAMMA, psb_gamma_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID, psb_intel_get_pipe_from_crtc_id, 0), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_PL_CREATE, psb_pl_create_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_PL_REFERENCE, psb_pl_reference_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_PL_UNREF, psb_pl_unref_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_PL_SYNCCPU, psb_pl_synccpu_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_PL_WAITIDLE, psb_pl_waitidle_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_PL_SETSTATUS, psb_pl_setstatus_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_PL_CREATE_UB, psb_pl_ub_create_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_FENCE_SIGNALED, psb_fence_signaled_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_FENCE_FINISH, psb_fence_finish_ioctl, DRM_AUTH), PSB_IOCTL_DEF(DRM_IOCTL_PSB_TTM_FENCE_UNREF, psb_fence_unref_ioctl, DRM_AUTH), }; static void psb_set_uopt(struct drm_psb_uopt *uopt) { return; } static void psb_lastclose(struct drm_device *dev) { struct drm_psb_private *dev_priv = (struct drm_psb_private *) dev->dev_private; return; if (!dev->dev_private) return; mutex_lock(&dev_priv->cmdbuf_mutex); if (dev_priv->context.buffers) { vfree(dev_priv->context.buffers); dev_priv->context.buffers = NULL; } mutex_unlock(&dev_priv->cmdbuf_mutex); } static void psb_do_takedown(struct drm_device *dev) { struct drm_psb_private *dev_priv = (struct drm_psb_private *) dev->dev_private; struct ttm_bo_device *bdev = &dev_priv->bdev; if (dev_priv->have_mem_mmu) { ttm_bo_clean_mm(bdev, DRM_PSB_MEM_MMU); dev_priv->have_mem_mmu = 0; } if (dev_priv->have_tt) { ttm_bo_clean_mm(bdev, TTM_PL_TT); dev_priv->have_tt = 0; } if (dev_priv->have_camera) { ttm_bo_clean_mm(bdev, TTM_PL_CI); dev_priv->have_camera = 0; } if (dev_priv->have_rar) { ttm_bo_clean_mm(bdev, TTM_PL_RAR); dev_priv->have_rar = 0; } } static void psb_get_core_freq(struct drm_device *dev) { uint32_t clock; struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); struct drm_psb_private *dev_priv = dev->dev_private; /*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/ /*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/ pci_write_config_dword(pci_root, 0xD0, 0xD0050300); pci_read_config_dword(pci_root, 0xD4, &clock); pci_dev_put(pci_root); switch (clock & 0x07) { case 0: dev_priv->core_freq = 100; break; case 1: dev_priv->core_freq = 133; break; case 2: dev_priv->core_freq = 150; break; case 3: dev_priv->core_freq = 178; break; case 4: dev_priv->core_freq = 200; break; case 5: case 6: case 7: dev_priv->core_freq = 266; default: dev_priv->core_freq = 0; } } #define FB_REG06 0xD0810600 #define FB_TOPAZ_DISABLE BIT0 #define FB_MIPI_DISABLE BIT11 #define FB_REG09 0xD0810900 #define FB_SKU_MASK (BIT12|BIT13|BIT14) #define FB_SKU_SHIFT 12 #define FB_SKU_100 0 #define FB_SKU_100L 1 #define FB_SKU_83 2 bool mid_get_pci_revID(struct drm_psb_private *dev_priv) { uint32_t platform_rev_id = 0; struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); /*get the revison ID, B0:D2:F0;0x08 */ pci_read_config_dword(pci_gfx_root, 0x08, &platform_rev_id); dev_priv->platform_rev_id = (uint8_t) platform_rev_id; pci_dev_put(pci_gfx_root); PSB_DEBUG_ENTRY("platform_rev_id is %x\n", dev_priv->platform_rev_id); return true; } static int psb_do_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = (struct drm_psb_private *) dev->dev_private; struct ttm_bo_device *bdev = &dev_priv->bdev; struct psb_gtt *pg = dev_priv->pg; uint32_t stolen_gtt; uint32_t tt_start; uint32_t tt_pages; int ret = -ENOMEM; /* * Initialize sequence numbers for the different command * submission mechanisms. */ dev_priv->sequence[PSB_ENGINE_2D] = 0; dev_priv->sequence[PSB_ENGINE_VIDEO] = 0; dev_priv->sequence[LNC_ENGINE_ENCODE] = 0; if (pg->mmu_gatt_start & 0x0FFFFFFF) { DRM_ERROR("Gatt must be 256M aligned. This is a bug.\n"); ret = -EINVAL; goto out_err; } 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; if (1 || drm_debug) { uint32_t core_id = PSB_RSGX32(PSB_CR_CORE_ID); uint32_t core_rev = PSB_RSGX32(PSB_CR_CORE_REVISION); DRM_INFO("SGX core id = 0x%08x\n", core_id); DRM_INFO("SGX core rev major = 0x%02x, minor = 0x%02x\n", (core_rev & _PSB_CC_REVISION_MAJOR_MASK) >> _PSB_CC_REVISION_MAJOR_SHIFT, (core_rev & _PSB_CC_REVISION_MINOR_MASK) >> _PSB_CC_REVISION_MINOR_SHIFT); DRM_INFO ("SGX core rev maintenance = 0x%02x, designer = 0x%02x\n", (core_rev & _PSB_CC_REVISION_MAINTENANCE_MASK) >> _PSB_CC_REVISION_MAINTENANCE_SHIFT, (core_rev & _PSB_CC_REVISION_DESIGNER_MASK) >> _PSB_CC_REVISION_DESIGNER_SHIFT); } spin_lock_init(&dev_priv->irqmask_lock); tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ? pg->gatt_pages : PSB_TT_PRIV0_PLIMIT; tt_start = dev_priv->gatt_free_offset - pg->mmu_gatt_start; tt_pages -= tt_start >> PAGE_SHIFT; dev_priv->sizes.ta_mem_size = 0; PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0); PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1); PSB_RSGX32(PSB_CR_BIF_BANK1); PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_MMU_ER_MASK, PSB_CR_BIF_CTRL); psb_spank(dev_priv); PSB_WSGX32(pg->mmu_gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); /* TT region managed by TTM. */ if (!ttm_bo_init_mm(bdev, TTM_PL_TT, pg->gatt_pages - (pg->ci_start >> PAGE_SHIFT) - ((dev_priv->ci_region_size + dev_priv->rar_region_size) >> PAGE_SHIFT))) { dev_priv->have_tt = 1; dev_priv->sizes.tt_size = (tt_pages << PAGE_SHIFT) / (1024 * 1024) / 2; } if (!ttm_bo_init_mm(bdev, DRM_PSB_MEM_MMU, PSB_MEM_TT_START >> PAGE_SHIFT)) { dev_priv->have_mem_mmu = 1; dev_priv->sizes.mmu_size = PSB_MEM_TT_START / (1024*1024); } PSB_DEBUG_INIT("Init MSVDX\n"); return 0; out_err: psb_do_takedown(dev); return ret; } static int psb_driver_unload(struct drm_device *dev) { struct drm_psb_private *dev_priv = (struct drm_psb_private *) dev->dev_private; /* Kill vblank etc here */ psb_backlight_exit(); /*writes minimum value to backlight HW reg */ if (drm_psb_no_fb == 0) psb_modeset_cleanup(dev); if (dev_priv) { psb_lid_timer_takedown(dev_priv); psb_do_takedown(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->pg; down_read(&pg->sem); psb_mmu_remove_pfn_sequence( psb_mmu_get_default_pd (dev_priv->mmu), pg->mmu_gatt_start, pg->vram_stolen_size >> PAGE_SHIFT); if (pg->ci_stolen_size != 0) psb_mmu_remove_pfn_sequence( psb_mmu_get_default_pd (dev_priv->mmu), pg->ci_start, pg->ci_stolen_size >> PAGE_SHIFT); if (pg->rar_stolen_size != 0) psb_mmu_remove_pfn_sequence( psb_mmu_get_default_pd (dev_priv->mmu), pg->rar_start, pg->rar_stolen_size >> PAGE_SHIFT); up_read(&pg->sem); psb_mmu_driver_takedown(dev_priv->mmu); dev_priv->mmu = NULL; } psb_gtt_takedown(dev_priv->pg, 1); if (dev_priv->scratch_page) { __free_page(dev_priv->scratch_page); dev_priv->scratch_page = NULL; } if (dev_priv->has_bo_device) { ttm_bo_device_release(&dev_priv->bdev); dev_priv->has_bo_device = 0; } if (dev_priv->has_fence_device) { ttm_fence_device_release(&dev_priv->fdev); dev_priv->has_fence_device = 0; } 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->tdev) ttm_object_device_release(&dev_priv->tdev); if (dev_priv->has_global) psb_ttm_global_release(dev_priv); kfree(dev_priv); dev->dev_private = NULL; /*destroy VBT data*/ psb_intel_destroy_bios(dev); } ospm_power_uninit(); return 0; } static int psb_driver_load(struct drm_device *dev, unsigned long chipset) { struct drm_psb_private *dev_priv; struct ttm_bo_device *bdev; unsigned long resource_start; struct psb_gtt *pg; unsigned long irqflags; int ret = -ENOMEM; uint32_t tt_pages; dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); if (dev_priv == NULL) return -ENOMEM; INIT_LIST_HEAD(&dev_priv->video_ctx); dev_priv->num_pipe = 2; dev_priv->dev = dev; bdev = &dev_priv->bdev; ret = psb_ttm_global_init(dev_priv); if (unlikely(ret != 0)) goto out_err; dev_priv->has_global = 1; dev_priv->tdev = ttm_object_device_init (dev_priv->mem_global_ref.object, PSB_OBJECT_HASH_ORDER); if (unlikely(dev_priv->tdev == NULL)) goto out_err; mutex_init(&dev_priv->temp_mem); mutex_init(&dev_priv->cmdbuf_mutex); mutex_init(&dev_priv->reset_mutex); INIT_LIST_HEAD(&dev_priv->context.validate_list); INIT_LIST_HEAD(&dev_priv->context.kern_validate_list); /* mutex_init(&dev_priv->dsr_mutex); */ spin_lock_init(&dev_priv->reloc_lock); DRM_INIT_WAITQUEUE(&dev_priv->rel_mapped_queue); dev->dev_private = (void *) dev_priv; dev_priv->chipset = chipset; psb_set_uopt(&dev_priv->uopt); PSB_DEBUG_INIT("Mapping MMIO\n"); resource_start = pci_resource_start(dev->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 + PSB_SGX_OFFSET, PSB_SGX_SIZE); if (!dev_priv->sgx_reg) goto out_err; psb_get_core_freq(dev); psb_intel_opregion_init(dev); psb_intel_init_bios(dev); PSB_DEBUG_INIT("Init TTM fence and BO driver\n"); /* Init OSPM support */ ospm_power_init(dev); ret = psb_ttm_fence_device_init(&dev_priv->fdev); if (unlikely(ret != 0)) goto out_err; dev_priv->has_fence_device = 1; ret = ttm_bo_device_init(bdev, dev_priv->bo_global_ref.ref.object, &psb_ttm_bo_driver, DRM_PSB_FILE_PAGE_OFFSET, false); if (unlikely(ret != 0)) goto out_err; dev_priv->has_bo_device = 1; ttm_lock_init(&dev_priv->ttm_lock); 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); dev_priv->pg = psb_gtt_alloc(dev); if (!dev_priv->pg) goto out_err; ret = psb_gtt_init(dev_priv->pg, 0); if (ret) goto out_err; ret = psb_gtt_mm_init(dev_priv->pg); if (ret) goto out_err; dev_priv->mmu = psb_mmu_driver_init((void *)0, drm_psb_trap_pagefaults, 0, dev_priv); if (!dev_priv->mmu) goto out_err; pg = dev_priv->pg; tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ? (pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT; /* CI/RAR use the lower half of TT. */ pg->ci_start = (tt_pages / 2) << PAGE_SHIFT; pg->rar_start = pg->ci_start + pg->ci_stolen_size; /* * Make MSVDX/TOPAZ MMU aware of the CI stolen memory area. */ if (dev_priv->pg->ci_stolen_size != 0) { down_read(&pg->sem); ret = psb_mmu_insert_pfn_sequence(psb_mmu_get_default_pd (dev_priv->mmu), dev_priv->ci_region_start >> PAGE_SHIFT, pg->mmu_gatt_start + pg->ci_start, pg->ci_stolen_size >> PAGE_SHIFT, 0); up_read(&pg->sem); if (ret) goto out_err; } /* * Make MSVDX/TOPAZ MMU aware of the rar stolen memory area. */ if (dev_priv->pg->rar_stolen_size != 0) { down_read(&pg->sem); ret = psb_mmu_insert_pfn_sequence( psb_mmu_get_default_pd(dev_priv->mmu), dev_priv->rar_region_start >> PAGE_SHIFT, pg->mmu_gatt_start + pg->rar_start, pg->rar_stolen_size >> PAGE_SHIFT, 0); up_read(&pg->sem); if (ret) goto out_err; } dev_priv->pf_pd = psb_mmu_alloc_pd(dev_priv->mmu, 1, 0); if (!dev_priv->pf_pd) goto out_err; psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0); psb_mmu_set_pd_context(dev_priv->pf_pd, 1); spin_lock_init(&dev_priv->sequence_lock); PSB_DEBUG_INIT("Begin to init MSVDX/Topaz\n"); ret = psb_do_init(dev); if (ret) return ret; /* igd_opregion_init(&dev_priv->opregion_dev); */ acpi_video_register(); if (dev_priv->lid_state) psb_lid_timer_init(dev_priv); 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); if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_irq_install(dev); dev->vblank_disable_allowed = 1; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ dev->driver->get_vblank_counter = psb_get_vblank_counter; if (drm_psb_no_fb == 0) { psb_modeset_init(dev); psb_fbdev_init(dev); drm_kms_helper_poll_init(dev); } ret = psb_backlight_init(dev); if (ret) return ret; #if 0 /*enable runtime pm at last*/ pm_runtime_enable(&dev->pdev->dev); pm_runtime_set_active(&dev->pdev->dev); #endif /*Intel drm driver load is done, continue doing pvr load*/ DRM_DEBUG("Pvr driver load\n"); /* if (PVRCore_Init() < 0) goto out_err; */ /* if (MRSTLFBInit(dev) < 0) goto out_err;*/ return 0; out_err: psb_driver_unload(dev); return ret; } int psb_driver_device_is_agp(struct drm_device *dev) { return 0; } static int psb_vt_leave_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); struct ttm_bo_device *bdev = &dev_priv->bdev; struct ttm_mem_type_manager *man; int ret; ret = ttm_vt_lock(&dev_priv->ttm_lock, 1, psb_fpriv(file_priv)->tfile); if (unlikely(ret != 0)) return ret; ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_TT); if (unlikely(ret != 0)) goto out_unlock; man = &bdev->man[TTM_PL_TT]; #if 0 /* What to do with this ? */ if (unlikely(!drm_mm_clean(&man->manager))) DRM_INFO("Warning: GATT was not clean after VT switch.\n"); #endif ttm_bo_swapout_all(&dev_priv->bdev); return 0; out_unlock: (void) ttm_vt_unlock(&dev_priv->ttm_lock); return ret; } static int psb_vt_enter_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); return ttm_vt_unlock(&dev_priv->ttm_lock); } static int psb_sizes_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); struct drm_psb_sizes_arg *arg = (struct drm_psb_sizes_arg *) data; *arg = dev_priv->sizes; return 0; } static int psb_dc_state_ioctl(struct drm_device *dev, void * data, struct drm_file *file_priv) { uint32_t flags; uint32_t obj_id; struct drm_mode_object *obj; struct drm_connector *connector; struct drm_crtc *crtc; struct drm_psb_dc_state_arg *arg = (struct drm_psb_dc_state_arg *)data; flags = arg->flags; obj_id = arg->obj_id; if (flags & PSB_DC_CRTC_MASK) { obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG("Invalid CRTC object.\n"); return -EINVAL; } crtc = obj_to_crtc(obj); mutex_lock(&dev->mode_config.mutex); if (drm_helper_crtc_in_use(crtc)) { if (flags & PSB_DC_CRTC_SAVE) crtc->funcs->save(crtc); else crtc->funcs->restore(crtc); } mutex_unlock(&dev->mode_config.mutex); return 0; } else if (flags & PSB_DC_OUTPUT_MASK) { obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { DRM_DEBUG("Invalid connector id.\n"); return -EINVAL; } connector = obj_to_connector(obj); if (flags & PSB_DC_OUTPUT_SAVE) connector->funcs->save(connector); else connector->funcs->restore(connector); return 0; } DRM_DEBUG("Bad flags 0x%x\n", flags); return -EINVAL; } static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); uint32_t *arg = data; struct backlight_device bd; dev_priv->blc_adj2 = *arg; #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE bd.props.brightness = psb_get_brightness(&bd); psb_set_brightness(&bd); #endif return 0; } static int psb_adb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); uint32_t *arg = data; struct backlight_device bd; dev_priv->blc_adj1 = *arg; #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE bd.props.brightness = psb_get_brightness(&bd); psb_set_brightness(&bd); #endif return 0; } /* return the current mode to the dpst module */ static int psb_dpst_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); uint32_t *arg = data; uint32_t x; uint32_t y; uint32_t reg; if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, OSPM_UHB_ONLY_IF_ON)) return 0; reg = PSB_RVDC32(PIPEASRC); ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); /* horizontal is the left 16 bits */ x = reg >> 16; /* vertical is the right 16 bits */ y = reg & 0x0000ffff; /* the values are the image size minus one */ x++; y++; *arg = (x << 16) | y; return 0; } static int psb_gamma_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_dpst_lut_arg *lut_arg = data; struct drm_mode_object *obj; struct drm_crtc *crtc; struct drm_connector *connector; struct psb_intel_crtc *psb_intel_crtc; int i = 0; int32_t obj_id; obj_id = lut_arg->output_id; obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { DRM_DEBUG("Invalid Connector object.\n"); return -EINVAL; } connector = obj_to_connector(obj); crtc = connector->encoder->crtc; psb_intel_crtc = to_psb_intel_crtc(crtc); for (i = 0; i < 256; i++) psb_intel_crtc->lut_adj[i] = lut_arg->lut[i]; psb_intel_crtc_load_lut(crtc); return 0; } static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { uint32_t obj_id; uint16_t op; struct drm_mode_modeinfo *umode; struct drm_display_mode *mode = NULL; struct drm_psb_mode_operation_arg *arg; struct drm_mode_object *obj; struct drm_connector *connector; struct drm_framebuffer *drm_fb; struct psb_framebuffer *psb_fb; struct drm_connector_helper_funcs *connector_funcs; int ret = 0; int resp = MODE_OK; struct drm_psb_private *dev_priv = psb_priv(dev); arg = (struct drm_psb_mode_operation_arg *)data; obj_id = arg->obj_id; op = arg->operation; switch (op) { case PSB_MODE_OPERATION_SET_DC_BASE: obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_FB); if (!obj) { DRM_ERROR("Invalid FB id %d\n", obj_id); return -EINVAL; } drm_fb = obj_to_fb(obj); psb_fb = to_psb_fb(drm_fb); if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, OSPM_UHB_ONLY_IF_ON)) { REG_WRITE(DSPASURF, psb_fb->offset); REG_READ(DSPASURF); ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } else { dev_priv->saveDSPASURF = psb_fb->offset; } return 0; case PSB_MODE_OPERATION_MODE_VALID: umode = &arg->mode; mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { ret = -EINVAL; goto mode_op_out; } connector = obj_to_connector(obj); mode = drm_mode_create(dev); if (!mode) { ret = -ENOMEM; goto mode_op_out; } /* drm_crtc_convert_umode(mode, umode); */ { mode->clock = umode->clock; mode->hdisplay = umode->hdisplay; mode->hsync_start = umode->hsync_start; mode->hsync_end = umode->hsync_end; mode->htotal = umode->htotal; mode->hskew = umode->hskew; mode->vdisplay = umode->vdisplay; mode->vsync_start = umode->vsync_start; mode->vsync_end = umode->vsync_end; mode->vtotal = umode->vtotal; mode->vscan = umode->vscan; mode->vrefresh = umode->vrefresh; mode->flags = umode->flags; mode->type = umode->type; strncpy(mode->name, umode->name, DRM_DISPLAY_MODE_LEN); mode->name[DRM_DISPLAY_MODE_LEN-1] = 0; } connector_funcs = (struct drm_connector_helper_funcs *) connector->helper_private; if (connector_funcs->mode_valid) { resp = connector_funcs->mode_valid(connector, mode); arg->data = (void *)resp; } /*do some clean up work*/ if (mode) drm_mode_destroy(dev, mode); mode_op_out: mutex_unlock(&dev->mode_config.mutex); return ret; default: DRM_DEBUG("Unsupported psb mode operation"); return -EOPNOTSUPP; } return 0; } static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); struct drm_psb_stolen_memory_arg *arg = data; arg->base = dev_priv->pg->stolen_base; arg->size = dev_priv->pg->vram_stolen_size; return 0; } static int psb_register_rw_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_psb_private *dev_priv = psb_priv(dev); struct drm_psb_register_rw_arg *arg = data; UHBUsage usage = arg->b_force_hw_on ? OSPM_UHB_FORCE_POWER_ON : OSPM_UHB_ONLY_IF_ON; if (arg->display_write_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { if (arg->display_write_mask & REGRWBITS_PFIT_CONTROLS) PSB_WVDC32(arg->display.pfit_controls, PFIT_CONTROL); if (arg->display_write_mask & REGRWBITS_PFIT_AUTOSCALE_RATIOS) PSB_WVDC32(arg->display.pfit_autoscale_ratios, PFIT_AUTO_RATIOS); if (arg->display_write_mask & REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) PSB_WVDC32( arg->display.pfit_programmed_scale_ratios, PFIT_PGM_RATIOS); if (arg->display_write_mask & REGRWBITS_PIPEASRC) PSB_WVDC32(arg->display.pipeasrc, PIPEASRC); if (arg->display_write_mask & REGRWBITS_PIPEBSRC) PSB_WVDC32(arg->display.pipebsrc, PIPEBSRC); if (arg->display_write_mask & REGRWBITS_VTOTAL_A) PSB_WVDC32(arg->display.vtotal_a, VTOTAL_A); if (arg->display_write_mask & REGRWBITS_VTOTAL_B) PSB_WVDC32(arg->display.vtotal_b, VTOTAL_B); ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } else { if (arg->display_write_mask & REGRWBITS_PFIT_CONTROLS) dev_priv->savePFIT_CONTROL = arg->display.pfit_controls; if (arg->display_write_mask & REGRWBITS_PFIT_AUTOSCALE_RATIOS) dev_priv->savePFIT_AUTO_RATIOS = arg->display.pfit_autoscale_ratios; if (arg->display_write_mask & REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) dev_priv->savePFIT_PGM_RATIOS = arg->display.pfit_programmed_scale_ratios; if (arg->display_write_mask & REGRWBITS_PIPEASRC) dev_priv->savePIPEASRC = arg->display.pipeasrc; if (arg->display_write_mask & REGRWBITS_PIPEBSRC) dev_priv->savePIPEBSRC = arg->display.pipebsrc; if (arg->display_write_mask & REGRWBITS_VTOTAL_A) dev_priv->saveVTOTAL_A = arg->display.vtotal_a; if (arg->display_write_mask & REGRWBITS_VTOTAL_B) dev_priv->saveVTOTAL_B = arg->display.vtotal_b; } } if (arg->display_read_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { if (arg->display_read_mask & REGRWBITS_PFIT_CONTROLS) arg->display.pfit_controls = PSB_RVDC32(PFIT_CONTROL); if (arg->display_read_mask & REGRWBITS_PFIT_AUTOSCALE_RATIOS) arg->display.pfit_autoscale_ratios = PSB_RVDC32(PFIT_AUTO_RATIOS); if (arg->display_read_mask & REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) arg->display.pfit_programmed_scale_ratios = PSB_RVDC32(PFIT_PGM_RATIOS); if (arg->display_read_mask & REGRWBITS_PIPEASRC) arg->display.pipeasrc = PSB_RVDC32(PIPEASRC); if (arg->display_read_mask & REGRWBITS_PIPEBSRC) arg->display.pipebsrc = PSB_RVDC32(PIPEBSRC); if (arg->display_read_mask & REGRWBITS_VTOTAL_A) arg->display.vtotal_a = PSB_RVDC32(VTOTAL_A); if (arg->display_read_mask & REGRWBITS_VTOTAL_B) arg->display.vtotal_b = PSB_RVDC32(VTOTAL_B); ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } else { if (arg->display_read_mask & REGRWBITS_PFIT_CONTROLS) arg->display.pfit_controls = dev_priv->savePFIT_CONTROL; if (arg->display_read_mask & REGRWBITS_PFIT_AUTOSCALE_RATIOS) arg->display.pfit_autoscale_ratios = dev_priv->savePFIT_AUTO_RATIOS; if (arg->display_read_mask & REGRWBITS_PFIT_PROGRAMMED_SCALE_RATIOS) arg->display.pfit_programmed_scale_ratios = dev_priv->savePFIT_PGM_RATIOS; if (arg->display_read_mask & REGRWBITS_PIPEASRC) arg->display.pipeasrc = dev_priv->savePIPEASRC; if (arg->display_read_mask & REGRWBITS_PIPEBSRC) arg->display.pipebsrc = dev_priv->savePIPEBSRC; if (arg->display_read_mask & REGRWBITS_VTOTAL_A) arg->display.vtotal_a = dev_priv->saveVTOTAL_A; if (arg->display_read_mask & REGRWBITS_VTOTAL_B) arg->display.vtotal_b = dev_priv->saveVTOTAL_B; } } if (arg->overlay_write_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { if (arg->overlay_write_mask & OV_REGRWBITS_OGAM_ALL) { PSB_WVDC32(arg->overlay.OGAMC5, OV_OGAMC5); PSB_WVDC32(arg->overlay.OGAMC4, OV_OGAMC4); PSB_WVDC32(arg->overlay.OGAMC3, OV_OGAMC3); PSB_WVDC32(arg->overlay.OGAMC2, OV_OGAMC2); PSB_WVDC32(arg->overlay.OGAMC1, OV_OGAMC1); PSB_WVDC32(arg->overlay.OGAMC0, OV_OGAMC0); } if (arg->overlay_write_mask & OVC_REGRWBITS_OGAM_ALL) { PSB_WVDC32(arg->overlay.OGAMC5, OVC_OGAMC5); PSB_WVDC32(arg->overlay.OGAMC4, OVC_OGAMC4); PSB_WVDC32(arg->overlay.OGAMC3, OVC_OGAMC3); PSB_WVDC32(arg->overlay.OGAMC2, OVC_OGAMC2); PSB_WVDC32(arg->overlay.OGAMC1, OVC_OGAMC1); PSB_WVDC32(arg->overlay.OGAMC0, OVC_OGAMC0); } if (arg->overlay_write_mask & OV_REGRWBITS_OVADD) { PSB_WVDC32(arg->overlay.OVADD, OV_OVADD); if (arg->overlay.b_wait_vblank) { /* Wait for 20ms.*/ unsigned long vblank_timeout = jiffies + HZ/50; uint32_t temp; while (time_before_eq(jiffies, vblank_timeout)) { temp = PSB_RVDC32(OV_DOVASTA); if ((temp & (0x1 << 31)) != 0) break; cpu_relax(); } } } if (arg->overlay_write_mask & OVC_REGRWBITS_OVADD) { PSB_WVDC32(arg->overlay.OVADD, OVC_OVADD); if (arg->overlay.b_wait_vblank) { /* Wait for 20ms.*/ unsigned long vblank_timeout = jiffies + HZ/50; uint32_t temp; while (time_before_eq(jiffies, vblank_timeout)) { temp = PSB_RVDC32(OVC_DOVCSTA); if ((temp & (0x1 << 31)) != 0) break; cpu_relax(); } } } ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } else { if (arg->overlay_write_mask & OV_REGRWBITS_OGAM_ALL) { dev_priv->saveOV_OGAMC5 = arg->overlay.OGAMC5; dev_priv->saveOV_OGAMC4 = arg->overlay.OGAMC4; dev_priv->saveOV_OGAMC3 = arg->overlay.OGAMC3; dev_priv->saveOV_OGAMC2 = arg->overlay.OGAMC2; dev_priv->saveOV_OGAMC1 = arg->overlay.OGAMC1; dev_priv->saveOV_OGAMC0 = arg->overlay.OGAMC0; } if (arg->overlay_write_mask & OVC_REGRWBITS_OGAM_ALL) { dev_priv->saveOVC_OGAMC5 = arg->overlay.OGAMC5; dev_priv->saveOVC_OGAMC4 = arg->overlay.OGAMC4; dev_priv->saveOVC_OGAMC3 = arg->overlay.OGAMC3; dev_priv->saveOVC_OGAMC2 = arg->overlay.OGAMC2; dev_priv->saveOVC_OGAMC1 = arg->overlay.OGAMC1; dev_priv->saveOVC_OGAMC0 = arg->overlay.OGAMC0; } if (arg->overlay_write_mask & OV_REGRWBITS_OVADD) dev_priv->saveOV_OVADD = arg->overlay.OVADD; if (arg->overlay_write_mask & OVC_REGRWBITS_OVADD) dev_priv->saveOVC_OVADD = arg->overlay.OVADD; } } if (arg->overlay_read_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { if (arg->overlay_read_mask & OV_REGRWBITS_OGAM_ALL) { arg->overlay.OGAMC5 = PSB_RVDC32(OV_OGAMC5); arg->overlay.OGAMC4 = PSB_RVDC32(OV_OGAMC4); arg->overlay.OGAMC3 = PSB_RVDC32(OV_OGAMC3); arg->overlay.OGAMC2 = PSB_RVDC32(OV_OGAMC2); arg->overlay.OGAMC1 = PSB_RVDC32(OV_OGAMC1); arg->overlay.OGAMC0 = PSB_RVDC32(OV_OGAMC0); } if (arg->overlay_read_mask & OVC_REGRWBITS_OGAM_ALL) { arg->overlay.OGAMC5 = PSB_RVDC32(OVC_OGAMC5); arg->overlay.OGAMC4 = PSB_RVDC32(OVC_OGAMC4); arg->overlay.OGAMC3 = PSB_RVDC32(OVC_OGAMC3); arg->overlay.OGAMC2 = PSB_RVDC32(OVC_OGAMC2); arg->overlay.OGAMC1 = PSB_RVDC32(OVC_OGAMC1); arg->overlay.OGAMC0 = PSB_RVDC32(OVC_OGAMC0); } if (arg->overlay_read_mask & OV_REGRWBITS_OVADD) arg->overlay.OVADD = PSB_RVDC32(OV_OVADD); if (arg->overlay_read_mask & OVC_REGRWBITS_OVADD) arg->overlay.OVADD = PSB_RVDC32(OVC_OVADD); ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } else { if (arg->overlay_read_mask & OV_REGRWBITS_OGAM_ALL) { arg->overlay.OGAMC5 = dev_priv->saveOV_OGAMC5; arg->overlay.OGAMC4 = dev_priv->saveOV_OGAMC4; arg->overlay.OGAMC3 = dev_priv->saveOV_OGAMC3; arg->overlay.OGAMC2 = dev_priv->saveOV_OGAMC2; arg->overlay.OGAMC1 = dev_priv->saveOV_OGAMC1; arg->overlay.OGAMC0 = dev_priv->saveOV_OGAMC0; } if (arg->overlay_read_mask & OVC_REGRWBITS_OGAM_ALL) { arg->overlay.OGAMC5 = dev_priv->saveOVC_OGAMC5; arg->overlay.OGAMC4 = dev_priv->saveOVC_OGAMC4; arg->overlay.OGAMC3 = dev_priv->saveOVC_OGAMC3; arg->overlay.OGAMC2 = dev_priv->saveOVC_OGAMC2; arg->overlay.OGAMC1 = dev_priv->saveOVC_OGAMC1; arg->overlay.OGAMC0 = dev_priv->saveOVC_OGAMC0; } if (arg->overlay_read_mask & OV_REGRWBITS_OVADD) arg->overlay.OVADD = dev_priv->saveOV_OVADD; if (arg->overlay_read_mask & OVC_REGRWBITS_OVADD) arg->overlay.OVADD = dev_priv->saveOVC_OVADD; } } if (arg->sprite_enable_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { PSB_WVDC32(0x1F3E, DSPARB); PSB_WVDC32(arg->sprite.dspa_control | PSB_RVDC32(DSPACNTR), DSPACNTR); PSB_WVDC32(arg->sprite.dspa_key_value, DSPAKEYVAL); PSB_WVDC32(arg->sprite.dspa_key_mask, DSPAKEYMASK); PSB_WVDC32(PSB_RVDC32(DSPASURF), DSPASURF); PSB_RVDC32(DSPASURF); PSB_WVDC32(arg->sprite.dspc_control, DSPCCNTR); PSB_WVDC32(arg->sprite.dspc_stride, DSPCSTRIDE); PSB_WVDC32(arg->sprite.dspc_position, DSPCPOS); PSB_WVDC32(arg->sprite.dspc_linear_offset, DSPCLINOFF); PSB_WVDC32(arg->sprite.dspc_size, DSPCSIZE); PSB_WVDC32(arg->sprite.dspc_surface, DSPCSURF); PSB_RVDC32(DSPCSURF); ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } } if (arg->sprite_disable_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { PSB_WVDC32(0x3F3E, DSPARB); PSB_WVDC32(0x0, DSPCCNTR); PSB_WVDC32(arg->sprite.dspc_surface, DSPCSURF); PSB_RVDC32(DSPCSURF); ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } } if (arg->subpicture_enable_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { uint32_t temp; if (arg->subpicture_enable_mask & REGRWBITS_DSPACNTR) { temp = PSB_RVDC32(DSPACNTR); temp &= ~DISPPLANE_PIXFORMAT_MASK; temp &= ~DISPPLANE_BOTTOM; temp |= DISPPLANE_32BPP; PSB_WVDC32(temp, DSPACNTR); temp = PSB_RVDC32(DSPABASE); PSB_WVDC32(temp, DSPABASE); PSB_RVDC32(DSPABASE); temp = PSB_RVDC32(DSPASURF); PSB_WVDC32(temp, DSPASURF); PSB_RVDC32(DSPASURF); } if (arg->subpicture_enable_mask & REGRWBITS_DSPBCNTR) { temp = PSB_RVDC32(DSPBCNTR); temp &= ~DISPPLANE_PIXFORMAT_MASK; temp &= ~DISPPLANE_BOTTOM; temp |= DISPPLANE_32BPP; PSB_WVDC32(temp, DSPBCNTR); temp = PSB_RVDC32(DSPBBASE); PSB_WVDC32(temp, DSPBBASE); PSB_RVDC32(DSPBBASE); temp = PSB_RVDC32(DSPBSURF); PSB_WVDC32(temp, DSPBSURF); PSB_RVDC32(DSPBSURF); } if (arg->subpicture_enable_mask & REGRWBITS_DSPCCNTR) { temp = PSB_RVDC32(DSPCCNTR); temp &= ~DISPPLANE_PIXFORMAT_MASK; temp &= ~DISPPLANE_BOTTOM; temp |= DISPPLANE_32BPP; PSB_WVDC32(temp, DSPCCNTR); temp = PSB_RVDC32(DSPCBASE); PSB_WVDC32(temp, DSPCBASE); PSB_RVDC32(DSPCBASE); temp = PSB_RVDC32(DSPCSURF); PSB_WVDC32(temp, DSPCSURF); PSB_RVDC32(DSPCSURF); } ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } } if (arg->subpicture_disable_mask != 0) { if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, usage)) { uint32_t temp; if (arg->subpicture_disable_mask & REGRWBITS_DSPACNTR) { temp = PSB_RVDC32(DSPACNTR); temp &= ~DISPPLANE_PIXFORMAT_MASK; temp |= DISPPLANE_32BPP_NO_ALPHA; PSB_WVDC32(temp, DSPACNTR); temp = PSB_RVDC32(DSPABASE); PSB_WVDC32(temp, DSPABASE); PSB_RVDC32(DSPABASE); temp = PSB_RVDC32(DSPASURF); PSB_WVDC32(temp, DSPASURF); PSB_RVDC32(DSPASURF); } if (arg->subpicture_disable_mask & REGRWBITS_DSPBCNTR) { temp = PSB_RVDC32(DSPBCNTR); temp &= ~DISPPLANE_PIXFORMAT_MASK; temp |= DISPPLANE_32BPP_NO_ALPHA; PSB_WVDC32(temp, DSPBCNTR); temp = PSB_RVDC32(DSPBBASE); PSB_WVDC32(temp, DSPBBASE); PSB_RVDC32(DSPBBASE); temp = PSB_RVDC32(DSPBSURF); PSB_WVDC32(temp, DSPBSURF); PSB_RVDC32(DSPBSURF); } if (arg->subpicture_disable_mask & REGRWBITS_DSPCCNTR) { temp = PSB_RVDC32(DSPCCNTR); temp &= ~DISPPLANE_PIXFORMAT_MASK; temp |= DISPPLANE_32BPP_NO_ALPHA; PSB_WVDC32(temp, DSPCCNTR); temp = PSB_RVDC32(DSPCBASE); PSB_WVDC32(temp, DSPCBASE); PSB_RVDC32(DSPCBASE); temp = PSB_RVDC32(DSPCSURF); PSB_WVDC32(temp, DSPCSURF); PSB_RVDC32(DSPCSURF); } ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); } } return 0; } /* always available as we are SIGIO'd */ static unsigned int psb_poll(struct file *filp, struct poll_table_struct *wait) { return POLLIN | POLLRDNORM; } /* Not sure what we will need yet - in the PVR driver this disappears into a tangle of abstracted handlers and per process crap */ struct psb_priv { int dummy; }; static int psb_driver_open(struct drm_device *dev, struct drm_file *priv) { struct psb_priv *psb = kzalloc(sizeof(struct psb_priv), GFP_KERNEL); if (psb == NULL) return -ENOMEM; priv->driver_priv = psb; DRM_DEBUG("\n"); /*return PVRSRVOpen(dev, priv);*/ return 0; } static void psb_driver_close(struct drm_device *dev, struct drm_file *priv) { kfree(priv->driver_priv); priv->driver_priv = NULL; } 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 = dev->dev_private; static unsigned int runtime_allowed; unsigned int nr = DRM_IOCTL_NR(cmd); DRM_DEBUG("cmd = %x, nr = %x\n", cmd, nr); if (runtime_allowed == 1 && dev_priv->is_lvds_on) { runtime_allowed++; pm_runtime_allow(&dev->pdev->dev); dev_priv->rpm_enabled = 1; } /* * The driver private ioctls and TTM ioctls should be * thread-safe. */ if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) { struct drm_ioctl_desc *ioctl = &psb_ioctls[nr - DRM_COMMAND_BASE]; if (unlikely(ioctl->cmd != cmd)) { DRM_ERROR( "Invalid drm cmnd %d ioctl->cmd %x, cmd %x\n", nr - DRM_COMMAND_BASE, ioctl->cmd, cmd); return -EINVAL; } return drm_ioctl(filp, cmd, arg); } /* * Not all old drm ioctls are thread-safe. */ return drm_ioctl(filp, cmd, arg); } /* When a client dies: * - Check for and clean up flipped page state */ void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) { } static void psb_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); drm_put_dev(dev); } static const struct dev_pm_ops psb_pm_ops = { .runtime_suspend = psb_runtime_suspend, .runtime_resume = psb_runtime_resume, .runtime_idle = psb_runtime_idle, }; static struct drm_driver driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \ DRIVER_IRQ_VBL | DRIVER_MODESET, .load = psb_driver_load, .unload = psb_driver_unload, .ioctls = psb_ioctls, .num_ioctls = DRM_ARRAY_SIZE(psb_ioctls), .device_is_agp = psb_driver_device_is_agp, .irq_preinstall = psb_irq_preinstall, .irq_postinstall = psb_irq_postinstall, .irq_uninstall = psb_irq_uninstall, .irq_handler = psb_irq_handler, .enable_vblank = psb_enable_vblank, .disable_vblank = psb_disable_vblank, .get_vblank_counter = psb_get_vblank_counter, .firstopen = NULL, .lastclose = psb_lastclose, .open = psb_driver_open, .postclose = psb_driver_close, #if 0 /* ACFIXME */ .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, .proc_init = psb_proc_init, .proc_cleanup = psb_proc_cleanup, #endif .preclose = psb_driver_preclose, .fops = { .owner = THIS_MODULE, .open = psb_open, .release = psb_release, .unlocked_ioctl = psb_unlocked_ioctl, .mmap = psb_mmap, .poll = psb_poll, .fasync = drm_fasync, .read = drm_read, }, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = PSB_DRM_DRIVER_DATE, .major = PSB_DRM_DRIVER_MAJOR, .minor = PSB_DRM_DRIVER_MINOR, .patchlevel = PSB_DRM_DRIVER_PATCHLEVEL }; static struct pci_driver psb_pci_driver = { .name = DRIVER_NAME, .id_table = pciidlist, .resume = ospm_power_resume, .suspend = ospm_power_suspend, .probe = psb_probe, .remove = psb_remove, #ifdef CONFIG_PM .driver.pm = &psb_pm_ops, #endif }; static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { /* MLD Added this from Inaky's patch */ if (pci_enable_msi(pdev)) DRM_ERROR("Enable MSI failed!\n"); return drm_get_pci_dev(pdev, ent, &driver); } static int __init psb_init(void) { return drm_pci_init(&driver, &psb_pci_driver); } static void __exit psb_exit(void) { drm_pci_exit(&driver, &psb_pci_driver); } late_initcall(psb_init); module_exit(psb_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL");