/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*- * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Rickard E. (Rik) Faith * Jeff Hartmann * Keith Whitwell * * Rewritten by: * Gareth Hughes */ #include "mga.h" #include "drmP.h" #include "drm.h" #include "mga_drm.h" #include "mga_drv.h" #define MGA_DEFAULT_USEC_TIMEOUT 10000 #define MGA_FREELIST_DEBUG 0 /* ================================================================ * Engine control */ int mga_do_wait_for_idle( drm_mga_private_t *dev_priv ) { u32 status = 0; int i; DRM_DEBUG( "\n" ); for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; if ( status == MGA_ENDPRDMASTS ) { MGA_WRITE8( MGA_CRTC_INDEX, 0 ); return 0; } DRM_UDELAY( 1 ); } #if MGA_DMA_DEBUG DRM_ERROR( "failed!\n" ); DRM_INFO( " status=0x%08x\n", status ); #endif return DRM_ERR(EBUSY); } int mga_do_dma_idle( drm_mga_private_t *dev_priv ) { u32 status = 0; int i; DRM_DEBUG( "\n" ); for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { status = MGA_READ( MGA_STATUS ) & MGA_DMA_IDLE_MASK; if ( status == MGA_ENDPRDMASTS ) return 0; DRM_UDELAY( 1 ); } #if MGA_DMA_DEBUG DRM_ERROR( "failed! status=0x%08x\n", status ); #endif return DRM_ERR(EBUSY); } int mga_do_dma_reset( drm_mga_private_t *dev_priv ) { drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_mga_primary_buffer_t *primary = &dev_priv->prim; DRM_DEBUG( "\n" ); /* The primary DMA stream should look like new right about now. */ primary->tail = 0; primary->space = primary->size; primary->last_flush = 0; sarea_priv->last_wrap = 0; /* FIXME: Reset counters, buffer ages etc... */ /* FIXME: What else do we need to reinitialize? WARP stuff? */ return 0; } int mga_do_engine_reset( drm_mga_private_t *dev_priv ) { DRM_DEBUG( "\n" ); /* Okay, so we've completely screwed up and locked the engine. * How about we clean up after ourselves? */ MGA_WRITE( MGA_RST, MGA_SOFTRESET ); DRM_UDELAY( 15 ); /* Wait at least 10 usecs */ MGA_WRITE( MGA_RST, 0 ); /* Initialize the registers that get clobbered by the soft * reset. Many of the core register values survive a reset, * but the drawing registers are basically all gone. * * 3D clients should probably die after calling this. The X * server should reset the engine state to known values. */ #if 0 MGA_WRITE( MGA_PRIMPTR, virt_to_bus((void *)dev_priv->prim.status_page) | MGA_PRIMPTREN0 | MGA_PRIMPTREN1 ); #endif MGA_WRITE( MGA_ICLEAR, MGA_SOFTRAPICLR ); MGA_WRITE( MGA_IEN, MGA_SOFTRAPIEN ); /* The primary DMA stream should look like new right about now. */ mga_do_dma_reset( dev_priv ); /* This bad boy will never fail. */ return 0; } /* ================================================================ * Primary DMA stream */ void mga_do_dma_flush( drm_mga_private_t *dev_priv ) { drm_mga_primary_buffer_t *primary = &dev_priv->prim; u32 head, tail; u32 status = 0; int i; DMA_LOCALS; DRM_DEBUG( "\n" ); /* We need to wait so that we can do an safe flush */ for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; if ( status == MGA_ENDPRDMASTS ) break; DRM_UDELAY( 1 ); } if ( primary->tail == primary->last_flush ) { DRM_DEBUG( " bailing out...\n" ); return; } tail = primary->tail + dev_priv->primary->offset; /* We need to pad the stream between flushes, as the card * actually (partially?) reads the first of these commands. * See page 4-16 in the G400 manual, middle of the page or so. */ BEGIN_DMA( 1 ); DMA_BLOCK( MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000 ); ADVANCE_DMA(); primary->last_flush = primary->tail; head = MGA_READ( MGA_PRIMADDRESS ); if ( head <= tail ) { primary->space = primary->size - primary->tail; } else { primary->space = head - tail; } DRM_DEBUG( " head = 0x%06lx\n", head - dev_priv->primary->offset ); DRM_DEBUG( " tail = 0x%06lx\n", tail - dev_priv->primary->offset ); DRM_DEBUG( " space = 0x%06x\n", primary->space ); mga_flush_write_combine(); MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); DRM_DEBUG( "done.\n" ); } void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv ) { drm_mga_primary_buffer_t *primary = &dev_priv->prim; u32 head, tail; DMA_LOCALS; DRM_DEBUG( "\n" ); BEGIN_DMA_WRAP(); DMA_BLOCK( MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000 ); ADVANCE_DMA(); tail = primary->tail + dev_priv->primary->offset; primary->tail = 0; primary->last_flush = 0; primary->last_wrap++; head = MGA_READ( MGA_PRIMADDRESS ); if ( head == dev_priv->primary->offset ) { primary->space = primary->size; } else { primary->space = head - dev_priv->primary->offset; } DRM_DEBUG( " head = 0x%06lx\n", head - dev_priv->primary->offset ); DRM_DEBUG( " tail = 0x%06x\n", primary->tail ); DRM_DEBUG( " wrap = %d\n", primary->last_wrap ); DRM_DEBUG( " space = 0x%06x\n", primary->space ); mga_flush_write_combine(); MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); set_bit( 0, &primary->wrapped ); DRM_DEBUG( "done.\n" ); } void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) { drm_mga_primary_buffer_t *primary = &dev_priv->prim; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; u32 head = dev_priv->primary->offset; DRM_DEBUG( "\n" ); sarea_priv->last_wrap++; DRM_DEBUG( " wrap = %d\n", sarea_priv->last_wrap ); mga_flush_write_combine(); MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL ); clear_bit( 0, &primary->wrapped ); DRM_DEBUG( "done.\n" ); } /* ================================================================ * Freelist management */ #define MGA_BUFFER_USED ~0 #define MGA_BUFFER_FREE 0 #if MGA_FREELIST_DEBUG static void mga_freelist_print( drm_device_t *dev ) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_freelist_t *entry; DRM_INFO( "\n" ); DRM_INFO( "current dispatch: last=0x%x done=0x%x\n", dev_priv->sarea_priv->last_dispatch, (unsigned int)(MGA_READ( MGA_PRIMADDRESS ) - dev_priv->primary->offset) ); DRM_INFO( "current freelist:\n" ); for ( entry = dev_priv->head->next ; entry ; entry = entry->next ) { DRM_INFO( " %p idx=%2d age=0x%x 0x%06lx\n", entry, entry->buf->idx, entry->age.head, entry->age.head - dev_priv->primary->offset ); } DRM_INFO( "\n" ); } #endif static int mga_freelist_init( drm_device_t *dev, drm_mga_private_t *dev_priv ) { drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; drm_mga_buf_priv_t *buf_priv; drm_mga_freelist_t *entry; int i; DRM_DEBUG( "count=%d\n", dma->buf_count ); dev_priv->head = DRM(alloc)( sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER ); if ( dev_priv->head == NULL ) return DRM_ERR(ENOMEM); memset( dev_priv->head, 0, sizeof(drm_mga_freelist_t) ); SET_AGE( &dev_priv->head->age, MGA_BUFFER_USED, 0 ); for ( i = 0 ; i < dma->buf_count ; i++ ) { buf = dma->buflist[i]; buf_priv = buf->dev_private; entry = DRM(alloc)( sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER ); if ( entry == NULL ) return DRM_ERR(ENOMEM); memset( entry, 0, sizeof(drm_mga_freelist_t) ); entry->next = dev_priv->head->next; entry->prev = dev_priv->head; SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); entry->buf = buf; if ( dev_priv->head->next != NULL ) dev_priv->head->next->prev = entry; if ( entry->next == NULL ) dev_priv->tail = entry; buf_priv->list_entry = entry; buf_priv->discard = 0; buf_priv->dispatched = 0; dev_priv->head->next = entry; } return 0; } static void mga_freelist_cleanup( drm_device_t *dev ) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_freelist_t *entry; drm_mga_freelist_t *next; DRM_DEBUG( "\n" ); entry = dev_priv->head; while ( entry ) { next = entry->next; DRM(free)( entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER ); entry = next; } dev_priv->head = dev_priv->tail = NULL; } #if 0 /* FIXME: Still needed? */ static void mga_freelist_reset( drm_device_t *dev ) { drm_device_dma_t *dma = dev->dma; drm_buf_t *buf; drm_mga_buf_priv_t *buf_priv; int i; for ( i = 0 ; i < dma->buf_count ; i++ ) { buf = dma->buflist[i]; buf_priv = buf->dev_private; SET_AGE( &buf_priv->list_entry->age, MGA_BUFFER_FREE, 0 ); } } #endif static drm_buf_t *mga_freelist_get( drm_device_t *dev ) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_freelist_t *next; drm_mga_freelist_t *prev; drm_mga_freelist_t *tail = dev_priv->tail; u32 head, wrap; DRM_DEBUG( "\n" ); head = MGA_READ( MGA_PRIMADDRESS ); wrap = dev_priv->sarea_priv->last_wrap; DRM_DEBUG( " tail=0x%06lx %d\n", tail->age.head ? tail->age.head - dev_priv->primary->offset : 0, tail->age.wrap ); DRM_DEBUG( " head=0x%06lx %d\n", head - dev_priv->primary->offset, wrap ); if ( TEST_AGE( &tail->age, head, wrap ) ) { prev = dev_priv->tail->prev; next = dev_priv->tail; prev->next = NULL; next->prev = next->next = NULL; dev_priv->tail = prev; SET_AGE( &next->age, MGA_BUFFER_USED, 0 ); return next->buf; } DRM_DEBUG( "returning NULL!\n" ); return NULL; } int mga_freelist_put( drm_device_t *dev, drm_buf_t *buf ) { drm_mga_private_t *dev_priv = dev->dev_private; drm_mga_buf_priv_t *buf_priv = buf->dev_private; drm_mga_freelist_t *head, *entry, *prev; DRM_DEBUG( "age=0x%06lx wrap=%d\n", buf_priv->list_entry->age.head - dev_priv->primary->offset, buf_priv->list_entry->age.wrap ); entry = buf_priv->list_entry; head = dev_priv->head; if ( buf_priv->list_entry->age.head == MGA_BUFFER_USED ) { SET_AGE( &entry->age, MGA_BUFFER_FREE, 0 ); prev = dev_priv->tail; prev->next = entry; entry->prev = prev; entry->next = NULL; } else { prev = head->next; head->next = entry; prev->prev = entry; entry->prev = head; entry->next = prev; } return 0; } /* ================================================================ * DMA initialization, cleanup */ static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) { drm_mga_private_t *dev_priv; int ret; DRM_DEBUG( "\n" ); dev_priv = DRM(alloc)( sizeof(drm_mga_private_t), DRM_MEM_DRIVER ); if ( !dev_priv ) return DRM_ERR(ENOMEM); memset( dev_priv, 0, sizeof(drm_mga_private_t) ); dev_priv->chipset = init->chipset; dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT; if ( init->sgram ) { dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK; } else { dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR; } dev_priv->maccess = init->maccess; dev_priv->fb_cpp = init->fb_cpp; dev_priv->front_offset = init->front_offset; dev_priv->front_pitch = init->front_pitch; dev_priv->back_offset = init->back_offset; dev_priv->back_pitch = init->back_pitch; dev_priv->depth_cpp = init->depth_cpp; dev_priv->depth_offset = init->depth_offset; dev_priv->depth_pitch = init->depth_pitch; /* FIXME: Need to support AGP textures... */ dev_priv->texture_offset = init->texture_offset[0]; dev_priv->texture_size = init->texture_size[0]; DRM_GETSAREA(); if(!dev_priv->sarea) { DRM_ERROR( "failed to find sarea!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(EINVAL); } DRM_FIND_MAP( dev_priv->mmio, init->mmio_offset ); if(!dev_priv->mmio) { DRM_ERROR( "failed to find mmio region!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(EINVAL); } DRM_FIND_MAP( dev_priv->status, init->status_offset ); if(!dev_priv->status) { DRM_ERROR( "failed to find status page!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(EINVAL); } DRM_FIND_MAP( dev_priv->warp, init->warp_offset ); if(!dev_priv->warp) { DRM_ERROR( "failed to find warp microcode region!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(EINVAL); } DRM_FIND_MAP( dev_priv->primary, init->primary_offset ); if(!dev_priv->primary) { DRM_ERROR( "failed to find primary dma region!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(EINVAL); } DRM_FIND_MAP( dev_priv->buffers, init->buffers_offset ); if(!dev_priv->buffers) { DRM_ERROR( "failed to find dma buffer region!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(EINVAL); } dev_priv->sarea_priv = (drm_mga_sarea_t *)((u8 *)dev_priv->sarea->handle + init->sarea_priv_offset); DRM_IOREMAP( dev_priv->warp, dev ); DRM_IOREMAP( dev_priv->primary, dev ); DRM_IOREMAP( dev_priv->buffers, dev ); if(!dev_priv->warp->handle || !dev_priv->primary->handle || !dev_priv->buffers->handle ) { DRM_ERROR( "failed to ioremap agp regions!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(ENOMEM); } ret = mga_warp_install_microcode( dev_priv ); if ( ret < 0 ) { DRM_ERROR( "failed to install WARP ucode!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return ret; } ret = mga_warp_init( dev_priv ); if ( ret < 0 ) { DRM_ERROR( "failed to init WARP engine!\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return ret; } dev_priv->prim.status = (u32 *)dev_priv->status->handle; mga_do_wait_for_idle( dev_priv ); /* Init the primary DMA registers. */ MGA_WRITE( MGA_PRIMADDRESS, dev_priv->primary->offset | MGA_DMA_GENERAL ); #if 0 MGA_WRITE( MGA_PRIMPTR, virt_to_bus((void *)dev_priv->prim.status) | MGA_PRIMPTREN0 | /* Soft trap, SECEND, SETUPEND */ MGA_PRIMPTREN1 ); /* DWGSYNC */ #endif dev_priv->prim.start = (u8 *)dev_priv->primary->handle; dev_priv->prim.end = ((u8 *)dev_priv->primary->handle + dev_priv->primary->size); dev_priv->prim.size = dev_priv->primary->size; dev_priv->prim.tail = 0; dev_priv->prim.space = dev_priv->prim.size; dev_priv->prim.wrapped = 0; dev_priv->prim.last_flush = 0; dev_priv->prim.last_wrap = 0; dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE; dev_priv->prim.status[0] = dev_priv->primary->offset; dev_priv->prim.status[1] = 0; dev_priv->sarea_priv->last_wrap = 0; dev_priv->sarea_priv->last_frame.head = 0; dev_priv->sarea_priv->last_frame.wrap = 0; if ( mga_freelist_init( dev, dev_priv ) < 0 ) { DRM_ERROR( "could not initialize freelist\n" ); /* Assign dev_private so we can do cleanup. */ dev->dev_private = (void *)dev_priv; mga_do_cleanup_dma( dev ); return DRM_ERR(ENOMEM); } /* Make dev_private visable to others. */ dev->dev_private = (void *)dev_priv; retu/* * Copyright 2007 Jérôme Glisse * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * Authors: * Jérôme Glisse <glisse@freedesktop.org> */ #include "radeon_ms.h" extern struct radeon_ms_output radeon_ms_dac1; extern struct radeon_ms_output radeon_ms_dac2; extern const struct drm_output_funcs radeon_ms_output_funcs; static struct combios_connector_chip_info * radeon_ms_combios_get_connector_chip_info(struct drm_device *dev, int chip_num) { struct drm_radeon_private *dev_priv = dev->dev_private; struct radeon_ms_rom *rom = &dev_priv->rom; struct combios_header *header; struct combios_connector_table *connector_table; struct combios_connector_chip_info *connector_chip_info; uint32_t offset; int numof_chips, i; if (rom->type != ROM_COMBIOS || rom->rom_image == NULL) { return NULL; } header = rom->rom.combios_header; offset = header->usPointerToExtendedInitTable2; if ((offset + sizeof(struct combios_connector_table)) > rom->rom_size) { DRM_INFO("[radeon_ms] wrong COMBIOS connector offset\n"); return NULL; } if (!offset) { return NULL; } connector_table = (struct combios_connector_table *) &rom->rom_image[offset]; numof_chips = (connector_table->ucConnectorHeader & BIOS_CONNECTOR_HEADER__NUMBER_OF_CHIPS__MASK) >> BIOS_CONNECTOR_HEADER__NUMBER_OF_CHIPS__SHIFT; DRM_INFO("[radeon_ms] COMBIOS number of chip: %d (table rev: %d)\n", numof_chips, (connector_table->ucConnectorHeader & BIOS_CONNECTOR_HEADER__TABLE_REVISION__MASK) >> BIOS_CONNECTOR_HEADER__TABLE_REVISION__SHIFT); for (i = 0; i < numof_chips; i++) { int chip; connector_chip_info = &connector_table->sChipConnectorInfo[i]; chip = (connector_chip_info->ucChipHeader & BIOS_CHIPINFO_HEADER__CHIP_NUMBER__MASK) >> BIOS_CHIPINFO_HEADER__CHIP_NUMBER__SHIFT; DRM_INFO("[radeon_ms] COMBIOS chip: %d (asked for: %d)\n", chip, chip_num); if (chip == chip_num) { return connector_chip_info; } } return NULL; } static int radeon_combios_get_connector_infos(struct drm_device *dev, int connector_info, int *connector_type, int *ddc_line, int *tmds_type, int *dac_type) { struct drm_radeon_private *dev_priv = dev->dev_private; *connector_type = (connector_info & BIOS_CONNECTOR_INFO__TYPE__MASK) >> BIOS_CONNECTOR_INFO__TYPE__SHIFT; *ddc_line = (connector_info & BIOS_CONNECTOR_INFO__DDC_LINE__MASK) >> BIOS_CONNECTOR_INFO__DDC_LINE__SHIFT; *tmds_type = (connector_info & BIOS_CONNECTOR_INFO__TMDS_TYPE__MASK) >> BIOS_CONNECTOR_INFO__TMDS_TYPE__SHIFT; *dac_type = (connector_info & BIOS_CONNECTOR_INFO__DAC_TYPE__MASK) >> BIOS_CONNECTOR_INFO__DAC_TYPE__SHIFT; /* most XPRESS chips seem to specify DDC_CRT2 for their * VGA DDC port, however DDC never seems to work on that * port. Some have reported success on DDC_MONID, so * lets see what happens with that. */ if (dev_priv->family == CHIP_RS400 && *connector_type == BIOS_CONNECTOR_TYPE__CRT && *ddc_line == BIOS_DDC_LINE__CRT2) { *ddc_line = BIOS_DDC_LINE__MONID01; } /* XPRESS desktop chips seem to have a proprietary * connector listed for DVI-D, try and do the right * thing here. */ if (dev_priv->family == CHIP_RS400 && *connector_type == BIOS_CONNECTOR_TYPE__PROPRIETARY) { DRM_INFO("[radeon_ms] COMBIOS Proprietary connector " "found, assuming DVI-D\n"); *dac_type = 2; *tmds_type = BIOS_TMDS_TYPE__EXTERNAL; *connector_type = BIOS_CONNECTOR_TYPE__DVI_D; } return 0; } static int radeon_ms_combios_connector_add(struct drm_device *dev, int connector_number, int connector_type, uint32_t i2c_reg) { struct drm_radeon_private *dev_priv = dev->dev_private; struct radeon_ms_connector *connector = NULL; struct drm_output *output = NULL; connector = drm_alloc(sizeof(struct radeon_ms_connector), DRM_MEM_DRIVER); if (connector == NULL) { radeon_ms_connectors_destroy(dev); return -ENOMEM; } memset(connector, 0, sizeof(struct radeon_ms_connector)); connector->monitor_type = MT_NONE; connector->type = connector_type; connector->i2c_reg = i2c_reg; switch (connector->type) { case CONNECTOR_VGA: sprintf(connector->name, "VGA"); break; case CONNECTOR_DVI_I: sprintf(connector->name, "DVI-I"); break; case CONNECTOR_DVI_D: sprintf(connector->name, "DVI-D"); break; default: sprintf(connector->name, "UNKNOWN-CONNECTOR"); break; } if (i2c_reg) { connector->i2c = radeon_ms_i2c_create(dev, connector->i2c_reg, connector->name); if (connector->i2c == NULL) { radeon_ms_connectors_destroy(dev); return -ENOMEM; } } else { connector->i2c = NULL; } output = drm_output_create(dev, &radeon_ms_output_funcs, connector->type); if (output == NULL) { radeon_ms_connectors_destroy(dev); return -EINVAL; } connector->output = output; output->driver_private = connector; output->possible_crtcs = 0x3; dev_priv->connectors[connector_number] = connector; return 0; } int radeon_ms_combios_get_properties(struct drm_device *dev) { struct drm_radeon_private *dev_priv = dev->dev_private; struct radeon_ms_rom *rom = &dev_priv->rom; struct combios_pll_block *pll_block; struct combios_header *header; uint32_t offset; if (rom->type != ROM_COMBIOS || rom->rom_image == NULL) { return 0; } header = rom->rom.combios_header; offset = header->usPointerToPllInfoBlock; if ((offset + sizeof(struct combios_pll_block)) > rom->rom_size) { DRM_INFO("[radeon_ms] wrong COMBIOS pll block offset\n"); return 0; } if (!offset) { return 0; } pll_block = (struct combios_pll_block *)&rom->rom_image[offset]; dev_priv->properties.pll_reference_freq = pll_block->usDotClockRefFreq; dev_priv->properties.pll_reference_div = pll_block->usDotClockRefDiv; dev_priv->properties.pll_min_pll_freq = pll_block->ulDotClockMinFreq; dev_priv->properties.pll_max_pll_freq = pll_block->ulDotClockMaxFreq; dev_priv->properties.pll_reference_freq *= 10; dev_priv->properties.pll_min_pll_freq *= 10; dev_priv->properties.pll_max_pll_freq *= 10; DRM_INFO("[radeon_ms] COMBIOS pll reference frequency : %d\n", dev_priv->properties.pll_reference_freq); DRM_INFO("[radeon_ms] COMBIOS pll reference divider : %d\n", dev_priv->properties.pll_reference_div); DRM_INFO("[radeon_ms] COMBIOS pll minimum frequency : %d\n", dev_priv->properties.pll_min_pll_freq); DRM_INFO("[radeon_ms] COMBIOS pll maximum frequency : %d\n", dev_priv->properties.pll_max_pll_freq); return 1; } int radeon_ms_connectors_from_combios(struct drm_device *dev) { struct drm_radeon_private *dev_priv = dev->dev_private; struct combios_connector_chip_info *connector_chip_info; int connector_type, ddc_line, tmds_type, dac_type; int dac1, dac2, tmdsint, tmdsext; int numof_connector, i, c = 0, added, j; uint32_t i2c_reg; int ret; dac1 = dac2 = tmdsint = tmdsext = -1; connector_chip_info = radeon_ms_combios_get_connector_chip_info(dev, 1); if (connector_chip_info == NULL) { return -1; } numof_connector = (connector_chip_info->ucChipHeader & BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__MASK) >> BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__SHIFT; DRM_INFO("[radeon_ms] COMBIOS number of connector: %d\n", numof_connector); for (i = 0; i < numof_connector; i++) { int connector_info = connector_chip_info->sConnectorInfo[i]; ret = radeon_combios_get_connector_infos(dev, connector_info, &connector_type, &ddc_line, &tmds_type, &dac_type); switch (ddc_line) { case BIOS_DDC_LINE__MONID01: i2c_reg = GPIO_MONID; break; case BIOS_DDC_LINE__DVI: i2c_reg = GPIO_DVI_DDC; break; case BIOS_DDC_LINE__VGA: i2c_reg = GPIO_DDC1; break; case BIOS_DDC_LINE__CRT2: i2c_reg = GPIO_CRT2_DDC; break; case BIOS_DDC_LINE__GPIOPAD: i2c_reg = VIPPAD_EN; break; case BIOS_DDC_LINE__ZV_LCDPAD: i2c_reg = VIPPAD1_EN; break; default: i2c_reg = 0; break; } added = 0; switch (connector_type) { case BIOS_CONNECTOR_TYPE__CRT: ret = radeon_ms_combios_connector_add(dev, c, CONNECTOR_VGA, i2c_reg); if (ret) { return ret; } added = 1; break; case BIOS_CONNECTOR_TYPE__DVI_I: ret = radeon_ms_combios_connector_add(dev, c, CONNECTOR_DVI_I, i2c_reg); if (ret) { return ret; } added = 1; break; case BIOS_CONNECTOR_TYPE__DVI_D: ret = radeon_ms_combios_connector_add(dev, c, CONNECTOR_DVI_D, i2c_reg); if (ret) { return ret; } added = 1; break; default: break; } if (added) { j = 0; /* find to which output this connector is associated * by following same algo as in: * radeon_ms_outputs_from_combios*/ switch (dac_type) { case BIOS_DAC_TYPE__CRT: if (dac1 == -1) { dac1 = c; } dev_priv->connectors[c]->outputs[j++] = dac1; break; case BIOS_DAC_TYPE__NON_CRT: if (dac2 == -1) { dac2 = c; } dev_priv->connectors[c]->outputs[j++] = dac2; break; } #if 0 switch (tmds_type) { case BIOS_TMDS_TYPE__INTERNAL: if (tmdsint == -1) { tmdsint = c; } dev_priv->connectors[c]->outputs[j++] = tmdsint; break; case BIOS_TMDS_TYPE__EXTERNAL: if (tmdsext == -1) { tmdsext = c; } dev_priv->connectors[c]->outputs[j++] = tmdsext; break; } #endif c++; } } return c; } int radeon_ms_outputs_from_combios(struct drm_device *dev) { struct drm_radeon_private *dev_priv = dev->dev_private; struct combios_connector_chip_info *connector_chip_info; int connector_type, ddc_line, tmds_type, dac_type; int numof_connector, i, dac1_present, dac2_present, c = 0; int ret; dac1_present = dac2_present = 0; connector_chip_info = radeon_ms_combios_get_connector_chip_info(dev, 1); if (connector_chip_info == NULL) { return -1; } numof_connector = (connector_chip_info->ucChipHeader & BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__MASK) >> BIOS_CHIPINFO_HEADER__NUMBER_OF_CONNECTORS__SHIFT; DRM_INFO("[radeon_ms] COMBIOS number of connector: %d\n", numof_connector); for (i = 0; i < numof_connector; i++) { int connector_info = connector_chip_info->sConnectorInfo[i]; ret = radeon_combios_get_connector_infos(dev, connector_info, &connector_type, &ddc_line, &tmds_type, &dac_type); if (!dac1_present && dac_type == BIOS_DAC_TYPE__CRT) { dev_priv->outputs[c] = drm_alloc(sizeof(struct radeon_ms_output), DRM_MEM_DRIVER); if (dev_priv->outputs[c] == NULL) { radeon_ms_outputs_destroy(dev); return -ENOMEM; } memcpy(dev_priv->outputs[c], &radeon_ms_dac1, sizeof(struct radeon_ms_output)); dev_priv->outputs[c]->dev = dev; dev_priv->outputs[c]->initialize(dev_priv->outputs[c]); dac1_present = 1; c++; } if (!dac2_present && dac_type == BIOS_DAC_TYPE__NON_CRT) { dev_priv->outputs[c] = drm_alloc(sizeof(struct radeon_ms_output), DRM_MEM_DRIVER); if (dev_priv->outputs[c] == NULL) { radeon_ms_outputs_destroy(dev); return -ENOMEM; } memcpy(dev_priv->outputs[c], &radeon_ms_dac2, sizeof(struct radeon_ms_output)); dev_priv->outputs[c]->dev = dev; dev_priv->outputs[c]->initialize(dev_priv->outputs[c]); dac1_present = 1; c++; } } return c; }