1450N/A/*
1450N/A * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
1450N/A */
1450N/A
1450N/A/*
1450N/A * drm_agpsupport.h -- DRM support for AGP/GART backend -*- linux-c -*-
1450N/A * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com
1450N/A */
1450N/A/*
1450N/A * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
1450N/A * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
1450N/A * All Rights Reserved.
1450N/A *
1450N/A * Permission is hereby granted, free of charge, to any person obtaining a
1450N/A * copy of this software and associated documentation files (the "Software"),
1450N/A * to deal in the Software without restriction, including without limitation
1450N/A * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1450N/A * and/or sell copies of the Software, and to permit persons to whom the
1450N/A * Software is furnished to do so, subject to the following conditions:
1450N/A *
1450N/A * The above copyright notice and this permission notice (including the next
1450N/A * paragraph) shall be included in all copies or substantial portions of the
1450N/A * Software.
1450N/A *
1450N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1450N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1450N/A * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1450N/A * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1450N/A * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1450N/A * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1450N/A * OTHER DEALINGS IN THE SOFTWARE.
1450N/A *
1450N/A * Author:
1450N/A * Rickard E. (Rik) Faith <faith@valinux.com>
1450N/A * Gareth Hughes <gareth@valinux.com>
1450N/A *
1450N/A */
1450N/A
1450N/A#include "drm.h"
1450N/A#include "drmP.h"
1450N/A
1450N/A#ifndef AGP_PAGE_SIZE
1450N/A#define AGP_PAGE_SIZE 4096
1450N/A#define AGP_PAGE_SHIFT 12
1450N/A#endif
1450N/A
1450N/A/*
1450N/A * The agpa_key field of struct agp_allocate_t actually is
1450N/A * an index to an array. It can be zero. But we will use
1450N/A * this agpa_key as a handle returned to userland. Generally,
1450N/A * 0 is not a valid value for a handle, so we add an offset
1450N/A * to the key to get a handle.
1450N/A */
1450N/A#define DRM_AGP_KEY_OFFSET 8
1450N/A
1450N/Aextern int drm_supp_device_capability(void *handle, int capid);
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_device_is_agp(drm_device_t *dev)
1450N/A{
1450N/A int ret;
1450N/A
1450N/A if (dev->driver->device_is_agp != NULL) {
1450N/A /*
1450N/A * device_is_agp returns a tristate:
1450N/A * 0 = not AGP;
1450N/A * 1 = definitely AGP;
1450N/A * 2 = fall back to PCI capability
1450N/A */
1450N/A ret = (*dev->driver->device_is_agp)(dev);
1450N/A if (ret != DRM_MIGHT_BE_AGP)
1450N/A return (ret);
1450N/A }
1450N/A
1450N/A return (drm_supp_device_capability(dev->drm_handle, PCIY_AGP));
1450N/A
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_device_is_pcie(drm_device_t *dev)
1450N/A{
1450N/A return (drm_supp_device_capability(dev->drm_handle, PCIY_EXPRESS));
1450N/A}
1450N/A
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_info(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A agp_info_t *agpinfo;
1450N/A drm_agp_info_t info;
1450N/A
1450N/A if (!dev->agp || !dev->agp->acquired)
1450N/A return (EINVAL);
1450N/A
1450N/A agpinfo = &dev->agp->agp_info;
1450N/A info.agp_version_major = agpinfo->agpi_version.agpv_major;
1450N/A info.agp_version_minor = agpinfo->agpi_version.agpv_minor;
1450N/A info.mode = agpinfo->agpi_mode;
1450N/A info.aperture_base = agpinfo->agpi_aperbase;
1450N/A info.aperture_size = agpinfo->agpi_apersize* 1024 * 1024;
1450N/A info.memory_allowed = agpinfo->agpi_pgtotal << PAGE_SHIFT;
1450N/A info.memory_used = agpinfo->agpi_pgused << PAGE_SHIFT;
1450N/A info.id_vendor = agpinfo->agpi_devid & 0xffff;
1450N/A info.id_device = agpinfo->agpi_devid >> 16;
1450N/A
1450N/A DRM_COPYTO_WITH_RETURN((void *)data, &info, sizeof (info));
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_acquire(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A int ret, rval;
1450N/A
1450N/A if (!dev->agp) {
1450N/A DRM_ERROR("drm_agp_acquire : agp isn't initialized yet");
1450N/A return (ENODEV);
1450N/A }
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ACQUIRE,
1450N/A (uintptr_t)0, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_ERROR("drm_agp_acquired: AGPIOC_ACQUIRE failed\n");
1450N/A return (EIO);
1450N/A }
1450N/A dev->agp->acquired = 1;
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_release(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A int ret, rval;
1450N/A
1450N/A if (!dev->agp)
1450N/A return (ENODEV);
1450N/A if (!dev->agp->acquired)
1450N/A return (EBUSY);
1450N/A
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RELEASE,
1450N/A (intptr_t)0, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_ERROR("drm_agp_release: AGPIOC_RELEASE failed\n");
1450N/A return (ENXIO);
1450N/A }
1450N/A dev->agp->acquired = 0;
1450N/A
1450N/A return (ret);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Adrm_agp_do_release(drm_device_t *dev)
1450N/A{
1450N/A int ret, rval;
1450N/A
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RELEASE,
1450N/A (intptr_t)0, FKIOCTL, kcred, &rval);
1450N/A
1450N/A if (ret == 0)
1450N/A dev->agp->acquired = 0;
1450N/A
1450N/A return (ret);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_enable(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A drm_agp_mode_t modes;
1450N/A agp_setup_t setup;
1450N/A int ret, rval;
1450N/A
1450N/A if (!dev->agp)
1450N/A return (ENODEV);
1450N/A if (!dev->agp->acquired)
1450N/A return (EBUSY);
1450N/A
1450N/A DRM_COPYFROM_WITH_RETURN(&modes, (void *)data, sizeof (modes));
1450N/A
1450N/A dev->agp->mode = modes.mode;
1450N/A setup.agps_mode = (uint32_t)modes.mode;
1450N/A
1450N/A DRM_DEBUG("drm_agp_enable: dev->agp->mode=%lx", modes.mode);
1450N/A
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_SETUP,
1450N/A (intptr_t)&setup, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_ERROR("drm_agp_enable: failed");
1450N/A return (EIO);
1450N/A }
1450N/A
1450N/A dev->agp->base = dev->agp->agp_info.agpi_aperbase;
1450N/A dev->agp->enabled = 1;
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_alloc(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A drm_agp_mem_t *entry;
1450N/A agp_allocate_t alloc;
1450N/A drm_agp_buffer_t request;
1450N/A int pages;
1450N/A int ret, rval;
1450N/A
1450N/A if (!dev->agp || !dev->agp->acquired)
1450N/A return (EINVAL);
1450N/A
1450N/A DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request));
1450N/A
1450N/A entry = kmem_zalloc(sizeof (*entry), KM_SLEEP);
1450N/A
1450N/A pages = btopr(request.size);
1450N/A alloc.agpa_pgcount = pages;
1450N/A alloc.agpa_type = AGP_NORMAL;
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ALLOCATE,
1450N/A (intptr_t)&alloc, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_ERROR("drm_agp_alloc: AGPIOC_ALLOCATE failed, ret=%d", ret);
1450N/A kmem_free(entry, sizeof (*entry));
1450N/A return (ret);
1450N/A }
1450N/A
1450N/A entry->bound = 0;
1450N/A entry->pages = pages;
1450N/A entry->handle = (void*)(uintptr_t)(alloc.agpa_key + DRM_AGP_KEY_OFFSET);
1450N/A entry->prev = NULL;
1450N/A entry->phys_addr = (void*)(uintptr_t)alloc.agpa_physical;
1450N/A entry->next = dev->agp->memory;
1450N/A if (dev->agp->memory)
1450N/A dev->agp->memory->prev = entry;
1450N/A dev->agp->memory = entry;
1450N/A
1450N/A /* physical is used only by i810 driver */
1450N/A request.physical = alloc.agpa_physical;
1450N/A request.handle = (unsigned long)entry->handle;
1450N/A
1450N/A /*
1450N/A * If failed to ddi_copyout(), we will free allocated AGP memory
1450N/A * when closing drm
1450N/A */
1450N/A DRM_COPYTO_WITH_RETURN((void *)data, &request, sizeof (request));
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Astatic drm_agp_mem_t *
1450N/Adrm_agp_lookup_entry(drm_device_t *dev, void *handle)
1450N/A{
1450N/A drm_agp_mem_t *entry;
1450N/A
1450N/A for (entry = dev->agp->memory; entry; entry = entry->next) {
1450N/A if (entry->handle == handle)
1450N/A return (entry);
1450N/A }
1450N/A
1450N/A return (NULL);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_unbind(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A agp_unbind_t unbind;
1450N/A drm_agp_binding_t request;
1450N/A drm_agp_mem_t *entry;
1450N/A int ret, rval;
1450N/A
1450N/A if (!dev->agp || !dev->agp->acquired)
1450N/A return (EINVAL);
1450N/A
1450N/A DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request));
1450N/A
1450N/A if (!(entry = drm_agp_lookup_entry(dev, (void *)request.handle)))
1450N/A return (EINVAL);
1450N/A if (!entry->bound)
1450N/A return (EINVAL);
1450N/A
1450N/A unbind.agpu_pri = 0;
1450N/A unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
1450N/A
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND,
1450N/A (intptr_t)&unbind, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_ERROR("drm_agp_unbind: AGPIOC_UNBIND failed");
1450N/A return (EIO);
1450N/A }
1450N/A entry->bound = 0;
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_bind(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A drm_agp_binding_t request;
1450N/A drm_agp_mem_t *entry;
1450N/A int start;
1450N/A uint_t key;
1450N/A
1450N/A if (!dev->agp || !dev->agp->acquired)
1450N/A return (EINVAL);
1450N/A
1450N/A DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request));
1450N/A
1450N/A entry = drm_agp_lookup_entry(dev, (void *)request.handle);
1450N/A if (!entry || entry->bound)
1450N/A return (EINVAL);
1450N/A
1450N/A key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
1450N/A start = btopr(request.offset);
1450N/A if (drm_agp_bind_memory(key, start, dev)) {
1450N/A DRM_ERROR("drm_agp_bind: failed key=%x, start=0x%x, "
1450N/A "agp_base=0x%lx", key, start, dev->agp->base);
1450N/A return (EIO);
1450N/A }
1450N/A
1450N/A entry->bound = dev->agp->base + (start << AGP_PAGE_SHIFT);
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_free(DRM_IOCTL_ARGS)
1450N/A{
1450N/A DRM_DEVICE;
1450N/A drm_agp_buffer_t request;
1450N/A drm_agp_mem_t *entry;
1450N/A int ret, rval;
1450N/A int agpu_key;
1450N/A
1450N/A DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request));
1450N/A if (!dev->agp || !dev->agp->acquired)
1450N/A return (EINVAL);
1450N/A if (!(entry = drm_agp_lookup_entry(dev, (void *)request.handle)))
1450N/A return (EINVAL);
1450N/A if (entry->bound)
1450N/A (void) drm_agp_unbind_memory(request.handle, dev);
1450N/A
1450N/A if (entry == dev->agp->memory)
1450N/A dev->agp->memory = entry->next;
1450N/A if (entry->prev)
1450N/A entry->prev->next = entry->next;
1450N/A if (entry->next)
1450N/A entry->next->prev = entry->prev;
1450N/A
1450N/A agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_DEALLOCATE,
1450N/A (intptr_t)agpu_key, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_ERROR("drm_agp_free: AGPIOC_DEALLOCATE failed,"
1450N/A "akey=%d, ret=%d", agpu_key, ret);
1450N/A return (EIO);
1450N/A }
1450N/A drm_free(entry, sizeof (*entry), DRM_MEM_AGPLISTS);
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Adrm_agp_head_t *
1450N/Adrm_agp_init(drm_device_t *dev)
1450N/A{
1450N/A drm_agp_head_t *agp = NULL;
1450N/A int retval, rval;
1450N/A
1450N/A DRM_DEBUG("drm_agp_init\n");
1450N/A agp = kmem_zalloc(sizeof (drm_agp_head_t), KM_SLEEP);
1450N/A
1450N/A retval = ldi_ident_from_dip(dev->dip, &agp->agpgart_li);
1450N/A if (retval != 0) {
1450N/A DRM_ERROR("drm_agp_init: failed to get layerd ident, retval=%d",
1450N/A retval);
1450N/A goto err_1;
1450N/A }
1450N/A
1450N/A retval = ldi_open_by_name(AGP_DEVICE, FEXCL, kcred,
1450N/A &agp->agpgart_lh, agp->agpgart_li);
1450N/A if (retval != 0) {
1450N/A DRM_ERROR("drm_agp_init: failed to open %s, retval=%d",
1450N/A AGP_DEVICE, retval);
1450N/A goto err_2;
1450N/A }
1450N/A
1450N/A retval = ldi_ioctl(agp->agpgart_lh, AGPIOC_INFO,
1450N/A (intptr_t)&agp->agp_info, FKIOCTL, kcred, &rval);
1450N/A
1450N/A if (retval != 0) {
1450N/A DRM_ERROR("drm_agp_init: failed to get agpinfo, retval=%d",
1450N/A retval);
1450N/A goto err_3;
1450N/A }
1450N/A
1450N/A return (agp);
1450N/A
1450N/Aerr_3:
1450N/A (void) ldi_close(agp->agpgart_lh, FEXCL, kcred);
1450N/A
1450N/Aerr_2:
1450N/A ldi_ident_release(agp->agpgart_li);
1450N/A
1450N/Aerr_1:
1450N/A kmem_free(agp, sizeof (drm_agp_head_t));
1450N/A return (NULL);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Avoid
1450N/Adrm_agp_fini(drm_device_t *dev)
1450N/A{
1450N/A drm_agp_head_t *agp = dev->agp;
1450N/A (void) ldi_close(agp->agpgart_lh, FEXCL, kcred);
1450N/A ldi_ident_release(agp->agpgart_li);
1450N/A kmem_free(agp, sizeof (drm_agp_head_t));
1450N/A dev->agp = NULL;
1450N/A}
1450N/A
1450N/A
1450N/A/*ARGSUSED*/
1450N/Avoid *
1450N/Adrm_agp_allocate_memory(size_t pages, uint32_t type)
1450N/A{
1450N/A return (NULL);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_free_memory(void *handle)
1450N/A{
1450N/A return (1);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_bind_memory(unsigned int key, uint32_t start, drm_device_t *dev)
1450N/A{
1450N/A agp_bind_t bind;
1450N/A int ret, rval;
1450N/A
1450N/A bind.agpb_pgstart = start;
1450N/A bind.agpb_key = key;
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_BIND,
1450N/A (intptr_t)&bind, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_DEBUG("drm_agp_bind_memory: AGPIOC_BIND failed");
1450N/A return (EIO);
1450N/A }
1450N/A return (0);
1450N/A}
1450N/A
1450N/A/*ARGSUSED*/
1450N/Aint
1450N/Adrm_agp_unbind_memory(unsigned long handle, drm_device_t *dev)
1450N/A{
1450N/A agp_unbind_t unbind;
1450N/A drm_agp_mem_t *entry;
1450N/A int ret, rval;
1450N/A
1450N/A if (!dev->agp || !dev->agp->acquired)
1450N/A return (EINVAL);
1450N/A
1450N/A entry = drm_agp_lookup_entry(dev, (void *)handle);
1450N/A if (!entry || !entry->bound)
1450N/A return (EINVAL);
1450N/A
1450N/A unbind.agpu_pri = 0;
1450N/A unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
1450N/A
1450N/A ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND,
1450N/A (intptr_t)&unbind, FKIOCTL, kcred, &rval);
1450N/A if (ret) {
1450N/A DRM_ERROR("drm_agp_unbind: AGPIO_UNBIND failed");
1450N/A return (EIO);
1450N/A }
1450N/A entry->bound = 0;
1450N/A return (0);
1450N/A}