1450N/A/*
1450N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
1450N/A */
1450N/A
1450N/A/*
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 * Authors:
1450N/A * Gareth Hughes <gareth@valinux.com>
1450N/A *
1450N/A */
1450N/A
1450N/A#include "drmP.h"
1450N/A#include "drm.h"
1450N/A#include "radeon_drm.h"
1450N/A#include "radeon_drv.h"
1450N/A#include "drm_pciids.h"
1450N/A
1450N/A#include "efb.h"
1450N/A#include "efb_edid.h"
1450N/A
1450N/Astatic void *radeon_statep;
1450N/Aextern struct cb_ops drm_cb_ops;
1450N/A
1450N/Astatic int efb_fifo_reset(efb_private_t *efb_priv);
1450N/A
1450N/A/* Little-endian mapping with strict ordering for registers */
1450N/Astatic struct ddi_device_acc_attr littleEnd = {
1450N/A DDI_DEVICE_ATTR_V0,
1450N/A DDI_STRUCTURE_LE_ACC,
1450N/A DDI_STRICTORDER_ACC
1450N/A};
1450N/A
1450N/Avoid
1450N/Aefb_init(void *rstatep, struct cb_ops *efb_cb_ops, drm_driver_t *driver)
1450N/A{
1450N/A radeon_statep = rstatep;
1450N/A
1450N/A efb_cb_ops->cb_open = drm_cb_ops.cb_open;
1450N/A efb_cb_ops->cb_close = drm_cb_ops.cb_close;
1450N/A efb_cb_ops->cb_devmap = drm_cb_ops.cb_devmap;
1450N/A
1450N/A driver->set_devmap_callbacks = efb_devmap_set_callbacks;
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_attach(drm_device_t *statep)
1450N/A{
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A
1450N/A /*
1450N/A * setup mapping for later PCI config space access
1450N/A */
1450N/A dev_priv = statep->dev_private;
1450N/A
1450N/A if (dev_priv->private_data == NULL) {
1450N/A dev_priv->private_data =
1450N/A kmem_zalloc(sizeof (efb_private_t), KM_SLEEP);
1450N/A }
1450N/A
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A efb_priv->dip = statep->dip;
1450N/A
1450N/A if (pci_config_setup(statep->dip, &efb_priv->pci_handle)
1450N/A != DDI_SUCCESS) {
1450N/A DRM_ERROR("efb_attach: "
1450N/A "PCI configuration space setup failed");
1450N/A efb_detach(statep);
1450N/A return (DDI_FAILURE);
1450N/A }
1450N/A
1450N/A if (efb_map_registers(statep) != DDI_SUCCESS) {
1450N/A DRM_ERROR("efb_attach: "
1450N/A "Map registers failed");
1450N/A efb_detach(statep);
1450N/A return (DDI_FAILURE);
1450N/A }
1450N/A
1450N/A efb_priv->primary_stream = 0;
1450N/A efb_priv->power_level[EFB_PM_BOARD] = EFB_PWR_ON;
1450N/A
1450N/A#if VIS_CONS_REV > 2
1450N/A /*
1450N/A * setup for coherent console
1450N/A */
1450N/A if (ddi_prop_update_int(DDI_DEV_T_NONE, statep->dip,
1450N/A "tem-support", 1) != DDI_PROP_SUCCESS) {
1450N/A DRM_ERROR("efb_attach: "
1450N/A "Unable to set tem-support property");
1450N/A return (DDI_FAILURE);
1450N/A }
1450N/A#endif
1450N/A
1450N/A return (DDI_SUCCESS);
1450N/A}
1450N/A
1450N/Avoid
1450N/Aefb_detach(drm_device_t *statep)
1450N/A{
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A if (efb_priv != NULL) {
1450N/A efb_unmap_registers(statep);
1450N/A kmem_free(dev_priv->private_data, sizeof (efb_private_t));
1450N/A dev_priv->private_data = NULL;
1450N/A }
1450N/A}
1450N/A
1450N/Astatic void
1450N/Aefb_read_pci_config(drm_device_t *statep, void *pBuffer, int offset,
1450N/A int length)
1450N/A{
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A int i;
1450N/A unsigned short *pshort = (unsigned short *) pBuffer;
1450N/A unsigned int *pint = (unsigned int *) pBuffer;
1450N/A unsigned long *plong = (unsigned long *) pBuffer;
1450N/A char *pBuf = (char *)pBuffer;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A#ifdef _BIG_ENDIAN
1450N/A switch (length) {
1450N/A case 1:
1450N/A *pBuf = pci_config_get8(efb_priv->pci_handle, offset);
1450N/A break;
1450N/A case 2:
1450N/A *pshort = pci_config_get16(efb_priv->pci_handle, offset);
1450N/A break;
1450N/A case 4:
1450N/A *pint = pci_config_get32(efb_priv->pci_handle, offset);
1450N/A break;
1450N/A case 8:
1450N/A *plong = pci_config_get64(efb_priv->pci_handle, offset);
1450N/A break;
1450N/A default:
1450N/A
1450N/A for (i = offset; i < offset + length; ) {
1450N/A switch (i) {
1450N/A
1450N/A case 0x00: case 0x02:
1450N/A case 0x04: case 0x06:
1450N/A case 0x2c: case 0x2e:
1450N/A case 0x4e: case 0x50:
1450N/A {
1450N/A *(pBuf+1) =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A *(pBuf+0) =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A pBuf += 2;
1450N/A break;
1450N/A }
1450N/A case 0x08: case 0x09:
1450N/A case 0x0a: case 0x0b:
1450N/A case 0x0c: case 0x0d:
1450N/A case 0x0e: case 0x0f:
1450N/A case 0x34: case 0x35:
1450N/A case 0x36: case 0x37:
1450N/A case 0x3c: case 0x3d:
1450N/A case 0x3e: case 0x3f:
1450N/A case 0x40: case 0x41:
1450N/A case 0x42: case 0x43:
1450N/A case 0x4c: case 0x4d:
1450N/A case 0x53:
1450N/A {
1450N/A *pBuf =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A pBuf += 1;
1450N/A break;
1450N/A }
1450N/A case 0x52:
1450N/A {
1450N/A *pBuf =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A pBuf += 1;
1450N/A break;
1450N/A }
1450N/A default:
1450N/A {
1450N/A *(pBuf+3) =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A *(pBuf+2) =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A *(pBuf+1) =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A *(pBuf+0) =
1450N/A pci_config_get8(efb_priv->pci_handle, i++);
1450N/A pBuf += 4;
1450N/A break;
1450N/A }
1450N/A }
1450N/A }
1450N/A }
1450N/A#endif /* _BIG_ENDIAN */
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_get_pci_config(drm_device_t *statep, dev_t dev, intptr_t arg, int mode)
1450N/A{
1450N/A _NOTE(ARGUNUSED(dev))
1450N/A
1450N/A struct gfx_pci_cfg efb_pci_cfg;
1450N/A
1450N/A efb_read_pci_config(statep, &efb_pci_cfg, (uint_t)0,
1450N/A sizeof (struct gfx_pci_cfg));
1450N/A
1450N/A if (ddi_copyout(&efb_pci_cfg, (void *)arg,
1450N/A sizeof (efb_pci_cfg), mode)) {
1450N/A return (EFAULT);
1450N/A }
1450N/A return (0);
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_map_registers(drm_device_t *statep)
1450N/A{
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A if (ddi_regs_map_setup(statep->dip, 3, (caddr_t *)&efb_priv->registers,
1450N/A 0L, EFB_REG_SIZE, &littleEnd, &efb_priv->registersmap)
1450N/A != DDI_SUCCESS) {
1450N/A return (DDI_FAILURE);
1450N/A }
1450N/A return (DDI_SUCCESS);
1450N/A}
1450N/A
1450N/Avoid
1450N/Aefb_unmap_registers(drm_device_t *statep)
1450N/A{
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A if (efb_priv->registers != NULL) {
1450N/A ddi_regs_map_free(&efb_priv->registersmap);
1450N/A efb_priv->registers = NULL;
1450N/A }
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_get_gfx_identifier(drm_device_t *statep, dev_t dev, intptr_t arg, int mode)
1450N/A{
1450N/A _NOTE(ARGUNUSED(dev))
1450N/A
1450N/A struct gfx_identifier radeon_gfx_identifier = {
1450N/A .version = GFX_IDENT_VERSION,
1450N/A .flags = 0
1450N/A };
1450N/A char *buf;
1450N/A int proplen;
1450N/A
1450N/A if (ddi_getlongprop(DDI_DEV_T_ANY, statep->dip, DDI_PROP_DONTPASS,
1450N/A "name", (caddr_t)&buf, &proplen) == DDI_SUCCESS) {
1450N/A (void) strlcpy(radeon_gfx_identifier.model_name, buf,
1450N/A sizeof (radeon_gfx_identifier.model_name));
1450N/A radeon_gfx_identifier.flags |= GFX_IDENT_MODELNAME;
1450N/A kmem_free(buf, proplen);
1450N/A }
1450N/A
1450N/A if (ddi_getlongprop(DDI_DEV_T_ANY, statep->dip, DDI_PROP_DONTPASS,
1450N/A "model", (caddr_t)&buf, &proplen) == DDI_SUCCESS) {
1450N/A (void) strlcpy(radeon_gfx_identifier.part_number, buf,
1450N/A sizeof (radeon_gfx_identifier.part_number));
1450N/A radeon_gfx_identifier.flags |= GFX_IDENT_PARTNUM;
1450N/A kmem_free(buf, proplen);
1450N/A }
1450N/A
1450N/A if (ddi_copyout(&radeon_gfx_identifier, (caddr_t)arg,
1450N/A sizeof (radeon_gfx_identifier), mode)) {
1450N/A return (EFAULT);
1450N/A }
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_set_video_mode(drm_device_t *statep, dev_t dev, intptr_t arg, int mode)
1450N/A{
1450N/A _NOTE(ARGUNUSED(dev))
1450N/A
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A if (ddi_copyin((caddr_t)arg, &efb_priv->videomode,
1450N/A sizeof (struct gfx_video_mode), mode)) {
1450N/A return (EFAULT);
1450N/A }
1450N/A
1450N/A#if VIS_CONS_REV > 2
1450N/A efb_termemu_callback(statep);
1450N/A#endif /* VIS_CONS_REV */
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_get_video_mode(drm_device_t *statep, dev_t dev, intptr_t arg, int mode)
1450N/A{
1450N/A _NOTE(ARGUNUSED(dev))
1450N/A
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A if (ddi_copyout(&efb_priv->videomode, (caddr_t)arg,
1450N/A sizeof (struct gfx_video_mode), mode)) {
1450N/A return (EFAULT);
1450N/A }
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_get_edid_length(drm_device_t *statep, dev_t dev1, intptr_t arg, int mode)
1450N/A{
1450N/A _NOTE(ARGUNUSED(dev1))
1450N/A
1450N/A drm_device_t *dev = statep;
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A uint8_t *results;
1450N/A uint_t length = GFX_EDID_BLOCK_SIZE;
1450N/A gfx_edid_t edid_buf;
1450N/A int ret;
1450N/A int stream = 0;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A if (ddi_copyin((void *)arg, &edid_buf, sizeof (gfx_edid_t), mode)) {
1450N/A return (EFAULT);
1450N/A }
1450N/A
1450N/A /* validate head */
1450N/A stream = edid_buf.head;
1450N/A if (stream < GFX_EDID_HEAD_ONE || stream > GFX_EDID_HEAD_TWO) {
1450N/A return (EINVAL);
1450N/A }
1450N/A
1450N/A /* map to the internal stream number */
1450N/A stream--;
1450N/A
1450N/A results = kmem_alloc(GFX_EDID_BLOCK_SIZE, KM_SLEEP);
1450N/A
1450N/A DRM_LOCK();
1450N/A
1450N/A ret = efb_read_edid(efb_priv, stream, results, &length);
1450N/A
1450N/A DRM_UNLOCK();
1450N/A
1450N/A if (!ret) {
1450N/A
1450N/A /* the 127th byte specifies the extension block count */
1450N/A edid_buf.length = GFX_EDID_BLOCK_SIZE * (1 + results[126]);
1450N/A
1450N/A if (ddi_copyout(&edid_buf, (caddr_t)arg, sizeof (gfx_edid_t),
1450N/A mode)) {
1450N/A ret = EFAULT;
1450N/A }
1450N/A }
1450N/A
1450N/A kmem_free(results, GFX_EDID_BLOCK_SIZE);
1450N/A return (ret);
1450N/A}
1450N/A
1450N/Aint
1450N/Aefb_get_edid(drm_device_t *statep, dev_t dev1, intptr_t arg, int mode)
1450N/A{
1450N/A _NOTE(ARGUNUSED(dev1))
1450N/A
1450N/A drm_device_t *dev = statep;
1450N/A drm_radeon_private_t *dev_priv;
1450N/A efb_private_t *efb_priv;
1450N/A int stream;
1450N/A int ret;
1450N/A gfx_edid_t edid_buf;
1450N/A uint8_t *results;
1450N/A int length;
1450N/A
1450N/A dev_priv = (drm_radeon_private_t *)statep->dev_private;
1450N/A efb_priv = (efb_private_t *)dev_priv->private_data;
1450N/A
1450N/A if (ddi_copyin((void *)arg, &edid_buf, sizeof (gfx_edid_t), mode)) {
1450N/A return (EFAULT);
1450N/A }
1450N/A
1450N/A length = edid_buf.length;
1450N/A if (length <= 0) {
1450N/A return (EINVAL);
1450N/A }
1450N/A
1450N/A /* validate head */
1450N/A stream = edid_buf.head;
1450N/A if (stream < GFX_EDID_HEAD_ONE || stream > GFX_EDID_HEAD_TWO) {
1450N/A return (EINVAL);
1450N/A }
1450N/A
1450N/A /* map to the internal stream number */
1450N/A stream--;
1450N/A
1450N/A results = kmem_alloc(length, KM_SLEEP);
1450N/A
1450N/A#ifdef _MULTI_DATAMODEL
1450N/A if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1450N/A edid_buf.data = (caddr_t)
1450N/A ((unsigned long)edid_buf.data & 0xffffffff);
1450N/A }
1450N/A#endif /* _MULTI_DATAMODEL */
1450N/A
1450N/A DRM_LOCK();
1450N/A
1450N/A ret = efb_read_edid(efb_priv, stream, results, &edid_buf.length);
1450N/A
1450N/A DRM_UNLOCK();
1450N/A
1450N/A if (!ret) {
1450N/A ret = ddi_copyout(results, edid_buf.data,
1450N/A edid_buf.length, mode);
1450N/A }
1450N/A
1450N/A if (!ret) {
1450N/A ret = ddi_copyout(&edid_buf, (caddr_t)arg,
1450N/A sizeof (gfx_edid_t), mode);
1450N/A }
1450N/A
1450N/A if (ret) {
1450N/A ret = EFAULT;
1450N/A }
1450N/A
1450N/A kmem_free(results, length);
1450N/A return (ret);
1450N/A}
1450N/A
1450N/A
1450N/Astatic const struct vis_identifier visId = {"SUNWefb"};
1450N/A
1450N/A/* default structure for FBIOGATTR ioctl */
1450N/Astatic const struct fbgattr fb_attr = {
1450N/A FBTYPE_LASTPLUSONE + 1, /* real type */
1450N/A 0, /* owner */
1450N/A { FBTYPE_LASTPLUSONE + 1, /* emulated type */
1450N/A 25, 80, 1, /* w, h, depth */
1450N/A 256, /* colormap size */
1450N/A 0, /* total size */
1450N/A },
1450N/A 0, /* flags */
1450N/A FBTYPE_LASTPLUSONE + 1, /* current emulated type */
1450N/A { 0 }, /* dev_specific */
1450N/A { -1, -1, -1, -1 }, /* Emulation types */
1450N/A};
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1450N/A int *rvalp)
1450N/A{
1450N/A
1450N/A drm_device_t *statep;
1450N/A int unit;
1450N/A int ret = 0;
1450N/A
1450N/A unit = DEV2INST(dev);
1450N/A statep = ddi_get_soft_state(radeon_statep, unit);
1450N/A if (statep == NULL)
1450N/A return (DDI_FAILURE);
1450N/A
1450N/A switch (cmd) {
1450N/A
1450N/A#if VIS_CONS_REV > 2
1450N/A case VIS_DEVINIT:
1450N/A {
1450N/A ret = efb_vis_devinit(statep, (caddr_t)arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case VIS_DEVFINI:
1450N/A {
1450N/A ret = efb_vis_devfini(statep, (caddr_t)arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case VIS_CONSDISPLAY:
1450N/A {
1450N/A ret = efb_vis_consdisplay(statep, (caddr_t)arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case VIS_CONSCURSOR:
1450N/A {
1450N/A ret = efb_vis_conscursor(statep, (caddr_t)arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case VIS_CONSCOPY:
1450N/A {
1450N/A ret = efb_vis_conscopy(statep, (caddr_t)arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case VIS_PUTCMAP:
1450N/A {
1450N/A ret = efb_vis_putcmap(statep, (caddr_t)arg, mode);
1450N/A break;
1450N/A }
1450N/A#endif /* VIS_CONS_REV */
1450N/A
1450N/A case VIS_GETIDENTIFIER:
1450N/A {
1450N/A if (ddi_copyout(&visId, (void *)arg,
1450N/A sizeof (struct vis_identifier), mode))
1450N/A ret = EFAULT;
1450N/A break;
1450N/A }
1450N/A
1450N/A case FBIOGATTR:
1450N/A {
1450N/A if (ddi_copyout(&fb_attr, (void *)arg,
1450N/A sizeof (struct fbgattr), mode))
1450N/A ret = EFAULT;
1450N/A break;
1450N/A }
1450N/A
1450N/A case FBIOGTYPE:
1450N/A {
1450N/A if (ddi_copyout(&fb_attr.fbtype, (void *)arg,
1450N/A sizeof (struct fbtype), mode))
1450N/A ret = EFAULT;
1450N/A break;
1450N/A }
1450N/A
1450N/A case FBIOGXINFO:
1450N/A {
1450N/A ret = EFAULT;
1450N/A break;
1450N/A }
1450N/A
1450N/A case GFX_IOCTL_GET_IDENTIFIER:
1450N/A {
1450N/A ret = efb_get_gfx_identifier(statep, dev, arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case GFX_IOCTL_GET_PCI_CONFIG:
1450N/A {
1450N/A ret = efb_get_pci_config(statep, dev, arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case GFX_IOCTL_SET_VIDEO_MODE:
1450N/A {
1450N/A ret = efb_set_video_mode(statep, dev, arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case GFX_IOCTL_GET_CURRENT_VIDEO_MODE:
1450N/A {
1450N/A ret = efb_get_video_mode(statep, dev, arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case GFX_IOCTL_GET_EDID_LENGTH:
1450N/A {
1450N/A ret = efb_get_edid_length(statep, dev, arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A case GFX_IOCTL_GET_EDID:
1450N/A {
1450N/A ret = efb_get_edid(statep, dev, arg, mode);
1450N/A break;
1450N/A }
1450N/A
1450N/A default:
1450N/A {
1450N/A ret = drm_cb_ops.cb_ioctl(dev, cmd, arg, mode,
1450N/A credp, rvalp);
1450N/A break;
1450N/A }
1450N/A }
1450N/A return (ret);
1450N/A}
1450N/A
1450N/A
1450N/A/*
1450N/A * Read width, height, depth, etc. parameters from hardware into softc.
1450N/A */
1450N/Astatic void
1450N/Asize_compute(efb_private_t *efb_priv, int stream,
1450N/A uint32_t h_total_disp, uint32_t v_total_disp)
1450N/A{
1450N/A int v2;
1450N/A
1450N/A v2 = (h_total_disp & CRTC_H_TOTAL_DISP__CRTC_H_DISP_MASK) >>
1450N/A CRTC_H_TOTAL_DISP__CRTC_H_DISP__SHIFT;
1450N/A efb_priv->w[stream] = (v2 + 1) * 8;
1450N/A
1450N/A v2 = (v_total_disp & CRTC_V_TOTAL_DISP__CRTC_V_DISP_MASK) >>
1450N/A CRTC_V_TOTAL_DISP__CRTC_V_DISP__SHIFT;
1450N/A efb_priv->h[stream] = v2 + 1;
1450N/A}
1450N/A
1450N/A/*
1450N/A * Read width, height, depth, stride, pixfreq, h/v sync, fporch, bporch,
1450N/A * freq parameters from hardware into softc.
1450N/A */
1450N/Avoid
1450N/Aefb_getsize(efb_private_t *efb_priv)
1450N/A{
1450N/A volatile caddr_t registers = efb_priv->registers;
1450N/A uint32_t v;
1450N/A uint32_t h_total_disp;
1450N/A uint32_t v_total_disp;
1450N/A int v2;
1450N/A static int depths[] = {0, 4, 8, 15, 16, 24, 32, 16, 16};
1450N/A
1450N/A if (efb_priv->power_level[EFB_PM_BOARD] <= EFB_PWR_OFF)
1450N/A return;
1450N/A
1450N/A /*
1450N/A * Stream 1
1450N/A */
1450N/A v = regr(CRTC_GEN_CNTL);
1450N/A v2 = (v & CRTC_GEN_CNTL__CRTC_PIX_WIDTH_MASK) >>
1450N/A CRTC_GEN_CNTL__CRTC_PIX_WIDTH__SHIFT;
1450N/A efb_priv->depth[0] = depths[v2];
1450N/A
1450N/A v = regr(CRTC_PITCH);
1450N/A efb_priv->stride[0] = (v & CRTC_PITCH__CRTC_PITCH_MASK) * 8;
1450N/A
1450N/A h_total_disp = regr(CRTC_H_TOTAL_DISP);
1450N/A v_total_disp = regr(CRTC_V_TOTAL_DISP);
1450N/A
1450N/A size_compute(efb_priv, 0, h_total_disp, v_total_disp);
1450N/A
1450N/A
1450N/A /*
1450N/A * Stream 2
1450N/A */
1450N/A v = regr(CRTC2_GEN_CNTL);
1450N/A v2 = (v & CRTC_GEN_CNTL__CRTC_PIX_WIDTH_MASK) >>
1450N/A CRTC_GEN_CNTL__CRTC_PIX_WIDTH__SHIFT;
1450N/A efb_priv->depth[1] = depths[v2];
1450N/A
1450N/A v = regr(CRTC2_PITCH);
1450N/A efb_priv->stride[1] = (v & CRTC_PITCH__CRTC_PITCH_MASK) * 8;
1450N/A
1450N/A h_total_disp = regr(CRTC2_H_TOTAL_DISP);
1450N/A v_total_disp = regr(CRTC2_V_TOTAL_DISP);
1450N/A
1450N/A size_compute(efb_priv, 1, h_total_disp, v_total_disp);
1450N/A}
1450N/A
1450N/A/*
1450N/A * Write all pending entries in softc colormap to hardware
1450N/A */
1450N/Avoid
1450N/Aefb_cmap_write(efb_private_t *efb_priv, int cmap)
1450N/A{
1450N/A volatile caddr_t registers = efb_priv->registers;
1450N/A int index, oflag;
1450N/A uint8_t *flags;
1450N/A uint16_t *red, *green, *blue;
1450N/A uint32_t dac_cntl2, v;
1450N/A
1450N/A int keep = 1;
1450N/A int map = cmap & 0x1;
1450N/A
1450N/A /*
1450N/A * if restoring the previous saved colormap entries,
1450N/A * reset the flag
1450N/A */
1450N/A if (cmap > 1)
1450N/A keep = 0;
1450N/A
1450N/A if (efb_priv->power_level[EFB_PM_BOARD] <= EFB_PWR_OFF)
1450N/A return;
1450N/A
1450N/A red = efb_priv->colormap[cmap][0];
1450N/A green = efb_priv->colormap[cmap][1];
1450N/A blue = efb_priv->colormap[cmap][2];
1450N/A flags = efb_priv->cmap_flags[map];
1450N/A
1450N/A v = dac_cntl2 = regr(DAC_CNTL2);
1450N/A v &= ~DAC_CNTL2__PALETTE_ACCESS_CNTL_MASK;
1450N/A v |= map<<5;
1450N/A regw(DAC_CNTL2, v);
1450N/A
1450N/A oflag = 0;
1450N/A for (index = 0; index < EFB_CMAP_ENTRIES; ++index) {
1450N/A
1450N/A if (*flags) {
1450N/A if (!oflag) {
1450N/A regw8(PALETTE_INDEX, index);
1450N/A }
1450N/A
1450N/A v = ((*red << PALETTE_30_DATA__PALETTE_DATA_R__SHIFT)
1450N/A & PALETTE_30_DATA__PALETTE_DATA_R_MASK) |
1450N/A ((*green << PALETTE_30_DATA__PALETTE_DATA_G__SHIFT)
1450N/A & PALETTE_30_DATA__PALETTE_DATA_G_MASK) |
1450N/A ((*blue << PALETTE_30_DATA__PALETTE_DATA_B__SHIFT)
1450N/A & PALETTE_30_DATA__PALETTE_DATA_B_MASK);
1450N/A
1450N/A regw(PALETTE_30_DATA, v);
1450N/A }
1450N/A
1450N/A oflag = *flags;
1450N/A *flags &= keep;
1450N/A flags++;
1450N/A ++red;
1450N/A ++green;
1450N/A ++blue;
1450N/A }
1450N/A
1450N/A regw(DAC_CNTL2, dac_cntl2);
1450N/A}
1450N/A
1450N/A
1450N/A/*
1450N/A * Read hardware colormap into softc
1450N/A */
1450N/Avoid
1450N/Aefb_cmap_read(efb_private_t *efb_priv, int start, int count, int map)
1450N/A{
1450N/A volatile caddr_t registers = efb_priv->registers;
1450N/A int idx, i;
1450N/A uint32_t dac_cntl2, v;
1450N/A
1450N/A if (efb_priv->power_level[EFB_PM_BOARD] <= EFB_PWR_OFF)
1450N/A return;
1450N/A
1450N/A v = dac_cntl2 = regr(DAC_CNTL2);
1450N/A v &= ~DAC_CNTL2__PALETTE_ACCESS_CNTL_MASK;
1450N/A v |= map<<5;
1450N/A regw(DAC_CNTL2, v);
1450N/A
1450N/A regw8(PALETTE_INDEX + 2, start);
1450N/A
1450N/A for (idx = start, i = count; --i >= 0; ++idx) {
1450N/A v = regr(PALETTE_30_DATA);
1450N/A efb_priv->colormap[map][0][idx] =
1450N/A (v & PALETTE_30_DATA__PALETTE_DATA_R_MASK) >>
1450N/A PALETTE_30_DATA__PALETTE_DATA_R__SHIFT;
1450N/A
1450N/A efb_priv->colormap[map][1][idx] =
1450N/A (v & PALETTE_30_DATA__PALETTE_DATA_G_MASK) >>
1450N/A PALETTE_30_DATA__PALETTE_DATA_G__SHIFT;
1450N/A
1450N/A efb_priv->colormap[map][2][idx] =
1450N/A (v & PALETTE_30_DATA__PALETTE_DATA_B_MASK) >>
1450N/A PALETTE_30_DATA__PALETTE_DATA_B__SHIFT;
1450N/A }
1450N/A
1450N/A regw(DAC_CNTL2, dac_cntl2);
1450N/A}
1450N/A
1450N/A
1450N/A
1450N/A/*
1450N/A * Wait for draw engine idle. Return 1 on success, 0 on
1450N/A * failure (busy bit never goes away)
1450N/A */
1450N/A
1450N/A#define MASK RBBM_STATUS__CMDFIFO_AVAIL_MASK
1450N/A
1450N/A/* Let's try a much simpler loop */
1450N/Aint
1450N/Aefb_wait_fifo(efb_private_t *efb_priv, int n, const char *func, int line)
1450N/A{
1450N/A volatile caddr_t registers = efb_priv->registers;
1450N/A long limit;
1450N/A long dt;
1450N/A
1450N/A for (limit = 10000;
1450N/A ((regr(RBBM_STATUS) & MASK) < n) && (limit > 0); limit--) {
1450N/A ;
1450N/A }
1450N/A
1450N/A if ((regr(RBBM_STATUS) & MASK) >= n) {
1450N/A return (DDI_SUCCESS);
1450N/A }
1450N/A
1450N/A /* Short loop timed out, make a slower loop up to 1 second */
1450N/A dt = drv_hztousec(1);
1450N/A for (limit = 1000000;
1450N/A ((regr(RBBM_STATUS) & MASK) < n) && (limit > 0); limit -= dt) {
1450N/A efb_delay(1);
1450N/A }
1450N/A
1450N/A if ((regr(RBBM_STATUS) & MASK) >= n) {
1450N/A return (DDI_SUCCESS);
1450N/A }
1450N/A
1450N/A cmn_err(CE_WARN, "efb: %s:%d: fifo timeout (%d), status=%x",
1450N/A func, line, n, regr(RBBM_STATUS));
1450N/A
1450N/A return (DDI_FAILURE);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_wait_idle(efb_private_t *efb_priv, const char *func, int line)
1450N/A{
1450N/A volatile caddr_t registers = efb_priv->registers;
1450N/A uint32_t status;
1450N/A long limit;
1450N/A long dt;
1450N/A
1450N/A status = regr(RBBM_STATUS);
1450N/A
1450N/A /* If it's already idle, nothing to do */
1450N/A if ((status & MASK) >= 64 && !(status & GUI_ACTIVE)) {
1450N/A return (1);
1450N/A }
1450N/A
1450N/A if (efb_wait_fifo(efb_priv, 64, func, line) != DDI_SUCCESS) {
1450N/A
1450N/A /* OK, now reset the FIFO */
1450N/A if (efb_fifo_reset(efb_priv) != DDI_SUCCESS) {
1450N/A return (0); /* This is really amazingly bad */
1450N/A }
1450N/A
1450N/A if (efb_wait_fifo(efb_priv, 64, func, line) != DDI_SUCCESS) {
1450N/A return (0);
1450N/A }
1450N/A }
1450N/A
1450N/A for (limit = 10000; (regr(RBBM_STATUS) & GUI_ACTIVE) && --limit > 0; ) {
1450N/A ;
1450N/A }
1450N/A
1450N/A if (!(regr(RBBM_STATUS) & GUI_ACTIVE)) {
1450N/A return (1);
1450N/A }
1450N/A
1450N/A /* Short loop timed out, make a slower loop up to 3 seconds */
1450N/A dt = drv_hztousec(1);
1450N/A for (limit = 1000000;
1450N/A (regr(RBBM_STATUS) & GUI_ACTIVE) && (limit > 0); limit -= dt) {
1450N/A efb_delay(1);
1450N/A }
1450N/A
1450N/A if (!(regr(RBBM_STATUS) & GUI_ACTIVE)) {
1450N/A return (1);
1450N/A }
1450N/A
1450N/A cmn_err(CE_WARN,
1450N/A "efb: %s:%d: efb_wait_idle: idle timeout status=%x",
1450N/A func, line, regr(RBBM_STATUS));
1450N/A
1450N/A if (efb_fifo_reset(efb_priv) != DDI_SUCCESS) {
1450N/A return (0); /* This is really amazingly bad */
1450N/A }
1450N/A
1450N/A return (1);
1450N/A}
1450N/A
1450N/A
1450N/Aint
1450N/Aefb_wait_host_data(efb_private_t *efb_priv, const char *func, int line)
1450N/A{
1450N/A volatile caddr_t registers = efb_priv->registers;
1450N/A uint32_t status;
1450N/A long limit;
1450N/A long dt;
1450N/A
1450N/A status = regr(RBBM_STATUS);
1450N/A
1450N/A /* If it's already idle, nothing to do */
1450N/A if ((status & MASK) >= 64 && !(status & GUI_ACTIVE)) {
1450N/A return (0);
1450N/A }
1450N/A
1450N/A if (efb_wait_fifo(efb_priv, 64, func, line) != DDI_SUCCESS) {
1450N/A
1450N/A /* OK, now reset the FIFO */
1450N/A if (efb_fifo_reset(efb_priv) != DDI_SUCCESS) {
1450N/A return (2); /* This is really amazingly bad */
1450N/A }
1450N/A
1450N/A if (efb_wait_fifo(efb_priv, 64, func, line) != DDI_SUCCESS) {
1450N/A return (2);
1450N/A }
1450N/A }
1450N/A
1450N/A for (limit = 10000;
1450N/A ((status = regr(RBBM_STATUS)) & GUI_ACTIVE) &&
1450N/A ((status & 0x800200ff) != 0x80020040) &&
1450N/A ((status & 0x800100ff) != 0x80010040) &&
1450N/A limit > 0; limit--) {
1450N/A ;
1450N/A }
1450N/A
1450N/A if (!(status & GUI_ACTIVE)) {
1450N/A return (0);
1450N/A }
1450N/A
1450N/A if ((status & 0x800200ff) == 0x80020040 ||
1450N/A (status & 0x800100ff) == 0x80010040) {
1450N/A return (1);
1450N/A }
1450N/A
1450N/A /* Short loop timed out, make a slower loop up to 3 seconds */
1450N/A dt = drv_hztousec(1);
1450N/A for (limit = 1000000;
1450N/A ((status = regr(RBBM_STATUS)) & GUI_ACTIVE) &&
1450N/A ((status & 0x800200ff) != 0x80020040) &&
1450N/A ((status & 0x800100ff) != 0x80010040) &&
1450N/A limit > 0; limit -= dt) {
1450N/A efb_delay(1);
1450N/A }
1450N/A
1450N/A if (!(status & GUI_ACTIVE)) {
1450N/A return (0);
1450N/A }
1450N/A
1450N/A if ((status & 0x800200ff) == 0x80020040 ||
1450N/A (status & 0x800100ff) == 0x80010040) {
1450N/A return (1);
1450N/A }
1450N/A cmn_err(CE_WARN,
1450N/A "efb: %s:%d: efb_wait_idle: idle timeout status=%x",
1450N/A func, line, regr(RBBM_STATUS));
1450N/A
1450N/A if (efb_fifo_reset(efb_priv) != DDI_SUCCESS) {
1450N/A return (2); /* This is really amazingly bad */
1450N/A }
1450N/A
1450N/A return (0);
1450N/A}
1450N/A
1450N/A
1450N/A
1450N/Astatic int
1450N/Aefb_fifo_reset(efb_private_t *efb_priv)
1450N/A{
1450N/A volatile caddr_t registers = efb_priv->registers;
1450N/A dev_info_t *devi = efb_priv->dip;
1450N/A int rval;
1450N/A uint32_t s;
1450N/A
1450N/A#define peek2(a, v) ddi_peek32(devi, (int32_t *)registers + (a), (int32_t *)(v))
1450N/A#define poke2(a, v) ddi_poke32(devi, (int32_t *)registers + (a), (int32_t)(v))
1450N/A
1450N/A if ((rval = poke2(RBBM_SOFT_RESET,
1450N/A RBBM_SOFT_RESET__SOFT_RESET_CP |
1450N/A RBBM_SOFT_RESET__SOFT_RESET_E2)) != DDI_SUCCESS) {
1450N/A
1450N/A cmn_err(CE_WARN, "efb: bus error resetting fifo");
1450N/A
1450N/A return (rval);
1450N/A }
1450N/A
1450N/A /* dummy read to flush the write */
1450N/A if ((rval = peek2(RBBM_SOFT_RESET, &s)) != DDI_SUCCESS) {
1450N/A
1450N/A cmn_err(CE_WARN, "efb: bus error resetting fifo");
1450N/A return (rval);
1450N/A }
1450N/A
1450N/A if ((rval = poke2(RBBM_SOFT_RESET, 0)) != DDI_SUCCESS) {
1450N/A
1450N/A cmn_err(CE_WARN, "efb: bus error resetting fifo");
1450N/A return (rval);
1450N/A }
1450N/A
1450N/A if ((rval = peek2(RBBM_SOFT_RESET, &s)) != DDI_SUCCESS) {
1450N/A
1450N/A cmn_err(CE_WARN, "efb: bus error resetting fifo");
1450N/A return (rval);
1450N/A }
1450N/A
1450N/A return (rval);
1450N/A}
1450N/A
1450N/Avoid
1450N/Aefb_delay(clock_t ticks)
1450N/A{
1450N/A#if VIS_CONS_REV > 2
1450N/A extern int efb_in_polledio;
1450N/A
1450N/A if (efb_in_polledio) {
1450N/A if (ticks <= 0)
1450N/A ticks = 1;
1450N/A drv_usecwait(TICK_TO_USEC(ticks));
1450N/A } else {
1450N/A delay(ticks);
1450N/A }
1450N/A#else
1450N/A delay(ticks);
1450N/A#endif
1450N/A}