885 lines
23 KiB
C
885 lines
23 KiB
C
/**************************************************************************
|
|
* Copyright (c) 2007-2008, Intel Corporation.
|
|
* 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 <linux/device.h>
|
|
#include <linux/version.h>
|
|
#include "drmP.h"
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
|
|
#include "drm.h"
|
|
#else
|
|
#include <uapi/drm/drm.h>
|
|
#endif
|
|
|
|
#include "i915_drm.h"
|
|
#include "i915_drv.h"
|
|
#include "vxd_drv.h"
|
|
#include "vxd_drm.h"
|
|
|
|
#include <linux/console.h>
|
|
#include <linux/module.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/intel_mid_pm.h>
|
|
#include <asm/intel_mid_pcihelpers.h>
|
|
|
|
extern int drm_psb_trap_pagefaults;
|
|
|
|
int drm_psb_cpurelax;
|
|
int drm_psb_udelaydivider = 1;
|
|
int drm_psb_priv_pmu_func = 0;
|
|
|
|
static struct pci_dev *pci_root;
|
|
|
|
int drm_psb_trap_pagefaults;
|
|
|
|
extern struct drm_device *i915_drm_dev;
|
|
int drm_psb_msvdx_tiling;
|
|
|
|
atomic_t g_videodec_access_count;
|
|
|
|
static void vxd_power_init(struct drm_device *dev);
|
|
static void vxd_power_post_init(struct drm_device *dev);
|
|
|
|
extern int pmc_nc_set_power_state(int islands, int state_type, int reg);
|
|
extern int pmc_nc_get_power_state(int islands, int reg);
|
|
|
|
module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600);
|
|
|
|
int drm_msvdx_pmpolicy = PSB_PMPOLICY_POWERDOWN;
|
|
int drm_msvdx_bottom_half = PSB_BOTTOM_HALF_WQ;
|
|
module_param_named(msvdx_pmpolicy, drm_msvdx_pmpolicy, int, 0600);
|
|
module_param_named(priv_pmu_func, drm_psb_priv_pmu_func, int, 0600);
|
|
MODULE_PARM_DESC(msvdx_pmpolicy,
|
|
"control d0i3 of msvdx "
|
|
"(default: 2"
|
|
"0 - d0i3 is disabled"
|
|
"1 - clockgating is enabled"
|
|
"2 - powerdown is enabled)");
|
|
MODULE_PARM_DESC(priv_pmu_func,
|
|
"Use private PMU function or not "
|
|
"(default: 0"
|
|
"0 - disable"
|
|
"1 - enable)");
|
|
|
|
int drm_psb_debug = 0x0;
|
|
module_param_named(psb_debug, drm_psb_debug, int, 0600);
|
|
MODULE_PARM_DESC(psb_debug,
|
|
"control debug info output"
|
|
"default: 0"
|
|
"0:PSB_D_GENERAL, 1:PSB_D_INIT, 2:PSB_D_IRQ, 3:PSB_D_ENTRY"
|
|
"6:PSB_D_PM, 8:PSB_D_REG, 9:PSB_D_MSVDX, 13:PSB_D_WARN");
|
|
|
|
#define DRM_IOCTL_PSB_EXTENSION \
|
|
DRM_IOWR(DRM_PSB_EXTENSION, \
|
|
union drm_psb_extension_arg)
|
|
#define DRM_IOCTL_PSB_GETPAGEADDRS \
|
|
DRM_IOWR(DRM_PSB_GETPAGEADDRS, \
|
|
struct drm_psb_getpageaddrs_arg)
|
|
#define DRM_IOCTL_PSB_CMDBUF \
|
|
DRM_IOW(DRM_PSB_CMDBUF, \
|
|
struct drm_psb_cmdbuf_arg)
|
|
#define DRM_IOCTL_PSB_TTM_PL_CREATE \
|
|
DRM_IOWR(DRM_PSB_TTM_PL_CREATE, \
|
|
union ttm_pl_create_arg)
|
|
#define DRM_IOCTL_PSB_TTM_PL_REFERENCE \
|
|
DRM_IOWR(DRM_PSB_TTM_PL_REFERENCE, \
|
|
union ttm_pl_reference_arg)
|
|
#define DRM_IOCTL_PSB_TTM_PL_UNREF \
|
|
DRM_IOW(DRM_PSB_TTM_PL_UNREF, \
|
|
struct ttm_pl_reference_req)
|
|
#define DRM_IOCTL_PSB_TTM_PL_SYNCCPU \
|
|
DRM_IOW(DRM_PSB_TTM_PL_SYNCCPU, \
|
|
struct ttm_pl_synccpu_arg)
|
|
#define DRM_IOCTL_PSB_TTM_PL_WAITIDLE \
|
|
DRM_IOW(DRM_PSB_TTM_PL_WAITIDLE, \
|
|
struct ttm_pl_waitidle_arg)
|
|
#define DRM_IOCTL_PSB_TTM_PL_SETSTATUS \
|
|
DRM_IOWR(DRM_PSB_TTM_PL_SETSTATUS, \
|
|
union ttm_pl_setstatus_arg)
|
|
#define DRM_IOCTL_PSB_TTM_PL_CREATE_UB \
|
|
DRM_IOWR(DRM_PSB_TTM_PL_CREATE_UB, \
|
|
union ttm_pl_create_ub_arg)
|
|
#define DRM_IOCTL_PSB_TTM_FENCE_SIGNALED \
|
|
DRM_IOWR(DRM_PSB_TTM_FENCE_SIGNALED, \
|
|
union ttm_fence_signaled_arg)
|
|
#define DRM_IOCTL_PSB_TTM_FENCE_FINISH \
|
|
DRM_IOWR(DRM_PSB_TTM_FENCE_FINISH, \
|
|
union ttm_fence_finish_arg)
|
|
#define DRM_IOCTL_PSB_TTM_FENCE_UNREF \
|
|
DRM_IOW(DRM_PSB_TTM_FENCE_UNREF, \
|
|
struct ttm_fence_unref_arg)
|
|
#define DRM_IOCTL_PSB_VIDEO_GETPARAM \
|
|
DRM_IOWR(DRM_PSB_VIDEO_GETPARAM, \
|
|
struct drm_lnc_video_getparam_arg)
|
|
|
|
#define VXD_IOCTL_DEF_DRV(ioctl, _func, _flags) \
|
|
[DRM_IOCTL_NR(DRM_##ioctl) - DRM_COMMAND_VXD_OFFSET] = \
|
|
{.cmd = DRM_##ioctl - DRM_COMMAND_VXD_OFFSET, .func = _func, .flags = _flags, .cmd_drv = DRM_IOCTL_##ioctl}
|
|
|
|
struct drm_ioctl_desc vxd_ioctls[] = {
|
|
VXD_IOCTL_DEF_DRV(PSB_EXTENSION, psb_extension_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_GETPAGEADDRS, psb_getpageaddrs_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_CMDBUF, psb_cmdbuf_ioctl,
|
|
DRM_AUTH | DRM_UNLOCKED),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_PL_CREATE, psb_pl_create_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_PL_REFERENCE, psb_pl_reference_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_PL_UNREF, psb_pl_unref_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_PL_SYNCCPU, psb_pl_synccpu_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_PL_WAITIDLE, psb_pl_waitidle_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_PL_SETSTATUS, psb_pl_setstatus_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_PL_CREATE_UB, psb_pl_ub_create_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_FENCE_SIGNALED, psb_fence_signaled_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_FENCE_FINISH, psb_fence_finish_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_TTM_FENCE_UNREF, psb_fence_unref_ioctl,
|
|
DRM_AUTH),
|
|
VXD_IOCTL_DEF_DRV(PSB_VIDEO_GETPARAM, psb_video_getparam,
|
|
DRM_AUTH | DRM_UNLOCKED),
|
|
};
|
|
|
|
int vxd_max_ioctl = DRM_ARRAY_SIZE(vxd_ioctls);
|
|
|
|
struct psb_fpriv *psb_fpriv(struct drm_file *file_priv)
|
|
{
|
|
struct drm_i915_file_private *i915_file_priv =
|
|
(struct drm_i915_file_private *)file_priv->driver_priv;
|
|
#ifdef CONFIG_DRM_VXD_BYT
|
|
return i915_file_priv->pPriv;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
struct drm_psb_private *psb_priv(struct drm_device *dev)
|
|
{
|
|
struct drm_i915_private *i915_dev_priv = dev->dev_private;
|
|
return i915_dev_priv->vxd_priv;
|
|
}
|
|
|
|
int vxd_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct drm_file *file_priv;
|
|
struct drm_i915_file_private *i915_file_priv;
|
|
struct psb_fpriv *psb_fp;
|
|
struct drm_psb_private *dev_priv;
|
|
struct msvdx_private *msvdx_priv;
|
|
int i;
|
|
|
|
file_priv = (struct drm_file *)filp->private_data;
|
|
i915_file_priv = file_priv->driver_priv;
|
|
psb_fp = i915_file_priv->pPriv;
|
|
|
|
dev_priv = psb_priv(file_priv->minor->dev);
|
|
msvdx_priv = (struct msvdx_private *)dev_priv->msvdx_private;
|
|
|
|
#if 0
|
|
/*cleanup for msvdx */
|
|
if (msvdx_priv->tfile == BCVideoGetPriv(file_priv)->tfile) {
|
|
msvdx_priv->fw_status = 0;
|
|
msvdx_priv->host_be_opp_enabled = 0;
|
|
memset(&msvdx_priv->frame_info, 0,
|
|
sizeof(struct drm_psb_msvdx_frame_info) *
|
|
MAX_DECODE_BUFFERS);
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_VIDEO_MRFLD_EC
|
|
for (i = 0; i < PSB_MAX_EC_INSTANCE; i++) {
|
|
if (msvdx_priv->msvdx_ec_ctx[i]->tfile == psb_fp->tfile) {
|
|
msvdx_priv->msvdx_ec_ctx[i]->tfile = NULL;
|
|
msvdx_priv->msvdx_ec_ctx[i]->context_id = 0;
|
|
msvdx_priv->msvdx_ec_ctx[i]->fence = PSB_MSVDX_INVALID_FENCE;
|
|
}
|
|
}
|
|
#endif
|
|
ttm_object_file_release(&psb_fp->tfile);
|
|
kfree(psb_fp);
|
|
|
|
/* remove video context */
|
|
/* psb_remove_videoctx(dev_priv, filp); */
|
|
return 0;
|
|
}
|
|
|
|
static struct vm_operations_struct psb_ttm_vm_ops;
|
|
|
|
int psb_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
{
|
|
struct drm_file *file_priv;
|
|
struct drm_psb_private *dev_priv;
|
|
int ret;
|
|
|
|
file_priv = (struct drm_file *) filp->private_data;
|
|
dev_priv = psb_priv(file_priv->minor->dev);
|
|
|
|
ret = ttm_bo_mmap(filp, vma, &dev_priv->bdev);
|
|
if (unlikely(ret != 0))
|
|
return ret;
|
|
|
|
if (unlikely(dev_priv->ttm_vm_ops == NULL)) {
|
|
dev_priv->ttm_vm_ops = (struct vm_operations_struct *)vma->vm_ops;
|
|
psb_ttm_vm_ops = *vma->vm_ops;
|
|
psb_ttm_vm_ops.fault = &psb_ttm_fault;
|
|
}
|
|
|
|
vma->vm_ops = &psb_ttm_vm_ops;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void psb_do_takedown(struct drm_device *dev)
|
|
{
|
|
struct drm_psb_private *dev_priv =
|
|
((struct drm_i915_private *)dev->dev_private)->vxd_priv;
|
|
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;
|
|
}
|
|
|
|
psb_msvdx_uninit(dev);
|
|
}
|
|
|
|
static int __vxd_driver_unload()
|
|
{
|
|
struct drm_i915_private *i915_dev_priv = i915_drm_dev->dev_private;
|
|
struct drm_psb_private *dev_priv = i915_dev_priv->vxd_priv;
|
|
|
|
if (dev_priv) {
|
|
psb_do_takedown(i915_dev_priv->dev);
|
|
|
|
if (dev_priv->pf_pd) {
|
|
psb_mmu_free_pagedir(dev_priv->pf_pd);
|
|
dev_priv->pf_pd = NULL;
|
|
}
|
|
if (dev_priv->mmu) {
|
|
psb_mmu_driver_takedown(dev_priv->mmu);
|
|
dev_priv->mmu = 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 0
|
|
if (dev_priv->msvdx_reg) {
|
|
iounmap(dev_priv->msvdx_reg);
|
|
dev_priv->msvdx_reg = NULL;
|
|
}
|
|
#endif
|
|
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);
|
|
i915_dev_priv->vxd_priv = NULL;
|
|
}
|
|
#if 0
|
|
ospm_power_uninit();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int __exit vxd_driver_unload()
|
|
{
|
|
return __vxd_driver_unload();
|
|
}
|
|
|
|
#define PCI_ROOT_MSGBUS_CTRL_REG 0xD0
|
|
#define PCI_ROOT_MSGBUS_DATA_REG 0xD4
|
|
#define PCI_ROOT_MSGBUS_CTRL_EXT_REG 0xD8
|
|
#define PCI_ROOT_MSGBUS_READ 0x10
|
|
#define PCI_ROOT_MSGBUS_WRITE 0x11
|
|
#define PCI_ROOT_MSGBUS_DWORD_ENABLE 0xf0
|
|
#define PUNIT_PORT 0x04
|
|
#define VEDSSPM0 0x32
|
|
#define VEDSSPM1 0x33
|
|
#define VEDSSC 0x1
|
|
|
|
u32 intel_mid_msgbus_read32_vxd(u8 port, u32 addr)
|
|
{
|
|
unsigned long irq_flags;
|
|
u32 data;
|
|
u32 cmd;
|
|
u32 cmdext;
|
|
|
|
cmd = (PCI_ROOT_MSGBUS_READ << 24) | (port << 16) |
|
|
((addr & 0xff) << 8) | PCI_ROOT_MSGBUS_DWORD_ENABLE;
|
|
cmdext = addr & 0xffffff00;
|
|
|
|
data = intel_mid_msgbus_read32_raw_ext(cmd, cmdext);
|
|
|
|
return data;
|
|
}
|
|
|
|
void intel_mid_msgbus_write32_vxd(u8 port, u32 addr, u32 data)
|
|
{
|
|
unsigned long irq_flags;
|
|
u32 cmd;
|
|
u32 cmdext;
|
|
|
|
cmd = (PCI_ROOT_MSGBUS_WRITE << 24) | (port << 16) |
|
|
((addr & 0xFF) << 8) | PCI_ROOT_MSGBUS_DWORD_ENABLE;
|
|
cmdext = addr & 0xffffff00;
|
|
|
|
intel_mid_msgbus_write32_raw_ext(cmd, cmdext, data);
|
|
}
|
|
|
|
static int __init vxd_driver_load()
|
|
{
|
|
struct drm_i915_private *i915_dev_priv;
|
|
struct drm_psb_private *dev_priv;
|
|
struct ttm_bo_device *bdev;
|
|
int ret = -ENOMEM;
|
|
uint32_t pwr_sts;
|
|
|
|
/* Check if DRM device is loaded first */
|
|
if (!i915_drm_dev)
|
|
return -ENODEV;
|
|
|
|
i915_dev_priv = i915_drm_dev->dev_private;
|
|
|
|
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
|
|
if (dev_priv == NULL)
|
|
return -ENOMEM;
|
|
INIT_LIST_HEAD(&dev_priv->video_ctx);
|
|
spin_lock_init(&dev_priv->video_ctx_lock);
|
|
dev_priv->dev = i915_dev_priv->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->cmdbuf_mutex);
|
|
INIT_LIST_HEAD(&dev_priv->decode_context.validate_list);
|
|
spin_lock_init(&dev_priv->reloc_lock);
|
|
|
|
i915_dev_priv->vxd_priv = dev_priv;
|
|
|
|
dev_priv->msvdx_reg = i915_dev_priv->regs + 0x170000;
|
|
DRM_INFO("%s 3, i915_dev_priv->regs is 0x%x.\n",
|
|
__func__, i915_dev_priv->regs);
|
|
DRM_INFO("0 MSVDX_CORE_REV_OFFSET value is 0x%x.\n",
|
|
readl(i915_dev_priv->regs + 0x170640));
|
|
DRM_INFO("1 MSVDX_CORE_REV_OFFSET value is 0x%x.\n",
|
|
PSB_RMSVDX32(MSVDX_CORE_REV_OFFSET));
|
|
|
|
vxd_power_init(i915_dev_priv->dev);
|
|
i915_dev_priv->vxd_driver_open = vxd_driver_open;
|
|
i915_dev_priv->vxd_lastclose = vxd_lastclose;
|
|
i915_dev_priv->vxd_ioctl = vxd_ioctl;
|
|
i915_dev_priv->vxd_release = vxd_release;
|
|
i915_dev_priv->psb_mmap = psb_mmap;
|
|
i915_dev_priv->psb_msvdx_interrupt = psb_msvdx_interrupt;
|
|
|
|
#if 0
|
|
get_imr_info(dev_priv);
|
|
|
|
/* Init OSPM support */
|
|
ospm_power_init(dev);
|
|
#endif
|
|
|
|
ret = psb_ttm_fence_device_init(&dev_priv->fdev);
|
|
if (unlikely(ret != 0))
|
|
goto out_err;
|
|
|
|
/* For VXD385 DE2.x firmware support 16bit fence value */
|
|
dev_priv->fdev.fence_class[PSB_ENGINE_VIDEO].wrap_diff =
|
|
(1 << 14);
|
|
dev_priv->fdev.fence_class[PSB_ENGINE_VIDEO].flush_diff =
|
|
(1 << 13);
|
|
dev_priv->fdev.fence_class[PSB_ENGINE_VIDEO].sequence_mask =
|
|
0x0000ffff;
|
|
|
|
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, true);
|
|
if (unlikely(ret != 0))
|
|
goto out_err;
|
|
dev_priv->has_bo_device = 1;
|
|
ttm_lock_init(&dev_priv->ttm_lock);
|
|
|
|
dev_priv->mmu = psb_mmu_driver_init((void *)0,
|
|
drm_psb_trap_pagefaults, 0,
|
|
dev_priv, IMG_MMU);
|
|
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;
|
|
|
|
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.\n");
|
|
|
|
/*
|
|
* Initialize sequence numbers for the different command
|
|
* submission mechanisms.
|
|
*/
|
|
dev_priv->sequence[PSB_ENGINE_VIDEO] = 1;
|
|
|
|
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);
|
|
}
|
|
|
|
/* Create tiling MMU region managed by TTM */
|
|
if (!ttm_bo_init_mm(bdev, DRM_PSB_MEM_MMU_TILING,
|
|
(0x10000000) >> PAGE_SHIFT))
|
|
dev_priv->have_mem_mmu_tiling = 1;
|
|
|
|
PSB_DEBUG_INIT("Init MSVDX\n");
|
|
psb_msvdx_init(i915_dev_priv->dev);
|
|
|
|
vxd_power_post_init(i915_dev_priv->dev);
|
|
|
|
#if 0
|
|
ospm_post_init(dev);
|
|
#endif
|
|
/* enable msvdx tiling on BYT */
|
|
drm_psb_msvdx_tiling = 1;
|
|
return 0;
|
|
out_err:
|
|
__vxd_driver_unload();
|
|
return ret;
|
|
|
|
}
|
|
|
|
void vxd_lastclose(struct drm_device *dev)
|
|
{
|
|
struct msvdx_private *msvdx_priv;
|
|
struct drm_psb_private *dev_priv = psb_priv(dev);
|
|
if (!dev_priv)
|
|
return;
|
|
|
|
msvdx_priv = dev_priv->msvdx_private;
|
|
if (msvdx_priv) {
|
|
mutex_lock(&msvdx_priv->msvdx_mutex);
|
|
if (dev_priv->decode_context.buffers) {
|
|
vfree(dev_priv->decode_context.buffers);
|
|
dev_priv->decode_context.buffers = NULL;
|
|
}
|
|
mutex_unlock(&msvdx_priv->msvdx_mutex);
|
|
}
|
|
}
|
|
|
|
int vxd_driver_open(struct drm_device *dev, struct drm_file *file)
|
|
{
|
|
struct psb_fpriv *psb_fp;
|
|
struct drm_psb_private *dev_priv;
|
|
struct drm_i915_file_private *file_priv;
|
|
dev_priv = psb_priv(dev);
|
|
file_priv = file->driver_priv;
|
|
psb_fp = kzalloc(sizeof(*psb_fp), GFP_KERNEL);
|
|
if (unlikely(psb_fp == NULL))
|
|
return -ENOMEM;
|
|
psb_fp->tfile = ttm_object_file_init(dev_priv->tdev,
|
|
PSB_FILE_OBJECT_HASH_ORDER);
|
|
if (unlikely(psb_fp->tfile == NULL)) {
|
|
kfree(psb_fp);
|
|
return -EINVAL;
|
|
}
|
|
if (unlikely(dev_priv->bdev.dev_mapping == NULL))
|
|
dev_priv->bdev.dev_mapping = dev_priv->dev->dev_mapping;
|
|
#ifdef CONFIG_DRM_VXD_BYT
|
|
file_priv->pPriv = psb_fp;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int psb_extension_ioctl(struct drm_device *dev, void *data,
|
|
struct drm_file *file_priv)
|
|
{
|
|
union drm_psb_extension_arg *arg = data;
|
|
struct drm_psb_extension_rep *rep = &arg->rep;
|
|
|
|
if (strcmp(arg->extension, "psb_ttm_placement_alphadrop") == 0) {
|
|
rep->exists = 1;
|
|
rep->driver_ioctl_offset = DRM_PSB_PLACEMENT_OFFSET;
|
|
rep->sarea_offset = 0;
|
|
rep->major = 1;
|
|
rep->minor = 0;
|
|
rep->pl = 0;
|
|
return 0;
|
|
}
|
|
if (strcmp(arg->extension, "psb_ttm_fence_alphadrop") == 0) {
|
|
rep->exists = 1;
|
|
rep->driver_ioctl_offset = DRM_PSB_FENCE_OFFSET;
|
|
rep->sarea_offset = 0;
|
|
rep->major = 1;
|
|
rep->minor = 0;
|
|
rep->pl = 0;
|
|
return 0;
|
|
}
|
|
if (strcmp(arg->extension, "psb_ttm_execbuf_alphadrop") == 0) {
|
|
rep->exists = 1;
|
|
rep->driver_ioctl_offset = DRM_PSB_CMDBUF;
|
|
rep->sarea_offset = 0;
|
|
rep->major = 1;
|
|
rep->minor = 0;
|
|
rep->pl = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* return the video rar offset */
|
|
if (strcmp(arg->extension, "lnc_video_getparam") == 0) {
|
|
rep->exists = 1;
|
|
rep->driver_ioctl_offset = DRM_PSB_VIDEO_GETPARAM;
|
|
rep->sarea_offset = 0;
|
|
rep->major = 1;
|
|
rep->minor = 0;
|
|
rep->pl = 0;
|
|
return 0;
|
|
}
|
|
|
|
rep->exists = 0;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Called whenever a 32-bit process running under a 64-bit kernel
|
|
* performs an ioctl on /dev/dri/card<n>.
|
|
*
|
|
* \param filp file pointer.
|
|
* \param cmd command.
|
|
* \param arg user argument.
|
|
* \return zero on success or negative number on failure.
|
|
*/
|
|
long vxd_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct drm_file *file_priv = filp->private_data;
|
|
struct drm_device *dev;
|
|
struct drm_ioctl_desc *ioctl;
|
|
drm_ioctl_t *func;
|
|
unsigned int nr = DRM_IOCTL_NR(cmd);
|
|
int retcode = -EINVAL;
|
|
char stack_kdata[128];
|
|
char *kdata = NULL;
|
|
unsigned int usize, asize;
|
|
u32 drv_size;
|
|
|
|
dev = file_priv->minor->dev;
|
|
|
|
if ((nr < DRM_COMMAND_VXD_BASE) ||
|
|
(nr >= DRM_COMMAND_VXD_BASE + DRM_ARRAY_SIZE(vxd_ioctls)))
|
|
return drm_ioctl(filp, cmd, arg);
|
|
|
|
if (drm_device_is_unplugged(dev))
|
|
return -ENODEV;
|
|
|
|
/* workaround drm authentification issue on Android
|
|
* don't need following check for DRM_AUTH
|
|
* otherwise maybe it will be reset before the check
|
|
*/
|
|
file_priv->authenticated = 1;
|
|
|
|
DRM_DEBUG("pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n",
|
|
task_pid_nr(current), cmd, nr,
|
|
(long)old_encode_dev(file_priv->minor->device),
|
|
file_priv->authenticated);
|
|
|
|
ioctl = &vxd_ioctls[nr - DRM_COMMAND_VXD_BASE];
|
|
drv_size = _IOC_SIZE(ioctl->cmd_drv);
|
|
usize = asize = _IOC_SIZE(cmd);
|
|
if (drv_size > asize)
|
|
asize = drv_size;
|
|
|
|
atomic_inc(&dev->ioctl_count);
|
|
atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
|
|
++file_priv->ioctl_count;
|
|
|
|
/* Do not trust userspace, use our own definition */
|
|
func = ioctl->func;
|
|
|
|
if (!func) {
|
|
DRM_DEBUG("no function\n");
|
|
retcode = -EINVAL;
|
|
} else {
|
|
if (cmd & (IOC_IN | IOC_OUT)) {
|
|
if (asize <= sizeof(stack_kdata)) {
|
|
kdata = stack_kdata;
|
|
} else {
|
|
kdata = kmalloc(asize, GFP_KERNEL);
|
|
if (!kdata) {
|
|
retcode = -ENOMEM;
|
|
goto err_i1;
|
|
}
|
|
}
|
|
if (asize > usize)
|
|
memset(kdata + usize, 0, asize - usize);
|
|
|
|
if (cmd & IOC_IN) {
|
|
if (copy_from_user(kdata, (void __user *)arg,
|
|
usize) != 0) {
|
|
retcode = -EFAULT;
|
|
goto err_i1;
|
|
}
|
|
} else {
|
|
memset(kdata, 0, usize);
|
|
}
|
|
}
|
|
|
|
if (ioctl->flags & DRM_UNLOCKED)
|
|
retcode = func(dev, kdata, file_priv);
|
|
else {
|
|
mutex_lock(&drm_global_mutex);
|
|
retcode = func(dev, kdata, file_priv);
|
|
mutex_unlock(&drm_global_mutex);
|
|
}
|
|
|
|
if ((cmd & IOC_OUT) && kdata) {
|
|
if (copy_to_user((void __user *)arg, kdata,
|
|
usize) != 0)
|
|
retcode = -EFAULT;
|
|
}
|
|
}
|
|
|
|
err_i1:
|
|
if (kdata != stack_kdata)
|
|
kfree(kdata);
|
|
atomic_dec(&dev->ioctl_count);
|
|
if (retcode)
|
|
DRM_DEBUG("ret = %d\n", retcode);
|
|
return retcode;
|
|
}
|
|
|
|
static bool vxd_power_down(struct drm_device *dev)
|
|
{
|
|
uint32_t pwr_sts;
|
|
PSB_DEBUG_PM("MSVDX: power off msvdx.\n");
|
|
if (drm_psb_priv_pmu_func) {
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D3);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
while (pwr_sts != 0x03000003) {
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D3);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
if (pmc_nc_set_power_state(VEDSSC, OSPM_ISLAND_DOWN, VEDSSPM0)) {
|
|
PSB_DEBUG_PM("VED: pmu_nc_set_power_state DOWN failed!\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static bool vxd_power_on(struct drm_device *dev)
|
|
{
|
|
uint32_t pwr_sts;
|
|
PSB_DEBUG_PM("MSVDX: power on msvdx.\n");
|
|
if (drm_psb_priv_pmu_func) {
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D0);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
while (pwr_sts != 0x0) {
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D0);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
}
|
|
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D3);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
while (pwr_sts != 0x03000003) {
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D3);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
}
|
|
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D0);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
while (pwr_sts != 0x0) {
|
|
intel_mid_msgbus_write32_vxd(PUNIT_PORT, VEDSSPM0, VXD_APM_STS_D0);
|
|
udelay(10);
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
if (pmc_nc_set_power_state(VEDSSC, OSPM_ISLAND_UP, VEDSSPM0)) {
|
|
PSB_DEBUG_PM("VED: pmu_nc_set_power_state ON failed!\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static void vxd_power_init(struct drm_device *dev)
|
|
{
|
|
struct drm_psb_private *dev_priv = psb_priv(dev);
|
|
PSB_DEBUG_PM("MSVDX: vxd power init.\n");
|
|
pci_root = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
|
|
if (!pci_root) {
|
|
printk(KERN_ALERT "%s: Error: msgbus PCI handle NULL",
|
|
__func__);
|
|
}
|
|
mutex_init(&dev_priv->vxd_pm_mutex);
|
|
vxd_power_on(dev);
|
|
}
|
|
|
|
static void vxd_power_post_init(struct drm_device *dev)
|
|
{
|
|
struct drm_psb_private *dev_priv = psb_priv(dev);
|
|
struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
|
|
/* need power down msvdx after init */
|
|
vxd_power_down(dev);
|
|
msvdx_priv->msvdx_needs_reset = 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* is_island_on
|
|
*
|
|
* Description: checks to see if the island is up
|
|
* returns true if hw_island is ON
|
|
* returns false if hw_island is OFF
|
|
*/
|
|
bool is_vxd_on()
|
|
{
|
|
uint32_t pwr_sts;
|
|
if (drm_psb_priv_pmu_func)
|
|
pwr_sts = intel_mid_msgbus_read32_vxd(PUNIT_PORT, VEDSSPM0);
|
|
else
|
|
pwr_sts = pmc_nc_get_power_state(VEDSSC, VEDSSPM0);
|
|
|
|
if (pwr_sts == VXD_APM_STS_D0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
int ospm_apm_power_down_msvdx(struct drm_device *dev, int force_off)
|
|
{
|
|
struct drm_psb_private *dev_priv = psb_priv(dev);
|
|
struct msvdx_private *msvdx_priv = dev_priv->msvdx_private;
|
|
PSB_DEBUG_PM("MSVDX: work queue is scheduled to power off msvdx.\n");
|
|
int ret = 0;
|
|
mutex_lock(&dev_priv->vxd_pm_mutex);
|
|
if (force_off)
|
|
goto power_off;
|
|
|
|
if (atomic_read(&g_videodec_access_count)) {
|
|
ret = -EBUSY;
|
|
PSB_DEBUG_PM("g_videodec_access_count has been set.\n");
|
|
goto out;
|
|
}
|
|
|
|
if (is_vxd_on() == false) {
|
|
PSB_DEBUG_PM("vxd already is in power off.\n");
|
|
goto out;
|
|
}
|
|
|
|
if (psb_check_msvdx_idle(dev)) {
|
|
ret = -EBUSY;
|
|
goto out;
|
|
}
|
|
|
|
psb_msvdx_save_context(dev);
|
|
|
|
power_off:
|
|
vxd_power_down(dev);
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
i915_rpm_put_vxd(dev);
|
|
#endif
|
|
/* MSVDX_NEW_PMSTATE(dev, msvdx_priv, PSB_PMSTATE_POWERDOWN); */
|
|
|
|
out:
|
|
mutex_unlock(&dev_priv->vxd_pm_mutex);
|
|
return ret;
|
|
}
|
|
|
|
bool ospm_power_using_video_begin(int hw_island)
|
|
{
|
|
struct pci_dev *pdev = i915_drm_dev->pdev;
|
|
struct drm_psb_private *dev_priv = psb_priv(i915_drm_dev);
|
|
|
|
PSB_DEBUG_PM("MSVDX: %s is called.\n", __func__);
|
|
if (hw_island != OSPM_VIDEO_DEC_ISLAND) {
|
|
DRM_ERROR("failed.\n");
|
|
return true;
|
|
}
|
|
|
|
mutex_lock(&dev_priv->vxd_pm_mutex);
|
|
/* ospm_resume_pci(pdev); */
|
|
if (!is_vxd_on()) {
|
|
#ifdef CONFIG_PM_RUNTIME
|
|
i915_rpm_get_vxd(dev_priv->dev);
|
|
#endif
|
|
vxd_power_on(i915_drm_dev);
|
|
}
|
|
atomic_inc(&g_videodec_access_count);
|
|
mutex_unlock(&dev_priv->vxd_pm_mutex);
|
|
return true;
|
|
}
|
|
|
|
void ospm_power_using_video_end(int hw_island)
|
|
{
|
|
struct pci_dev *pdev = i915_drm_dev->pdev;
|
|
struct drm_psb_private *dev_priv = psb_priv(i915_drm_dev);
|
|
|
|
PSB_DEBUG_PM("MSVDX: %s is called.\n", __func__);
|
|
if (hw_island != OSPM_VIDEO_DEC_ISLAND) {
|
|
DRM_ERROR("failed.\n");
|
|
return true;
|
|
}
|
|
mutex_lock(&dev_priv->vxd_pm_mutex);
|
|
if (atomic_read(&g_videodec_access_count) <= 0)
|
|
DRM_ERROR("g_videodec_access_count <=0.\n");
|
|
else
|
|
atomic_dec(&g_videodec_access_count);
|
|
mutex_unlock(&dev_priv->vxd_pm_mutex);
|
|
}
|
|
|
|
module_init(vxd_driver_load);
|
|
module_exit(vxd_driver_unload);
|
|
MODULE_LICENSE("GPL");
|