summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/drm/drm_sunmod.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/drm/drm_sunmod.c')
-rw-r--r--usr/src/uts/common/io/drm/drm_sunmod.c1010
1 files changed, 1010 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/drm/drm_sunmod.c b/usr/src/uts/common/io/drm/drm_sunmod.c
new file mode 100644
index 0000000..2f69229
--- /dev/null
+++ b/usr/src/uts/common/io/drm/drm_sunmod.c
@@ -0,0 +1,1010 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Common misc module interfaces of DRM under Solaris
+ */
+
+/*
+ * This module calls into gfx and agpmaster misc modules respectively
+ * for generic graphics operations and AGP master device support.
+ */
+
+#include "drm_sunmod.h"
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <vm/seg_kmem.h>
+
+static struct modlmisc modlmisc = {
+ &mod_miscops, "DRM common interfaces"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlmisc, NULL
+};
+
+static drm_inst_list_t *drm_inst_head;
+static kmutex_t drm_inst_list_lock;
+
+static int drm_sun_open(dev_t *, int, int, cred_t *);
+static int drm_sun_close(dev_t, int, int, cred_t *);
+static int drm_sun_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int drm_sun_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
+ size_t *, uint_t);
+
+/*
+ * devmap callbacks for AGP and PCI GART
+ */
+static int drm_devmap_map(devmap_cookie_t, dev_t,
+ uint_t, offset_t, size_t, void **);
+static int drm_devmap_dup(devmap_cookie_t, void *,
+ devmap_cookie_t, void **);
+static void drm_devmap_unmap(devmap_cookie_t, void *,
+ offset_t, size_t, devmap_cookie_t, void **, devmap_cookie_t, void **);
+
+static drm_inst_list_t *drm_supp_alloc_drv_entry(dev_info_t *);
+static drm_inst_state_t *drm_sup_devt_to_state(dev_t);
+static void drm_supp_free_drv_entry(dev_info_t *);
+
+static struct devmap_callback_ctl drm_devmap_callbacks = {
+ DEVMAP_OPS_REV, /* devmap_rev */
+ drm_devmap_map, /* devmap_map */
+ NULL, /* devmap_access */
+ drm_devmap_dup, /* devmap_dup */
+ drm_devmap_unmap /* devmap_unmap */
+};
+
+/*
+ * Common device operations structure for all DRM drivers
+ */
+struct cb_ops drm_cb_ops = {
+ drm_sun_open, /* cb_open */
+ drm_sun_close, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ drm_sun_ioctl, /* cb_ioctl */
+ drm_sun_devmap, /* cb_devmap */
+ nodev, /* cb_mmap */
+ NULL, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* cb_stream */
+ D_NEW | D_MTSAFE |D_DEVMAP /* cb_flag */
+};
+
+int
+_init(void)
+{
+ int error;
+
+ if ((error = mod_install(&modlinkage)) != 0) {
+ return (error);
+ }
+
+ /* initialize the instance list lock */
+ mutex_init(&drm_inst_list_lock, NULL, MUTEX_DRIVER, NULL);
+ return (0);
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ if ((err = mod_remove(&modlinkage)) != 0)
+ return (err);
+
+ mutex_destroy(&drm_inst_list_lock);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+void *
+drm_supp_register(dev_info_t *dip, drm_device_t *dp)
+{
+ int error;
+ char buf[80];
+ int instance = ddi_get_instance(dip);
+ ddi_acc_handle_t pci_cfg_handle;
+ agp_master_softc_t *agpm;
+ drm_inst_state_t *mstate;
+ drm_inst_list_t *entry;
+ gfxp_vgatext_softc_ptr_t gfxp;
+ struct dev_ops *devop;
+
+ ASSERT(dip != NULL);
+
+ entry = drm_supp_alloc_drv_entry(dip);
+ if (entry == NULL) {
+ cmn_err(CE_WARN, "drm_supp_register: failed to get softstate");
+ return (NULL);
+ }
+ mstate = &entry->disl_state;
+
+ /*
+ * DRM drivers are required to use common cb_ops
+ */
+ devop = ddi_get_driver(dip);
+ if (devop->devo_cb_ops != &drm_cb_ops) {
+ devop->devo_cb_ops = &drm_cb_ops;
+ }
+
+ /* Generic graphics initialization */
+ gfxp = gfxp_vgatext_softc_alloc();
+ error = gfxp_vgatext_attach(dip, DDI_ATTACH, gfxp);
+ if (error != DDI_SUCCESS) {
+ DRM_ERROR("drm_supp_regiter: failed to init gfx");
+ goto exit1;
+ }
+
+ /* create a minor node for common graphics ops */
+ (void) sprintf(buf, "%s%d", GFX_NAME, instance);
+ error = ddi_create_minor_node(dip, buf, S_IFCHR,
+ INST2NODE0(instance), DDI_NT_DISPLAY, NULL);
+ if (error != DDI_SUCCESS) {
+ DRM_ERROR("drm_supp_regiter: "
+ "failed to create minor node for gfx");
+ goto exit2;
+ }
+
+ /* setup mapping for later PCI config space access */
+ error = pci_config_setup(dip, &pci_cfg_handle);
+ if (error != DDI_SUCCESS) {
+ DRM_ERROR("drm_supp_regiter: "
+ "PCI configuration space setup failed");
+ goto exit2;
+ }
+
+ /* AGP master attach */
+ agpm = NULL;
+ if (dp->driver->use_agp) {
+ DRM_DEBUG("drm_supp_regiter: driver use AGP\n");
+ error = agpmaster_attach(dip, &agpm,
+ pci_cfg_handle, INST2NODE1(instance));
+ if ((error != DDI_SUCCESS) && (dp->driver->require_agp)) {
+ DRM_ERROR("drm_supp_regiter: "
+ "AGP master support not available");
+ goto exit3;
+ }
+ }
+
+ mutex_enter(&mstate->mis_lock);
+ mstate->mis_major = ddi_driver_major(dip);
+ mstate->mis_dip = dip;
+ mstate->mis_gfxp = gfxp;
+ mstate->mis_agpm = agpm;
+ mstate->mis_cfg_hdl = pci_cfg_handle;
+ mstate->mis_devp = dp;
+ mutex_exit(&mstate->mis_lock);
+
+ /* create minor node for DRM access */
+ (void) sprintf(buf, "%s%d", DRM_DEVNODE, instance);
+ if (ddi_create_minor_node(dip, buf, S_IFCHR,
+ INST2NODE2(instance), DDI_NT_DISPLAY_DRM, 0)) {
+ DRM_ERROR("supp_regiter: faled to create minor node for drm");
+ goto exit4;
+ }
+
+ return ((void *)mstate);
+
+exit4:
+ if ((dp->driver->use_agp) && agpm)
+ agpmaster_detach(&agpm);
+exit3:
+ pci_config_teardown(&pci_cfg_handle);
+exit2:
+ (void) gfxp_vgatext_detach(dip, DDI_DETACH, gfxp);
+exit1:
+ gfxp_vgatext_softc_free(gfxp);
+ drm_supp_free_drv_entry(dip);
+ ddi_remove_minor_node(dip, NULL);
+
+ return (NULL);
+}
+
+
+int
+drm_supp_unregister(void *handle)
+{
+ drm_inst_list_t *list;
+ drm_inst_state_t *mstate;
+
+ list = (drm_inst_list_t *)handle;
+ mstate = &list->disl_state;
+ mutex_enter(&mstate->mis_lock);
+
+ /* AGP master detach */
+ if (mstate->mis_agpm != NULL)
+ agpmaster_detach(&mstate->mis_agpm);
+
+ /* free PCI config access handle */
+ if (mstate->mis_cfg_hdl)
+ pci_config_teardown(&mstate->mis_cfg_hdl);
+
+ /* graphics misc module detach */
+ if (mstate->mis_gfxp) {
+ (void) gfxp_vgatext_detach(mstate->mis_dip, DDI_DETACH,
+ mstate->mis_gfxp);
+ gfxp_vgatext_softc_free(mstate->mis_gfxp);
+ }
+
+ mstate->mis_devp = NULL;
+
+ /* remove all minor nodes */
+ ddi_remove_minor_node(mstate->mis_dip, NULL);
+ mutex_exit(&mstate->mis_lock);
+ drm_supp_free_drv_entry(mstate->mis_dip);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*ARGSUSED*/
+static int
+drm_sun_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ drm_inst_state_t *mstate;
+ drm_cminor_t *mp, *newp;
+ drm_device_t *dp;
+ minor_t minor;
+ int newminor;
+ int instance;
+ int err;
+
+ mstate = drm_sup_devt_to_state(*devp);
+ /*
+ * return ENXIO for deferred attach so that system can
+ * attach us again.
+ */
+ if (mstate == NULL)
+ return (ENXIO);
+
+ /*
+ * The lest significant 15 bits are used for minor_number, and
+ * the mid 3 bits are used for instance number. All minor numbers
+ * are used as follows:
+ * 0 -- gfx
+ * 1 -- agpmaster
+ * 2 -- drm
+ * (3, MAX_CLONE_MINOR) -- drm minor node for clone open.
+ */
+ minor = DEV2MINOR(*devp);
+ instance = DEV2INST(*devp);
+ ASSERT(minor <= MAX_CLONE_MINOR);
+
+ /*
+ * No operations for VGA & AGP mater devices, always return OK.
+ */
+ if ((minor == GFX_MINOR) || (minor == AGPMASTER_MINOR))
+ return (0);
+
+ /*
+ * From here, we start to process drm
+ */
+
+ dp = mstate->mis_devp;
+ if (!dp)
+ return (ENXIO);
+
+ /*
+ * Drm driver implements a software lock to serialize access
+ * to graphics hardware based on per-process granulation. Before
+ * operating graphics hardware, all clients, including kernel
+ * and applications, must acquire this lock via DRM_IOCTL_LOCK
+ * ioctl, and release it via DRM_IOCTL_UNLOCK after finishing
+ * operations. Drm driver will grant r/w permission to the
+ * process which acquires this lock (Kernel is assumed to have
+ * process ID 0).
+ *
+ * A process might be terminated without releasing drm lock, in
+ * this case, drm driver is responsible for clearing the holding.
+ * To be informed of process exiting, drm driver uses clone open
+ * to guarantee that each call to open(9e) have one corresponding
+ * call to close(9e). In most cases, a process will close drm
+ * during process termination, so that drm driver could have a
+ * chance to release drm lock.
+ *
+ * In fact, a driver cannot know exactly when a process exits.
+ * Clone open doesn't address this issue completely: Because of
+ * inheritance, child processes inherit file descriptors from
+ * their parent. As a result, if the parent exits before its
+ * children, drm close(9e) entrypoint won't be called until all
+ * of its children terminate.
+ *
+ * Another issue brought up by inhertance is the process PID
+ * that calls the drm close() entry point may not be the same
+ * as the one who called open(). Per-process struct is allocated
+ * when a process first open() drm, and released when the process
+ * last close() drm. Since open()/close() may be not the same
+ * process, PID cannot be used for key to lookup per-process
+ * struct. So, we associate minor number with per-process struct
+ * during open()'ing, and find corresponding process struct
+ * via minor number when close() is called.
+ */
+ newp = kmem_zalloc(sizeof (drm_cminor_t), KM_SLEEP);
+ mutex_enter(&dp->dev_lock);
+ for (newminor = DRM_MIN_CLONEMINOR; newminor < MAX_CLONE_MINOR;
+ newminor ++) {
+ TAILQ_FOREACH(mp, &dp->minordevs, link) {
+ if (mp->minor == newminor)
+ break;
+ }
+ if (mp == NULL)
+ goto gotminor;
+ }
+
+ mutex_exit(&dp->dev_lock);
+ (void) kmem_free(newp, sizeof (drm_cminor_t));
+ return (EMFILE);
+
+gotminor:
+ TAILQ_INSERT_TAIL(&dp->minordevs, newp, link);
+ newp->minor = newminor;
+ mutex_exit(&dp->dev_lock);
+ err = drm_open(dp, newp, flag, otyp, credp);
+ if (err) {
+ mutex_enter(&dp->dev_lock);
+ TAILQ_REMOVE(&dp->minordevs, newp, link);
+ (void) kmem_free(newp, sizeof (drm_cminor_t));
+ mutex_exit(&dp->dev_lock);
+
+ return (err);
+ }
+
+ /* return a clone minor */
+ newminor = newminor | (instance << NBITSMNODE);
+ *devp = makedevice(getmajor(*devp), newminor);
+ return (err);
+}
+
+/*ARGSUSED*/
+static int
+drm_sun_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ drm_inst_state_t *mstate;
+ drm_device_t *dp;
+ minor_t minor;
+ int ret;
+
+ mstate = drm_sup_devt_to_state(dev);
+ if (mstate == NULL)
+ return (EBADF);
+
+ minor = DEV2MINOR(dev);
+ ASSERT(minor <= MAX_CLONE_MINOR);
+ if ((minor == GFX_MINOR) || (minor == AGPMASTER_MINOR))
+ return (0);
+
+ dp = mstate->mis_devp;
+ if (dp == NULL) {
+ DRM_ERROR("drm_sun_close: NULL soft state");
+ return (ENXIO);
+ }
+
+ ret = drm_close(dp, minor, flag, otyp, credp);
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+static int
+drm_sun_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
+ cred_t *credp, int *rvalp)
+{
+ extern drm_ioctl_desc_t drm_ioctls[];
+
+ drm_inst_state_t *mstate;
+ drm_device_t *dp;
+ drm_ioctl_desc_t *ioctl;
+ drm_ioctl_t *func;
+ drm_file_t *fpriv;
+ minor_t minor;
+ int retval;
+ int nr;
+
+ if (cmd == VIS_GETIDENTIFIER) {
+ if (ddi_copyout(&text_ident, (void *)arg,
+ sizeof (struct vis_identifier), mode))
+ return (EFAULT);
+ }
+
+ mstate = drm_sup_devt_to_state(dev);
+ if (mstate == NULL) {
+ return (EIO);
+ }
+
+ minor = DEV2MINOR(dev);
+ ASSERT(minor <= MAX_CLONE_MINOR);
+ switch (minor) {
+ case GFX_MINOR:
+ retval = gfxp_vgatext_ioctl(dev, cmd, arg,
+ mode, credp, rvalp, mstate->mis_gfxp);
+ return (retval);
+
+ case AGPMASTER_MINOR:
+ retval = agpmaster_ioctl(dev, cmd, arg, mode,
+ credp, rvalp, mstate->mis_agpm);
+ return (retval);
+
+ case DRM_MINOR:
+ default: /* DRM cloning minor nodes */
+ break;
+ }
+
+ dp = mstate->mis_devp;
+ ASSERT(dp != NULL);
+
+ nr = DRM_IOCTL_NR(cmd);
+ ioctl = &drm_ioctls[nr];
+ atomic_inc_32(&dp->counts[_DRM_STAT_IOCTLS]);
+
+ /* It's not a core DRM ioctl, try driver-specific. */
+ if (ioctl->func == NULL && nr >= DRM_COMMAND_BASE) {
+ /* The array entries begin at DRM_COMMAND_BASE ioctl nr */
+ nr -= DRM_COMMAND_BASE;
+ if (nr > dp->driver->max_driver_ioctl) {
+ DRM_ERROR("Bad driver ioctl number, 0x%x (of 0x%x)",
+ nr, dp->driver->max_driver_ioctl);
+ return (EINVAL);
+ }
+ ioctl = &dp->driver->driver_ioctls[nr];
+ }
+
+ func = ioctl->func;
+ if (func == NULL) {
+ return (ENOTSUP);
+ }
+
+ mutex_enter(&dp->dev_lock);
+ fpriv = drm_find_file_by_proc(dp, credp);
+ mutex_exit(&dp->dev_lock);
+ if (fpriv == NULL) {
+ DRM_ERROR("drm_sun_ioctl : can't find authenticator");
+ return (EACCES);
+ }
+
+ if (((ioctl->flags & DRM_ROOT_ONLY) && !DRM_SUSER(credp)) ||
+ ((ioctl->flags & DRM_AUTH) && !fpriv->authenticated) ||
+ ((ioctl->flags & DRM_MASTER) && !fpriv->master))
+ return (EACCES);
+
+ fpriv->dev = dev;
+ fpriv->credp = credp;
+
+ retval = func(dp, arg, fpriv, mode);
+
+ return (retval);
+}
+
+/*ARGSUSED*/
+static int
+drm_sun_devmap(dev_t dev, devmap_cookie_t dhp, offset_t offset,
+ size_t len, size_t *maplen, uint_t model)
+{
+ extern int drm_get_pci_index_reg(dev_info_t *, uint_t, uint_t, off_t *);
+
+ drm_inst_state_t *mstate;
+ drm_device_t *dp;
+ ddi_umem_cookie_t cookie;
+ drm_local_map_t *map = NULL;
+ unsigned long aperbase;
+ u_offset_t handle;
+ offset_t koff;
+ caddr_t kva;
+ minor_t minor;
+ size_t length;
+ int ret;
+
+ static ddi_device_acc_attr_t dev_attr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_NEVERSWAP_ACC,
+ DDI_STRICTORDER_ACC,
+ };
+ static ddi_device_acc_attr_t gem_dev_attr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_NEVERSWAP_ACC,
+ DDI_MERGING_OK_ACC
+ };
+
+ mstate = drm_sup_devt_to_state(dev);
+ if (mstate == NULL)
+ return (ENXIO);
+
+ minor = DEV2MINOR(dev);
+ switch (minor) {
+ case GFX_MINOR:
+ ret = gfxp_vgatext_devmap(dev, dhp, offset, len, maplen, model,
+ mstate->mis_gfxp);
+ return (ret);
+
+ case AGPMASTER_MINOR:
+ return (ENOTSUP);
+
+ case DRM_MINOR:
+ break;
+
+ default:
+ /* DRM cloning nodes */
+ if (minor > MAX_CLONE_MINOR)
+ return (EBADF);
+ break;
+ }
+
+
+ dp = mstate->mis_devp;
+ if (dp == NULL) {
+ DRM_ERROR("drm_sun_devmap: NULL soft state");
+ return (EINVAL);
+ }
+
+ mutex_enter(&dp->dev_lock);
+
+ if (dp->driver->use_gem == 1) {
+ struct idr_list *entry;
+ drm_cminor_t *mp;
+
+ mp = drm_find_file_by_minor(dp, minor);
+ if (!mp) {
+ mutex_exit(&dp->dev_lock);
+ DRM_ERROR("drm_sun_devmap: can't find authenticator");
+ return (EACCES);
+ }
+
+ spin_lock(&dp->struct_mutex);
+ idr_list_for_each(entry, &(mp->fpriv->object_idr)) {
+ if ((uintptr_t)entry->obj == (u_offset_t)offset) {
+ map = entry->obj->map;
+ goto goon;
+ }
+ }
+goon:
+ spin_unlock(&dp->struct_mutex);
+ }
+
+ if (map == NULL) {
+ /*
+ * We will solve 32-bit application on 64-bit kernel
+ * issue later, now, we just use low 32-bit
+ */
+ handle = (u_offset_t)offset;
+ handle &= 0xffffffff;
+
+ TAILQ_FOREACH(map, &dp->maplist, link) {
+ if (handle ==
+ ((u_offset_t)((uintptr_t)map->handle) & 0xffffffff))
+ break;
+ }
+
+ /*
+ * Temporarily, because offset is phys_addr for register
+ * and framebuffer, is kernel virtual_addr for others
+ * Maybe we will use hash table to solve this issue later.
+ */
+ if (map == NULL) {
+ TAILQ_FOREACH(map, &dp->maplist, link) {
+ if (handle == (map->offset & 0xffffffff))
+ break;
+ }
+ }
+ }
+
+ if (map == NULL) {
+ u_offset_t tmp;
+
+ mutex_exit(&dp->dev_lock);
+ cmn_err(CE_WARN, "Can't find map, offset=0x%llx, len=%x\n",
+ offset, (int)len);
+ cmn_err(CE_WARN, "Current mapping:\n");
+ TAILQ_FOREACH(map, &dp->maplist, link) {
+ tmp = (u_offset_t)((uintptr_t)map->handle) & 0xffffffff;
+ cmn_err(CE_WARN, "map(handle=0x%p, size=0x%lx,type=%d,"
+ "offset=0x%lx), handle=%llx, tmp=%lld", map->handle,
+ map->size, map->type, map->offset, handle, tmp);
+ }
+ return (-1);
+ }
+ if (map->flags & _DRM_RESTRICTED) {
+ mutex_exit(&dp->dev_lock);
+ cmn_err(CE_WARN, "restricted map\n");
+ return (-1);
+ }
+
+ mutex_exit(&dp->dev_lock);
+ switch (map->type) {
+ case _DRM_FRAME_BUFFER:
+ case _DRM_REGISTERS:
+ {
+ int regno;
+ off_t regoff;
+
+ regno = drm_get_pci_index_reg(dp->dip,
+ map->offset, (uint_t)len, &regoff);
+ if (regno < 0) {
+ DRM_ERROR("devmap: failed to get register"
+ " offset=0x%llx, len=0x%x", handle, len);
+ return (EINVAL);
+ }
+
+ ret = devmap_devmem_setup(dhp, dp->dip, NULL,
+ regno, (offset_t)regoff, len, PROT_ALL,
+ 0, &dev_attr);
+ if (ret != 0) {
+ *maplen = 0;
+ DRM_ERROR("devmap: failed, regno=%d,type=%d,"
+ " handle=0x%x, offset=0x%llx, len=0x%x",
+ regno, map->type, handle, offset, len);
+ return (ret);
+ }
+ *maplen = len;
+ return (ret);
+ }
+
+ case _DRM_SHM:
+ if (map->drm_umem_cookie == NULL)
+ return (EINVAL);
+ length = ptob(btopr(map->size));
+ ret = devmap_umem_setup(dhp, dp->dip, NULL,
+ map->drm_umem_cookie, 0, length,
+ PROT_ALL, IOMEM_DATA_CACHED, NULL);
+ if (ret != 0) {
+ *maplen = 0;
+ return (ret);
+ }
+ *maplen = length;
+
+ return (DDI_SUCCESS);
+
+ case _DRM_AGP:
+ if (dp->agp == NULL) {
+ cmn_err(CE_WARN, "drm_sun_devmap: attempted to mmap AGP"
+ "memory before AGP support is enabled");
+ return (DDI_FAILURE);
+ }
+
+ aperbase = dp->agp->base;
+ koff = map->offset - aperbase;
+ length = ptob(btopr(len));
+ kva = map->dev_addr;
+ cookie = gfxp_umem_cookie_init(kva, length);
+ if (cookie == NULL) {
+ cmn_err(CE_WARN, "devmap:failed to get umem_cookie");
+ return (DDI_FAILURE);
+ }
+
+ if ((ret = devmap_umem_setup(dhp, dp->dip,
+ &drm_devmap_callbacks, cookie, 0, length, PROT_ALL,
+ IOMEM_DATA_UNCACHED | DEVMAP_ALLOW_REMAP, &dev_attr)) < 0) {
+ gfxp_umem_cookie_destroy(cookie);
+ cmn_err(CE_WARN, "devmap:failed, retval=%d", ret);
+ return (DDI_FAILURE);
+ }
+ *maplen = length;
+ break;
+
+ case _DRM_SCATTER_GATHER:
+ koff = map->offset - (unsigned long)(caddr_t)dp->sg->virtual;
+ kva = map->dev_addr + koff;
+ length = ptob(btopr(len));
+ if (length > map->size) {
+ cmn_err(CE_WARN, "offset=0x%lx, virtual=0x%p,"
+ "mapsize=0x%lx,len=0x%lx", map->offset,
+ dp->sg->virtual, map->size, len);
+ return (DDI_FAILURE);
+ }
+ cookie = gfxp_umem_cookie_init(kva, length);
+ if (cookie == NULL) {
+ cmn_err(CE_WARN, "devmap:failed to get umem_cookie");
+ return (DDI_FAILURE);
+ }
+ ret = devmap_umem_setup(dhp, dp->dip,
+ &drm_devmap_callbacks, cookie, 0, length, PROT_ALL,
+ IOMEM_DATA_UNCACHED | DEVMAP_ALLOW_REMAP, &dev_attr);
+ if (ret != 0) {
+ cmn_err(CE_WARN, "sun_devmap: umem_setup fail");
+ gfxp_umem_cookie_destroy(cookie);
+ return (DDI_FAILURE);
+ }
+ *maplen = length;
+ break;
+
+ case _DRM_TTM:
+ if (map->drm_umem_cookie == NULL)
+ return (EINVAL);
+
+ if (gfxp_devmap_umem_setup(dhp, dp->dip,
+ NULL, map->drm_umem_cookie, 0, map->size, PROT_ALL,
+ IOMEM_DATA_UC_WR_COMBINE | DEVMAP_ALLOW_REMAP,
+ &gem_dev_attr)) {
+ cmn_err(CE_WARN, "devmap:failed, retval=%d", ret);
+ return (DDI_FAILURE);
+ }
+ *maplen = map->size;
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+
+}
+
+/*ARGSUSED*/
+static int
+drm_devmap_map(devmap_cookie_t dhc, dev_t dev, uint_t flags,
+ offset_t offset, size_t len, void **new_priv)
+{
+ devmap_handle_t *dhp;
+ drm_inst_state_t *statep;
+ struct ddi_umem_cookie *cp;
+
+ statep = drm_sup_devt_to_state(dev);
+ ASSERT(statep != NULL);
+
+ /*
+ * This driver only supports MAP_SHARED,
+ * and doesn't support MAP_PRIVATE
+ */
+ if (flags & MAP_PRIVATE) {
+ cmn_err(CE_WARN, "!DRM driver doesn't support MAP_PRIVATE");
+ return (EINVAL);
+ }
+
+ mutex_enter(&statep->dis_ctxlock);
+ dhp = (devmap_handle_t *)dhc;
+ cp = (struct ddi_umem_cookie *)dhp->dh_cookie;
+ cp->cook_refcnt = 1;
+ mutex_exit(&statep->dis_ctxlock);
+ *new_priv = statep;
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+drm_devmap_unmap(devmap_cookie_t dhc, void *pvtp, offset_t off, size_t len,
+ devmap_cookie_t new_dhp1, void **new_pvtp1, devmap_cookie_t new_dhp2,
+ void **new_pvtp2)
+{
+ devmap_handle_t *dhp;
+ devmap_handle_t *ndhp;
+ drm_inst_state_t *statep;
+ struct ddi_umem_cookie *cp;
+ struct ddi_umem_cookie *ncp;
+
+ dhp = (devmap_handle_t *)dhc;
+ statep = (drm_inst_state_t *)pvtp;
+
+ mutex_enter(&statep->dis_ctxlock);
+ cp = (struct ddi_umem_cookie *)dhp->dh_cookie;
+ if (new_dhp1 != NULL) {
+ ndhp = (devmap_handle_t *)new_dhp1;
+ ncp = (struct ddi_umem_cookie *)ndhp->dh_cookie;
+ ncp->cook_refcnt ++;
+ *new_pvtp1 = statep;
+ ASSERT(ncp == cp);
+ }
+
+ if (new_dhp2 != NULL) {
+ ndhp = (devmap_handle_t *)new_dhp2;
+ ncp = (struct ddi_umem_cookie *)ndhp->dh_cookie;
+ ncp->cook_refcnt ++;
+ *new_pvtp2 = statep;
+ ASSERT(ncp == cp);
+ }
+
+ cp->cook_refcnt --;
+ if (cp->cook_refcnt == 0) {
+ gfxp_umem_cookie_destroy(dhp->dh_cookie);
+ dhp->dh_cookie = NULL;
+ }
+ mutex_exit(&statep->dis_ctxlock);
+}
+
+
+/*ARGSUSED*/
+static int
+drm_devmap_dup(devmap_cookie_t dhc, void *pvtp, devmap_cookie_t new_dhc,
+ void **new_pvtp)
+{
+ devmap_handle_t *dhp;
+ drm_inst_state_t *statep;
+ struct ddi_umem_cookie *cp;
+
+ statep = (drm_inst_state_t *)pvtp;
+ mutex_enter(&statep->dis_ctxlock);
+ dhp = (devmap_handle_t *)dhc;
+ cp = (struct ddi_umem_cookie *)dhp->dh_cookie;
+ cp->cook_refcnt ++;
+ mutex_exit(&statep->dis_ctxlock);
+ *new_pvtp = statep;
+
+ return (0);
+}
+
+int
+drm_dev_to_instance(dev_t dev)
+{
+ return (DEV2INST(dev));
+}
+
+/*
+ * drm_supp_alloc_drv_entry()
+ *
+ * Description:
+ * Create a DRM entry and add it into the instance list (drm_inst_head).
+ * Note that we don't allow a duplicated entry
+ */
+static drm_inst_list_t *
+drm_supp_alloc_drv_entry(dev_info_t *dip)
+{
+ drm_inst_list_t **plist;
+ drm_inst_list_t *list;
+ drm_inst_list_t *entry;
+
+ /* protect the driver list */
+ mutex_enter(&drm_inst_list_lock);
+ plist = &drm_inst_head;
+ list = *plist;
+ while (list) {
+ if (list->disl_state.mis_dip == dip) {
+ mutex_exit(&drm_inst_list_lock);
+ cmn_err(CE_WARN, "%s%d already registered",
+ ddi_driver_name(dip), ddi_get_instance(dip));
+ return (NULL);
+ }
+ plist = &list->disl_next;
+ list = list->disl_next;
+ }
+
+ /* "dip" is not registered, create new one and add to list */
+ entry = kmem_zalloc(sizeof (*entry), KM_SLEEP);
+ *plist = entry;
+ entry->disl_state.mis_dip = dip;
+ mutex_init(&entry->disl_state.mis_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&entry->disl_state.dis_ctxlock, NULL, MUTEX_DRIVER, NULL);
+ mutex_exit(&drm_inst_list_lock);
+
+ return (entry);
+
+} /* drm_supp_alloc_drv_entry */
+
+/*
+ * drm_supp_free_drv_entry()
+ */
+static void
+drm_supp_free_drv_entry(dev_info_t *dip)
+{
+ drm_inst_list_t *list;
+ drm_inst_list_t **plist;
+ drm_inst_state_t *mstate;
+
+ /* protect the driver list */
+ mutex_enter(&drm_inst_list_lock);
+ plist = &drm_inst_head;
+ list = *plist;
+ while (list) {
+ if (list->disl_state.mis_dip == dip) {
+ *plist = list->disl_next;
+ mstate = &list->disl_state;
+ mutex_destroy(&mstate->mis_lock);
+ mutex_destroy(&mstate->dis_ctxlock);
+ kmem_free(list, sizeof (*list));
+ mutex_exit(&drm_inst_list_lock);
+ return;
+ }
+ plist = &list->disl_next;
+ list = list->disl_next;
+ }
+ mutex_exit(&drm_inst_list_lock);
+
+} /* drm_supp_free_drv_entry() */
+
+/*
+ * drm_sup_devt_to_state()
+ *
+ * description:
+ * Get the soft state of DRM instance by device number
+ */
+static drm_inst_state_t *
+drm_sup_devt_to_state(dev_t dev)
+{
+ drm_inst_list_t *list;
+ drm_inst_state_t *mstate;
+ major_t major = getmajor(dev);
+ int instance = DEV2INST(dev);
+
+ mutex_enter(&drm_inst_list_lock);
+ list = drm_inst_head;
+ while (list) {
+ mstate = &list->disl_state;
+ mutex_enter(&mstate->mis_lock);
+
+ if ((mstate->mis_major == major) &&
+ (ddi_get_instance(mstate->mis_dip) == instance)) {
+ mutex_exit(&mstate->mis_lock);
+ mutex_exit(&drm_inst_list_lock);
+ return (mstate);
+ }
+
+ list = list->disl_next;
+ mutex_exit(&mstate->mis_lock);
+ }
+
+ mutex_exit(&drm_inst_list_lock);
+ return (NULL);
+
+} /* drm_sup_devt_to_state() */
+
+int
+drm_supp_get_irq(void *handle)
+{
+ drm_inst_list_t *list;
+ drm_inst_state_t *mstate;
+ int irq;
+
+ list = (drm_inst_list_t *)handle;
+ mstate = &list->disl_state;
+ ASSERT(mstate != NULL);
+ irq = pci_config_get8(mstate->mis_cfg_hdl, PCI_CONF_ILINE);
+ return (irq);
+}
+
+int
+drm_supp_device_capability(void *handle, int capid)
+{
+ drm_inst_list_t *list;
+ drm_inst_state_t *mstate;
+ uint8_t cap = 0;
+ uint16_t caps_ptr;
+
+ list = (drm_inst_list_t *)handle;
+ mstate = &list->disl_state;
+ ASSERT(mstate != NULL);
+
+ /* has capabilities list ? */
+ if ((pci_config_get16(mstate->mis_cfg_hdl, PCI_CONF_STAT) &
+ PCI_CONF_CAP_MASK) == 0)
+ return (NULL);
+
+ caps_ptr = pci_config_get8(mstate->mis_cfg_hdl, PCI_CONF_CAP_PTR);
+ while (caps_ptr != PCI_CAP_NEXT_PTR_NULL) {
+ cap = pci_config_get32(mstate->mis_cfg_hdl, caps_ptr);
+ if ((cap & PCI_CONF_CAPID_MASK) == capid)
+ return (cap);
+ caps_ptr = pci_config_get8(mstate->mis_cfg_hdl,
+ caps_ptr + PCI_CAP_NEXT_PTR);
+ }
+
+ return (0);
+}