blob: 2f692295217c93019ff3c4863c2edbf5cabcf045 [file] [log] [blame]
/*
* 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);
}