/* Copyright (c) 2002,2007-2009, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "gsl.h" #include "gsl_hal.h" #include "gsl_cmdstream.h" #ifdef GSL_BLD_YAMATO ////////////////////////////////////////////////////////////////////////////// // ucode ////////////////////////////////////////////////////////////////////////////// #define uint32 unsigned int #include "pm4_microcode.inl" #include "pfp_microcode_nrt.inl" #undef uint32 ////////////////////////////////////////////////////////////////////////////// // defines ////////////////////////////////////////////////////////////////////////////// #define GSL_RB_NOP_SIZEDWORDS 2 // default is 2 #define GSL_RB_PROTECTED_MODE_CONTROL 0x00000000 // protected mode error checking below register address 0x800 // note: if CP_INTERRUPT packet is used then checking needs // to change to below register address 0x7C8 ////////////////////////////////////////////////////////////////////////////// // ringbuffer size log2 quadwords equivalent ////////////////////////////////////////////////////////////////////////////// OSINLINE unsigned int gsl_ringbuffer_sizelog2quadwords(unsigned int sizedwords) { unsigned int sizelog2quadwords = 0; int i = sizedwords >> 1; while (i >>= 1) { sizelog2quadwords++; } return (sizelog2quadwords); } ////////////////////////////////////////////////////////////////////////////// // private prototypes ////////////////////////////////////////////////////////////////////////////// #ifdef _DEBUG static void kgsl_ringbuffer_debug(gsl_ringbuffer_t *rb, gsl_rb_debug_t *rb_debug); #endif ////////////////////////////////////////////////////////////////////////////// // functions ////////////////////////////////////////////////////////////////////////////// void kgsl_cp_intrcallback(gsl_intrid_t id, void *cookie) { gsl_ringbuffer_t *rb = (gsl_ringbuffer_t *) cookie; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> void kgsl_cp_intrcallback(gsl_intrid_t id=%I, void *cookie=0x%08x)\n", id, cookie ); switch(id) { // error condition interrupt case GSL_INTR_YDX_CP_T0_PACKET_IN_IB: case GSL_INTR_YDX_CP_OPCODE_ERROR: case GSL_INTR_YDX_CP_PROTECTED_MODE_ERROR: case GSL_INTR_YDX_CP_RESERVED_BIT_ERROR: case GSL_INTR_YDX_CP_IB_ERROR: rb->device->ftbl.device_destroy(rb->device); break; // non-error condition interrupt case GSL_INTR_YDX_CP_SW_INT: case GSL_INTR_YDX_CP_IB2_INT: case GSL_INTR_YDX_CP_IB1_INT: case GSL_INTR_YDX_CP_RING_BUFFER: // signal intr completion event kos_event_signal(rb->device->intr.evnt[id]); break; default: break; } kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_cp_intrcallback.\n" ); } //---------------------------------------------------------------------------- void kgsl_ringbuffer_watchdog() { gsl_ringbuffer_t *rb = &(gsl_driver.device[GSL_DEVICE_YAMATO-1]).ringbuffer; // device_id is 1 based kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> void kgsl_ringbuffer_watchdog()\n" ); if (rb->flags & GSL_FLAGS_STARTED) { GSL_RB_GET_READPTR(rb, &rb->rptr); // ringbuffer is currently not empty if (rb->rptr != rb->wptr) { // and a rptr sample was taken during interval n-1 if (rb->watchdog.flags & GSL_FLAGS_ACTIVE) { // and the rptr did not advance between interval n-1 and n if (rb->rptr == rb->watchdog.rptr_sample) { // then the core has hung kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_FATAL, "ERROR: Watchdog detected core hung.\n" ); rb->device->ftbl.device_destroy(rb->device); return; } } // save rptr sample for interval n rb->watchdog.flags |= GSL_FLAGS_ACTIVE; rb->watchdog.rptr_sample = rb->rptr; } else { // clear rptr sample for interval n rb->watchdog.flags &= ~GSL_FLAGS_ACTIVE; } } kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_watchdog.\n" ); } //---------------------------------------------------------------------------- #ifdef _DEBUG OSINLINE void kgsl_ringbuffer_checkregister(unsigned int reg, int pmodecheck) { if (pmodecheck) { // check for register protection mode violation if (reg <= (GSL_RB_PROTECTED_MODE_CONTROL & 0x3FFF)) { kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "ERROR: Register protection mode violation.\n" ); KOS_ASSERT(0); } } // range check register offset if (reg > (gsl_driver.device[GSL_DEVICE_YAMATO-1].regspace.sizebytes >> 2)) { kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "ERROR: Register out of range.\n" ); KOS_ASSERT(0); } } //---------------------------------------------------------------------------- void kgsl_ringbuffer_checkpm4type0(unsigned int header, unsigned int** cmds, int pmodeoff) { pm4_type0 pm4header = *((pm4_type0*) &header); unsigned int reg; if (pm4header.one_reg_wr) { reg = pm4header.base_index; } else { reg = pm4header.base_index + pm4header.count; } kgsl_ringbuffer_checkregister(reg, !pmodeoff); *cmds += pm4header.count + 1; } //---------------------------------------------------------------------------- void kgsl_ringbuffer_checkpm4type3(unsigned int header, unsigned int** cmds, int indirection, int pmodeoff) { pm4_type3 pm4header = *((pm4_type3*) &header); unsigned int *ordinal2 = *cmds; unsigned int *ibcmds, *end; unsigned int reg, length; // check indirect buffer level if (indirection > 2) { kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "ERROR: Only two levels of indirection supported.\n" ); KOS_ASSERT(0); } switch(pm4header.it_opcode) { case PM4_INDIRECT_BUFFER: case PM4_INDIRECT_BUFFER_PFD: // determine ib host base and end address ibcmds = (unsigned int*) kgsl_sharedmem_convertaddr(*ordinal2, 0); end = ibcmds + *(ordinal2 + 1); // walk through the ib while(ibcmds < end) { unsigned int tmpheader = *(ibcmds++); switch(tmpheader & PM4_PKT_MASK) { case PM4_TYPE0_PKT: kgsl_ringbuffer_checkpm4type0(tmpheader, &ibcmds, pmodeoff); break; case PM4_TYPE1_PKT: case PM4_TYPE2_PKT: break; case PM4_TYPE3_PKT: kgsl_ringbuffer_checkpm4type3(tmpheader, &ibcmds, (indirection + 1), pmodeoff); break; } } break; case PM4_ME_INIT: if(indirection != 0) { kgsl_log_write( KGSL_LOG_GROUP_MEMORY | KGSL_LOG_LEVEL_ERROR, "ERROR: ME INIT packet cannot reside in an ib.\n" ); KOS_ASSERT(0); } break; case PM4_REG_RMW: reg = (*ordinal2) & 0x1FFF; kgsl_ringbuffer_checkregister(reg, !pmodeoff); break; case PM4_SET_CONSTANT: if((((*ordinal2) >> 16) & 0xFF) == 0x4) // incremental register update { reg = 0x2000 + ((*ordinal2) & 0x3FF); // gfx decode space address starts at 0x2000 length = pm4header.count - 1; kgsl_ringbuffer_checkregister(reg + length, 0); } break; case PM4_LOAD_CONSTANT_CONTEXT: if(((*(ordinal2 + 1) >> 16) & 0xFF) == 0x4) // incremental register update { reg = 0x2000 + (*(ordinal2 + 1) & 0x3FF); // gfx decode space address starts at 0x2000 length = *(ordinal2 + 2); kgsl_ringbuffer_checkregister(reg + length, 0); } break; case PM4_COND_WRITE: if(((*ordinal2) & 0x00000100) == 0x0) // write to register { reg = *(ordinal2 + 4) & 0x3FFF; kgsl_ringbuffer_checkregister(reg, !pmodeoff); } break; } *cmds += pm4header.count + 1; } //---------------------------------------------------------------------------- void kgsl_ringbuffer_checkpm4(unsigned int* cmds, unsigned int sizedwords, int pmodeoff) { unsigned int *ringcmds = cmds; unsigned int *end = cmds + sizedwords; while(ringcmds < end) { unsigned int header = *(ringcmds++); switch(header & PM4_PKT_MASK) { case PM4_TYPE0_PKT: kgsl_ringbuffer_checkpm4type0(header, &ringcmds, pmodeoff); break; case PM4_TYPE1_PKT: case PM4_TYPE2_PKT: break; case PM4_TYPE3_PKT: kgsl_ringbuffer_checkpm4type3(header, &ringcmds, 0, pmodeoff); break; } } } #endif // _DEBUG //---------------------------------------------------------------------------- static void kgsl_ringbuffer_submit(gsl_ringbuffer_t *rb) { unsigned int value; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> static void kgsl_ringbuffer_submit(gsl_ringbuffer_t *rb=0x%08x)\n", rb ); KOS_ASSERT(rb->wptr != 0); kgsl_device_active(rb->device); GSL_RB_UPDATE_WPTR_POLLING(rb); // send the wptr to the hw rb->device->ftbl.device_regwrite(rb->device, mmCP_RB_WPTR, rb->wptr); // force wptr register to be updated do { rb->device->ftbl.device_regread(rb->device, mmCP_RB_WPTR, &value); } while (value != rb->wptr); rb->flags |= GSL_FLAGS_ACTIVE; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_submit.\n" ); } //---------------------------------------------------------------------------- static int kgsl_ringbuffer_waitspace(gsl_ringbuffer_t *rb, unsigned int numcmds, int wptr_ahead) { int nopcount; unsigned int freecmds; unsigned int *cmds; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> static int kgsl_ringbuffer_waitspace(gsl_ringbuffer_t *rb=0x%08x, unsigned int numcmds=%d, int wptr_ahead=%d)\n", rb, numcmds, wptr_ahead ); // if wptr ahead, fill the remaining with NOPs if (wptr_ahead) { nopcount = rb->sizedwords - rb->wptr - 1; // -1 for header cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; GSL_RB_WRITE(cmds, pm4_nop_packet(nopcount)); rb->wptr++; kgsl_ringbuffer_submit(rb); rb->wptr = 0; GSL_RB_STATS(rb->stats.wraps++); } KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, KGSL_DEBUG_DUMPX(BB_DUMP_RBWAIT, GSL_DEVICE_YAMATO, rb->wptr, numcmds, "kgsl_ringbuffer_waitspace")); // wait for space in ringbuffer for( ; ; ) { GSL_RB_GET_READPTR(rb, &rb->rptr); freecmds = rb->rptr - rb->wptr; if ((freecmds == 0) || (freecmds > numcmds)) { break; } } kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_waitspace. Return value %B\n", GSL_SUCCESS ); return (GSL_SUCCESS); } //---------------------------------------------------------------------------- static unsigned int * kgsl_ringbuffer_addcmds(gsl_ringbuffer_t *rb, unsigned int numcmds) { unsigned int *ptr; int status = GSL_SUCCESS; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> static unsigned int* kgsl_ringbuffer_addcmds(gsl_ringbuffer_t *rb=0x%08x, unsigned int numcmds=%d)\n", rb, numcmds ); KOS_ASSERT(numcmds < rb->sizedwords); // update host copy of read pointer when running in safe mode if (rb->device->flags & GSL_FLAGS_SAFEMODE) { GSL_RB_GET_READPTR(rb, &rb->rptr); } // check for available space if (rb->wptr >= rb->rptr) { // wptr ahead or equal to rptr if ((rb->wptr + numcmds) > (rb->sizedwords - GSL_RB_NOP_SIZEDWORDS)) // reserve dwords for nop packet { status = kgsl_ringbuffer_waitspace(rb, numcmds, 1); } } else { // wptr behind rptr if ((rb->wptr + numcmds) >= rb->rptr) { status = kgsl_ringbuffer_waitspace(rb, numcmds, 0); } // check for remaining space if ((rb->wptr + numcmds) > (rb->sizedwords - GSL_RB_NOP_SIZEDWORDS)) // reserve dwords for nop packet { status = kgsl_ringbuffer_waitspace(rb, numcmds, 1); } } ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; rb->wptr += numcmds; if (status == GSL_SUCCESS) { kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_waitspace. Return value 0x%08x\n", ptr ); return (ptr); } else { kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_waitspace. Return value 0x%08x\n", NULL ); return (NULL); } } //---------------------------------------------------------------------------- int kgsl_ringbuffer_start(gsl_ringbuffer_t *rb) { int status; cp_rb_cntl_u cp_rb_cntl; int i; unsigned int *cmds; gsl_device_t *device = rb->device; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> static int kgsl_ringbuffer_start(gsl_ringbuffer_t *rb=0x%08x)\n", rb ); if (rb->flags & GSL_FLAGS_STARTED) { kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_start. Return value %B\n", GSL_SUCCESS ); return (GSL_SUCCESS); } // clear memptrs values kgsl_sharedmem_set0(&rb->memptrs_desc, 0, 0, sizeof(gsl_rbmemptrs_t)); // clear ringbuffer kgsl_sharedmem_set0(&rb->buffer_desc, 0, 0x12341234, (rb->sizedwords << 2)); // setup WPTR polling address device->ftbl.device_regwrite(device, mmCP_RB_WPTR_BASE, (rb->memptrs_desc.gpuaddr + GSL_RB_MEMPTRS_WPTRPOLL_OFFSET)); // setup WPTR delay device->ftbl.device_regwrite(device, mmCP_RB_WPTR_DELAY, 0/*0x70000010*/); // setup RB_CNTL device->ftbl.device_regread(device, mmCP_RB_CNTL, (unsigned int *)&cp_rb_cntl); cp_rb_cntl.f.rb_bufsz = gsl_ringbuffer_sizelog2quadwords(rb->sizedwords); // size of ringbuffer cp_rb_cntl.f.rb_blksz = rb->blksizequadwords; // quadwords to read before updating mem RPTR cp_rb_cntl.f.rb_poll_en = GSL_RB_CNTL_POLL_EN; // WPTR polling cp_rb_cntl.f.rb_no_update = GSL_RB_CNTL_NO_UPDATE; // mem RPTR writebacks device->ftbl.device_regwrite(device, mmCP_RB_CNTL, cp_rb_cntl.val); // setup RB_BASE device->ftbl.device_regwrite(device, mmCP_RB_BASE, rb->buffer_desc.gpuaddr); // setup RPTR_ADDR device->ftbl.device_regwrite(device, mmCP_RB_RPTR_ADDR, rb->memptrs_desc.gpuaddr + GSL_RB_MEMPTRS_RPTR_OFFSET); // explicitly clear all cp interrupts when running in safe mode if (rb->device->flags & GSL_FLAGS_SAFEMODE) { device->ftbl.device_regwrite(device, mmCP_INT_ACK, 0xFFFFFFFF); } // setup scratch/timestamp addr device->ftbl.device_regwrite(device, mmSCRATCH_ADDR, device->memstore.gpuaddr + GSL_DEVICE_MEMSTORE_OFFSET(soptimestamp)); // setup scratch/timestamp mask device->ftbl.device_regwrite(device, mmSCRATCH_UMSK, GSL_RB_MEMPTRS_SCRATCH_MASK); // load the CP ucode device->ftbl.device_regwrite(device, mmCP_DEBUG, 0x02000000); device->ftbl.device_regwrite(device, mmCP_ME_RAM_WADDR, 0); for (i = 0; i < PM4_MICROCODE_SIZE; i++ ) { device->ftbl.device_regwrite(device, mmCP_ME_RAM_DATA, aPM4_Microcode[i][0]); device->ftbl.device_regwrite(device, mmCP_ME_RAM_DATA, aPM4_Microcode[i][1]); device->ftbl.device_regwrite(device, mmCP_ME_RAM_DATA, aPM4_Microcode[i][2]); } // load the prefetch parser ucode device->ftbl.device_regwrite(device, mmCP_PFP_UCODE_ADDR, 0); for ( i = 0; i < PFP_MICROCODE_SIZE_NRT; i++ ) { device->ftbl.device_regwrite(device, mmCP_PFP_UCODE_DATA, aPFP_Microcode_nrt[i]); } // queue thresholds ??? device->ftbl.device_regwrite(device, mmCP_QUEUE_THRESHOLDS, 0x000C0804); // reset pointers rb->rptr = 0; rb->wptr = 0; // init timestamp rb->timestamp = 0; GSL_RB_INIT_TIMESTAMP(rb); // clear ME_HALT to start micro engine device->ftbl.device_regwrite(device, mmCP_ME_CNTL, 0); // ME_INIT cmds = kgsl_ringbuffer_addcmds(rb, 19); GSL_RB_WRITE(cmds, PM4_HDR_ME_INIT); GSL_RB_WRITE(cmds, 0x000003ff); // All fields present (bits 9:0) GSL_RB_WRITE(cmds, 0x00000000); // Disable/Enable Real-Time Stream processing (present but ignored) GSL_RB_WRITE(cmds, 0x00000000); // Enable (2D to 3D) and (3D to 2D) implicit synchronization (present but ignored) GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmRB_SURFACE_INFO)); GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmPA_SC_WINDOW_OFFSET)); GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmVGT_MAX_VTX_INDX)); GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmSQ_PROGRAM_CNTL)); GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmRB_DEPTHCONTROL)); GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmPA_SU_POINT_SIZE)); GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmPA_SC_LINE_CNTL)); GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(mmPA_SU_POLY_OFFSET_FRONT_SCALE)); GSL_RB_WRITE(cmds, 0x80000180); // Vertex and Pixel Shader Start Addresses in instructions (3 DWORDS per instruction) GSL_RB_WRITE(cmds, 0x00000001); // Maximum Contexts GSL_RB_WRITE(cmds, 0x00000000); // Write Confirm Interval and The CP will wait the wait_interval * 16 clocks between polling GSL_RB_WRITE(cmds, 0x00000000); // NQ and External Memory Swap GSL_RB_WRITE(cmds, GSL_RB_PROTECTED_MODE_CONTROL); // Protected mode error checking GSL_RB_WRITE(cmds, 0x00000000); // Disable header dumping and Header dump address GSL_RB_WRITE(cmds, 0x00000000); // Header dump size KGSL_DEBUG(GSL_DBGFLAGS_PM4CHECK, kgsl_ringbuffer_checkpm4((unsigned int *)rb->buffer_desc.hostptr, 19, 1)); KGSL_DEBUG(GSL_DBGFLAGS_PM4, KGSL_DEBUG_DUMPPM4((unsigned int *)rb->buffer_desc.hostptr, 19)); kgsl_ringbuffer_submit(rb); // idle device to validate ME INIT status = device->ftbl.device_idle(device, GSL_TIMEOUT_DEFAULT); if (status == GSL_SUCCESS) { rb->flags |= GSL_FLAGS_STARTED; } // enable cp interrupts kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_SW_INT, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_T0_PACKET_IN_IB, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_OPCODE_ERROR, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_PROTECTED_MODE_ERROR, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_RESERVED_BIT_ERROR, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_IB_ERROR, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_IB2_INT, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_IB1_INT, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_attach(&device->intr, GSL_INTR_YDX_CP_RING_BUFFER, kgsl_cp_intrcallback, (void *) rb); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_SW_INT); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_T0_PACKET_IN_IB); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_OPCODE_ERROR); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_PROTECTED_MODE_ERROR); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_RESERVED_BIT_ERROR); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_IB_ERROR); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_IB2_INT); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_IB1_INT); kgsl_intr_enable(&device->intr, GSL_INTR_YDX_CP_RING_BUFFER); kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_start. Return value %B\n", status ); return (status); } //---------------------------------------------------------------------------- int kgsl_ringbuffer_stop(gsl_ringbuffer_t *rb) { kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> static int kgsl_ringbuffer_stop(gsl_ringbuffer_t *rb=0x%08x)\n", rb ); if (rb->flags & GSL_FLAGS_STARTED) { // disable cp interrupts kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_SW_INT); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_T0_PACKET_IN_IB); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_OPCODE_ERROR); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_PROTECTED_MODE_ERROR); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_RESERVED_BIT_ERROR); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_IB_ERROR); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_IB2_INT); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_IB1_INT); kgsl_intr_detach(&rb->device->intr, GSL_INTR_YDX_CP_RING_BUFFER); // ME_HALT rb->device->ftbl.device_regwrite(rb->device, mmCP_ME_CNTL, 0x10000000); rb->flags &= ~GSL_FLAGS_STARTED; } kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_stop. Return value %B\n", GSL_SUCCESS ); return (GSL_SUCCESS); } //---------------------------------------------------------------------------- int kgsl_ringbuffer_init(gsl_device_t *device) { int status; gsl_flags_t flags; gsl_ringbuffer_t *rb = &device->ringbuffer; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> int kgsl_ringbuffer_init(gsl_device_t *device=0x%08x)\n", device ); rb->device = device; rb->sizedwords = (2 << gsl_cfg_rb_sizelog2quadwords); rb->blksizequadwords = gsl_cfg_rb_blksizequadwords; // allocate memory for ringbuffer, needs to be double octword aligned // align on page from contiguous physical memory flags = (GSL_MEMFLAGS_ALIGNPAGE | GSL_MEMFLAGS_CONPHYS | GSL_MEMFLAGS_STRICTREQUEST); KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, flags = (GSL_MEMFLAGS_ALIGNPAGE | GSL_MEMFLAGS_STRICTREQUEST)); /* set MMU table for ringbuffer */ status = kgsl_sharedmem_alloc0(device->id, flags, (rb->sizedwords << 2), &rb->buffer_desc); KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, KGSL_DEBUG_DUMPX(BB_DUMP_RINGBUF_SET, (unsigned int)rb->buffer_desc.gpuaddr, (unsigned int)rb->buffer_desc.hostptr, 0, "kgsl_ringbuffer_init")); if (status != GSL_SUCCESS) { kgsl_ringbuffer_close(rb); kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_init. Return value %B\n", status ); return (status); } // allocate memory for polling and timestamps flags = (GSL_MEMFLAGS_ALIGN32 | GSL_MEMFLAGS_CONPHYS); KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, flags = GSL_MEMFLAGS_ALIGN32); status = kgsl_sharedmem_alloc0(device->id, flags, sizeof(gsl_rbmemptrs_t), &rb->memptrs_desc); if (status != GSL_SUCCESS) { kgsl_ringbuffer_close(rb); kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_init. Return value %B\n", status ); return (status); } // overlay structure on memptrs memory rb->memptrs = (gsl_rbmemptrs_t *)rb->memptrs_desc.hostptr; rb->flags |= GSL_FLAGS_INITIALIZED; // validate command stream data when running in safe mode if (device->flags & GSL_FLAGS_SAFEMODE) { gsl_driver.flags_debug |= GSL_DBGFLAGS_PM4CHECK; } // start ringbuffer status = kgsl_ringbuffer_start(rb); if (status != GSL_SUCCESS) { kgsl_ringbuffer_close(rb); kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_init. Return value %B\n", status ); return (status); } kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_init. Return value %B\n", GSL_SUCCESS ); return (GSL_SUCCESS); } //---------------------------------------------------------------------------- int kgsl_ringbuffer_close(gsl_ringbuffer_t *rb) { kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> int kgsl_ringbuffer_close(gsl_ringbuffer_t *rb=0x%08x)\n", rb ); // stop ringbuffer kgsl_ringbuffer_stop(rb); // free buffer if (rb->buffer_desc.hostptr) { kgsl_sharedmem_free0(&rb->buffer_desc, GSL_CALLER_PROCESSID_GET()); } // free memory pointers if (rb->memptrs_desc.hostptr) { kgsl_sharedmem_free0(&rb->memptrs_desc, GSL_CALLER_PROCESSID_GET()); } rb->flags &= ~GSL_FLAGS_INITIALIZED; kos_memset(rb, 0, sizeof(gsl_ringbuffer_t)); kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_close. Return value %B\n", GSL_SUCCESS ); return (GSL_SUCCESS); } //---------------------------------------------------------------------------- gsl_timestamp_t kgsl_ringbuffer_issuecmds(gsl_device_t *device, int pmodeoff, unsigned int *cmds, int sizedwords, unsigned int pid) { gsl_ringbuffer_t *rb = &device->ringbuffer; unsigned int pmodesizedwords; unsigned int *ringcmds; unsigned int timestamp; pmodeoff = 0; kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> gsl_timestamp_t kgsl_ringbuffer_issuecmds(gsl_device_t *device=0x%08x, int pmodeoff=%d, unsigned int *cmds=0x%08x, int sizedwords=%d, unsigned int pid=0x%08x)\n", device, pmodeoff, cmds, sizedwords, pid ); if (!(device->ringbuffer.flags & GSL_FLAGS_STARTED)) { return (0); } // set mmu pagetable kgsl_mmu_setpagetable(device, pid); KGSL_DEBUG(GSL_DBGFLAGS_PM4CHECK, kgsl_ringbuffer_checkpm4(cmds, sizedwords, pmodeoff)); KGSL_DEBUG(GSL_DBGFLAGS_PM4, KGSL_DEBUG_DUMPPM4(cmds, sizedwords)); // reserve space to temporarily turn off protected mode error checking if needed pmodesizedwords = pmodeoff ? 8 : 0; #if defined GSL_RB_TIMESTAMP_INTERUPT pmodesizedwords += 2; #endif // allocate space in ringbuffer ringcmds = kgsl_ringbuffer_addcmds(rb, pmodesizedwords + sizedwords + 6); if (pmodeoff) { // disable protected mode error checking *ringcmds++ = pm4_type3_packet(PM4_ME_INIT, 2); *ringcmds++ = 0x00000080; *ringcmds++ = 0x00000000; } // copy the cmds to the ringbuffer kos_memcpy(ringcmds, cmds, (sizedwords << 2)); ringcmds += sizedwords; if (pmodeoff) { *ringcmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); *ringcmds++ = 0; // re-enable protected mode error checking *ringcmds++ = pm4_type3_packet(PM4_ME_INIT, 2); *ringcmds++ = 0x00000080; *ringcmds++ = GSL_RB_PROTECTED_MODE_CONTROL; } // increment timestamp rb->timestamp++; timestamp = rb->timestamp; // start-of-pipeline and end-of-pipeline timestamps *ringcmds++ = pm4_type0_packet(mmCP_TIMESTAMP, 1); *ringcmds++ = rb->timestamp; *ringcmds++ = pm4_type3_packet(PM4_EVENT_WRITE, 3); *ringcmds++ = CACHE_FLUSH_TS; *ringcmds++ = device->memstore.gpuaddr + GSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp); *ringcmds++ = rb->timestamp; #if defined GSL_RB_TIMESTAMP_INTERUPT *ringcmds++ = pm4_type3_packet(PM4_INTERRUPT, 1); *ringcmds++ = 0x80000000; #endif KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, KGSL_DEBUG_DUMPX(BB_DUMP_MEMWRITE, (unsigned int)((char*)ringcmds - ((pmodesizedwords + sizedwords + 6) << 2)), (unsigned int)((char*)ringcmds - ((pmodesizedwords + sizedwords + 6) << 2)), (pmodesizedwords + sizedwords + 6) << 2, "kgsl_ringbuffer_issuecmds")); // issue the commands kgsl_ringbuffer_submit(rb); // stats GSL_RB_STATS(rb->stats.wordstotal += sizedwords); GSL_RB_STATS(rb->stats.issues++); kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_issuecmds. Return value %d\n", timestamp ); // return timestamp of issued commands return (timestamp); } //---------------------------------------------------------------------------- int kgsl_ringbuffer_issueibcmds(gsl_device_t *device, int drawctxt_index, gpuaddr_t ibaddr, int sizedwords, gsl_timestamp_t *timestamp, gsl_flags_t flags) { unsigned int link[3]; int dumpx_swap; (void)dumpx_swap; // used only when BB_DUMPX is defined kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> gsl_timestamp_t kgsl_ringbuffer_issueibcmds(gsl_device_t device=%0x%08x, int drawctxt_index=%d, gpuaddr_t ibaddr=0x%08x, int sizedwords=%d, gsl_timestamp_t *timestamp=0x%08x)\n", device, drawctxt_index, ibaddr, sizedwords, timestamp ); if (!(device->ringbuffer.flags & GSL_FLAGS_STARTED)) { kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_issueibcmds. Return value %B\n", GSL_FAILURE ); return (GSL_FAILURE); } KOS_ASSERT(ibaddr); KOS_ASSERT(sizedwords); KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, dumpx_swap = kgsl_dumpx_parse_ibs(ibaddr, sizedwords)); // context switch if needed kgsl_drawctxt_switch(device, &device->drawctxt[drawctxt_index], flags); link[0] = PM4_HDR_INDIRECT_BUFFER_PFD; link[1] = ibaddr; link[2] = sizedwords; *timestamp = kgsl_ringbuffer_issuecmds(device, 0, &link[0], 3, GSL_CALLER_PROCESSID_GET()); // idle device when running in safe mode if (device->flags & GSL_FLAGS_SAFEMODE) { device->ftbl.device_idle(device, GSL_TIMEOUT_DEFAULT); } else { KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, { // insert wait for idle after every IB1 // this is conservative but works reliably and is ok even for performance simulations device->ftbl.device_idle(device, GSL_TIMEOUT_DEFAULT); }); } KGSL_DEBUG(GSL_DBGFLAGS_DUMPX, { if(dumpx_swap) { KGSL_DEBUG_DUMPX( BB_DUMP_EXPORT_CBUF, 0, 0, 0, "resolve"); KGSL_DEBUG_DUMPX( BB_DUMP_FLUSH,0,0,0," "); } }); kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_issueibcmds. Return value %B\n", GSL_SUCCESS ); return (GSL_SUCCESS); } //---------------------------------------------------------------------------- #ifdef _DEBUG static void kgsl_ringbuffer_debug(gsl_ringbuffer_t *rb, gsl_rb_debug_t *rb_debug) { kos_memset(rb_debug, 0, sizeof(gsl_rb_debug_t)); rb_debug->pm4_ucode_rel = PM4_MICROCODE_VERSION; rb_debug->pfp_ucode_rel = PFP_MICROCODE_VERSION; rb->device->ftbl.device_regread(rb->device, mmCP_RB_BASE, (unsigned int *)&rb_debug->cp_rb_base); rb->device->ftbl.device_regread(rb->device, mmCP_RB_CNTL, (unsigned int *)&rb_debug->cp_rb_cntl); rb->device->ftbl.device_regread(rb->device, mmCP_RB_RPTR_ADDR, (unsigned int *)&rb_debug->cp_rb_rptr_addr); rb->device->ftbl.device_regread(rb->device, mmCP_RB_RPTR, (unsigned int *)&rb_debug->cp_rb_rptr); rb->device->ftbl.device_regread(rb->device, mmCP_RB_WPTR, (unsigned int *)&rb_debug->cp_rb_wptr); rb->device->ftbl.device_regread(rb->device, mmCP_RB_WPTR_BASE, (unsigned int *)&rb_debug->cp_rb_wptr_base); rb->device->ftbl.device_regread(rb->device, mmSCRATCH_UMSK, (unsigned int *)&rb_debug->scratch_umsk); rb->device->ftbl.device_regread(rb->device, mmSCRATCH_ADDR, (unsigned int *)&rb_debug->scratch_addr); rb->device->ftbl.device_regread(rb->device, mmCP_ME_CNTL, (unsigned int *)&rb_debug->cp_me_cntl); rb->device->ftbl.device_regread(rb->device, mmCP_ME_STATUS, (unsigned int *)&rb_debug->cp_me_status); rb->device->ftbl.device_regread(rb->device, mmCP_DEBUG, (unsigned int *)&rb_debug->cp_debug); rb->device->ftbl.device_regread(rb->device, mmCP_STAT, (unsigned int *)&rb_debug->cp_stat); rb->device->ftbl.device_regread(rb->device, mmRBBM_STATUS, (unsigned int *)&rb_debug->rbbm_status); rb_debug->sop_timestamp = kgsl_cmdstream_readtimestamp(rb->device->id, GSL_TIMESTAMP_CONSUMED); rb_debug->eop_timestamp = kgsl_cmdstream_readtimestamp(rb->device->id, GSL_TIMESTAMP_RETIRED); } #endif //---------------------------------------------------------------------------- int kgsl_ringbuffer_querystats(gsl_ringbuffer_t *rb, gsl_rbstats_t *stats) { #ifdef GSL_STATS_RINGBUFFER KOS_ASSERT(stats); if (!(rb->flags & GSL_FLAGS_STARTED)) { return (GSL_FAILURE); } kos_memcpy(stats, &rb->stats, sizeof(gsl_rbstats_t)); return (GSL_SUCCESS); #else // unreferenced formal parameters (void) rb; (void) stats; return (GSL_FAILURE_NOTSUPPORTED); #endif // GSL_STATS_RINGBUFFER } //---------------------------------------------------------------------------- int kgsl_ringbuffer_bist(gsl_ringbuffer_t *rb) { unsigned int *cmds; unsigned int temp, k, j; int status; int i; #ifdef _DEBUG gsl_rb_debug_t rb_debug; #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "--> int kgsl_ringbuffer_bist(gsl_ringbuffer_t *rb=0x%08x)\n", rb ); if (!(rb->flags & GSL_FLAGS_STARTED)) { kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_FAILURE ); return (GSL_FAILURE); } // simple nop submit cmds = kgsl_ringbuffer_addcmds(rb, 2); if (!cmds) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_FAILURE ); return (GSL_FAILURE); } GSL_RB_WRITE(cmds, pm4_nop_packet(1)); GSL_RB_WRITE(cmds, 0xDEADBEEF); kgsl_ringbuffer_submit(rb); status = rb->device->ftbl.device_idle(rb->device, GSL_TIMEOUT_DEFAULT); if (status != GSL_SUCCESS) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", status ); return (status); } // simple scratch submit cmds = kgsl_ringbuffer_addcmds(rb, 2); if (!cmds) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_FAILURE ); return (GSL_FAILURE); } GSL_RB_WRITE(cmds, pm4_type0_packet(mmSCRATCH_REG7, 1)); GSL_RB_WRITE(cmds, 0xFEEDF00D); kgsl_ringbuffer_submit(rb); status = rb->device->ftbl.device_idle(rb->device, GSL_TIMEOUT_DEFAULT); if (status != GSL_SUCCESS) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", status ); return (status); } rb->device->ftbl.device_regread(rb->device, mmSCRATCH_REG7, &temp); if (temp != 0xFEEDF00D) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_FAILURE ); return (GSL_FAILURE); } // simple wraps for (i = 0; i < 256; i+=2) { j = ((rb->sizedwords >> 2) - 256) + i; cmds = kgsl_ringbuffer_addcmds(rb, j); if (!cmds) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_FAILURE ); return (GSL_FAILURE); } k = 0; while (k < j) { k+=2; GSL_RB_WRITE(cmds, pm4_type0_packet(mmSCRATCH_REG7, 1)); GSL_RB_WRITE(cmds, k); } kgsl_ringbuffer_submit(rb); status = rb->device->ftbl.device_idle(rb->device, GSL_TIMEOUT_DEFAULT); if (status != GSL_SUCCESS) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", status ); return (status); } rb->device->ftbl.device_regread(rb->device, mmSCRATCH_REG7, &temp); if (temp != k) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_FAILURE ); return (GSL_FAILURE); } } // max size submits, TODO do this at least with regreads for (i = 0; i < 256; i++) { cmds = kgsl_ringbuffer_addcmds(rb, (rb->sizedwords >> 2)); if (!cmds) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_FAILURE ); return (GSL_FAILURE); } GSL_RB_WRITE(cmds, pm4_nop_packet((rb->sizedwords >> 2) - 1)); kgsl_ringbuffer_submit(rb); status = rb->device->ftbl.device_idle(rb->device, GSL_TIMEOUT_DEFAULT); if (status != GSL_SUCCESS) { #ifdef _DEBUG kgsl_ringbuffer_debug(rb, &rb_debug); #endif kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", status ); return (status); } } // submit load with randomness #ifdef GSL_RB_USE_MEM_TIMESTAMP // scratch memptr validate #endif // GSL_RB_USE_MEM_TIMESTAMP #ifdef GSL_RB_USE_MEM_RPTR // rptr memptr validate #endif // GSL_RB_USE_MEM_RPTR #ifdef GSL_RB_USE_WPTR_POLLING // wptr memptr validate #endif // GSL_RB_USE_WPTR_POLLING kgsl_log_write( KGSL_LOG_GROUP_COMMAND | KGSL_LOG_LEVEL_TRACE, "<-- kgsl_ringbuffer_bist. Return value %d\n", GSL_SUCCESS ); return (GSL_SUCCESS); } #endif