/* HwMpeg.c * * Copyright 2004 BEAM Ltd. * 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 * BEAM LTD, TUNGSTEN GRAPHICS 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: * Terry Barnaby * * * Description * This code provides access to a Hardware MPEG controller. */ #include #include #include #include #include #include #include #include "HwMpeg.h" #include /* Debug bits */ #define DEBUG 1 #if DEBUG #define dprintf(fmt, a...) printf("HwMpeg %s: " fmt, __FUNCTION__, ##a); #else #define dprintf(fmt, a...) #endif #define VIA_REG_BASE 0xdc000000 /* Via Mpeg registers base offset */ #define VIA_REG_SIZE 0x9000 /* Size of Via Mpeg registers */ #define VIA_SLICEBUSYMASK 0x00000200 #define VIA_BUSYMASK 0x00000207 #define VIA_SLICEIDLEVAL 0x00000200 #define VIA_IDLEVAL 0x00000204 /* HwMpeg* hardware controller data structure */ struct _HwMpeg { int drmFd; int performLocking; void* mmioAddress; HwMpegQMatrix qmatrix; HwMpegConfig config; int intraLoaded; int nonIntraLoaded; int yStride; int frameSize; void* frames[3]; }; #define MPEGIN(state,reg) \ *((volatile uint32_t*)(((uint8_t*)(state)->mmioAddress) + 0xc00 + (reg))) #define MPEGOUT(state,reg,val) \ *((volatile uint32_t*)(((uint8_t*)(state)->mmioAddress) + 0xc00 + (reg))) \ = (val); static void viaMpegReset(HwMpeg* hwMpeg){ int i,j; for(i = 0; i < 14; i++) MPEGOUT(hwMpeg, 0x08, 0); MPEGOUT(hwMpeg, 0x98, 0x400000); for(i = 0; i < 6; i++){ MPEGOUT(hwMpeg, 0x0c, 0x43 | 0x20); for(j = 0x10; j < 0x20; j += 4) MPEGOUT(hwMpeg, j, 0); } MPEGOUT(hwMpeg, 0x0c, 0xc3 | 0x20); for(j = 0x10; j < 0x20; j += 4) MPEGOUT(hwMpeg, j, 0); } static void viaMpegSetFB(HwMpeg* mpeg, unsigned i, uint32_t address){ uint32_t yOffs = 0xFFFFFFFF; uint32_t vOffs = 0xFFFFFFFF; uint32_t uOffs = 0xFFFFFFFF; if(address){ yOffs = address; vOffs = yOffs + (mpeg->config.height * mpeg->yStride); uOffs = vOffs + ((mpeg->config.height >> 1) * (mpeg->yStride >> 1)); } i *= 12; /* printf("BEAMFB: %d: %x %x %x\n", i / 12, yOffs, uOffs, vOffs); */ MPEGOUT(mpeg, 0x20 + i, yOffs >> 3); MPEGOUT(mpeg, 0x24 + i, uOffs >> 3); MPEGOUT(mpeg, 0x28 + i, vOffs >> 3); } static void viaMpegBeginPicture(HwMpeg* mpeg){ uint32_t j, mb_width, mb_height; mb_width = (mpeg->config.width + 15) >> 4; if((mpeg->config.mpeg_coding == HWMPEG_MCODE_MPEG_2) && (mpeg->config.flags & HWMPEG_FLAG_PROGRESSIVE_SEQUENCE)){ mb_height = 2 * ((mpeg->config.height + 31) >> 5); } else { mb_height =(((mpeg->config.height + 15) >> 4) & ~1); } MPEGOUT(mpeg, 0x00, ((mpeg->config.picture_structure & HWMPEG_PSTRUCT_FRAME_PICTURE) << 2) | ((mpeg->config.picture_coding_type & 3) << 4) | ((mpeg->config.flags & HWMPEG_FLAG_ALTERNATE_SCAN) ? (1 << 6) : 0)); if (!(mpeg->intraLoaded)) { MPEGOUT(mpeg, 0x5c, 0); for (j = 0; j < 64; j += 4) { MPEGOUT(mpeg, 0x60, mpeg->qmatrix.intra_quantiser_matrix[j] | (mpeg->qmatrix.intra_quantiser_matrix[j+1] << 8) | (mpeg->qmatrix.intra_quantiser_matrix[j+2] << 16) | (mpeg->qmatrix.intra_quantiser_matrix[j+3] << 24)); } mpeg->intraLoaded = 1; } if (!(mpeg->nonIntraLoaded)) { MPEGOUT(mpeg, 0x5c, 1); for(j = 0; j < 64; j += 4) { MPEGOUT(mpeg, 0x60, mpeg->qmatrix.non_intra_quantiser_matrix[j] | (mpeg->qmatrix.non_intra_quantiser_matrix[j+1] << 8) | (mpeg->qmatrix.non_intra_quantiser_matrix[j+2] << 16) | (mpeg->qmatrix.non_intra_quantiser_matrix[j+3] << 24)); } mpeg->nonIntraLoaded = 1; } MPEGOUT(mpeg, 0x90, ((mb_width * mb_height) & 0x3fff) | ((mpeg->config.flags & HWMPEG_FLAG_PRED_DCT_FRAME) ? ( 1 << 14) : 0) | ((mpeg->config.flags & HWMPEG_FLAG_TOP_FIELD_FIRST) ? (1 << 15) : 0 ) | ((mpeg->config.mpeg_coding == HWMPEG_MCODE_MPEG_2) ? (1 << 16) : 0) | ((mb_width & 0xff) << 18)); MPEGOUT(mpeg, 0x94, ((mpeg->config.flags & HWMPEG_FLAG_CONCEALMENT_MOTION_VECTORS) ? 1 : 0) | ((mpeg->config.flags & HWMPEG_FLAG_Q_SCALE_TYPE) ? 2 : 0) | ((mpeg->config.intra_dc_precision & 3) << 2) | (((1 + 0x100000 / mb_width) & 0xfffff) << 4) | ((mpeg->config.flags & HWMPEG_FLAG_INTRA_VLC_FORMAT) ? (1 << 24) : 0)); MPEGOUT(mpeg, 0x98, (((mpeg->config.FHMV_range) & 0xf) << 0) | (((mpeg->config.FVMV_range) & 0xf) << 4) | (((mpeg->config.BHMV_range) & 0xf) << 8) | (((mpeg->config.BVMV_range) & 0xf) << 12) | ((mpeg->config.flags & HWMPEG_FLAG_SECOND_FIELD) ? (1 << 20) : 0) | (0x0a6 << 16)); } typedef enum DecoderWaitMode { HwMpegWaitFrame, HwMpegWaitSlice } DecoderWaitMode; static uint32_t viaMpegGetStatus(HwMpeg* hwMpeg){ return MPEGIN(hwMpeg, 0x54); } static int viaMpegIsBusy(HwMpeg* hwMpeg, uint32_t mask, uint32_t idle) { uint32_t tmp = viaMpegGetStatus(hwMpeg); /* Error detected. */ if(tmp & 0x70) return 0; return (tmp & mask) != idle; } static int viaMpegSliceBusy(HwMpeg* hwMpeg){ return (viaMpegGetStatus(hwMpeg) & (1 << 9)); } #include #include #include #include #include static int rtcFD; static void closeRTC(){ close(rtcFD); rtcFD = -1; } static void openRTC(){ rtcFD = open("/dev/rtc",O_RDONLY); if(rtcFD >= 0){ unsigned long tmp; tmp = 8192; if(-1 == (ioctl(rtcFD, RTC_IRQP_SET, (void *)tmp))){ tmp = 4096; if (-1 == (ioctl(rtcFD, RTC_IRQP_SET, (void *)tmp))) { closeRTC(); return; } } if(-1 == (ioctl(rtcFD, RTC_PIE_ON, 0))) { closeRTC(); return; } fprintf(stderr,"HwMpeg: RTC driver running at %ld Hz.\n",tmp); } else { fprintf(stderr,"HwMpeg: RTC error. %s\n", strerror(errno)); rtcFD = -1; } } /* * Sleep until the RTC generates an interrupt. */ static void sleepRTC(){ unsigned long buf; if(rtcFD >= 0) { read(rtcFD, &buf, 4); } } static uint32_t timeDiff(struct timeval* now, struct timeval* then){ return (now->tv_usec >= then->tv_usec) ? now->tv_usec - then->tv_usec : 1000000 - (then->tv_usec - now->tv_usec); } #define VIA_XVMC_DECODERTIMEOUT 40000 /*(microseconds)*/ static int decoderWait(HwMpeg* mpeg, DecoderWaitMode mode){ struct timespec sleep; struct timeval now,then; struct timezone here; uint32_t ret, busyMask, idleVal; sleep.tv_nsec = 1; sleep.tv_sec = 0; here.tz_minuteswest = 0; here.tz_dsttime = 0; gettimeofday(&then,&here); switch (mode) { case HwMpegWaitSlice: busyMask = VIA_SLICEBUSYMASK; idleVal = VIA_SLICEIDLEVAL; break; case HwMpegWaitFrame: default: busyMask = VIA_BUSYMASK; idleVal = VIA_IDLEVAL; break; } while(viaMpegIsBusy(mpeg, busyMask, idleVal)){ gettimeofday(&now, &here); if(timeDiff(&now, &then) > VIA_XVMC_DECODERTIMEOUT){ if(viaMpegIsBusy(mpeg, busyMask, idleVal)){ /* ctx->decTimeOut = 1; */ fprintf(stderr,"ViaXvMC: Decoder timed out.\n"); } break; } #ifdef ZAP HW_UNLOCK(ctx); viaSleepRTC(ctx); HW_LOCK(ctx); #else #ifdef ZAP sleep.tv_sec = 0; sleep.tv_nsec = 1; nanosleep(&sleep, 0); #else sleepRTC(); #endif #endif } ret = viaMpegGetStatus(mpeg); return ret; } static void viaLoadDefaultQMatrices(HwMpeg* mpeg) { int i; static const char intra[64] = { 8, 16, 19, 22, 26, 27, 29, 34, 16, 16, 22, 24, 27, 29, 34, 37, 19, 22, 26, 27, 29, 34, 34, 38, 22, 22, 26, 27, 29, 34, 37, 40, 22, 26, 27, 29, 32, 35, 40, 48, 26, 27, 29, 32, 35, 40, 48, 58, 26, 27, 29, 34, 38, 46, 56, 69, 27, 29, 35, 38, 46, 56, 69, 83 }; for(i = 0; i < 64; ++i){ mpeg->qmatrix.intra_quantiser_matrix[i] = intra[i]; mpeg->qmatrix.non_intra_quantiser_matrix[i] = 16; } mpeg->intraLoaded = 0; mpeg->nonIntraLoaded = 0; } /* MpegInit: Initialises MPEG Decoder and returns pointer to allocated Mpeg data structure */ /* This would open the DRM driver and allocate some resources */ int hwMpegInit(HwMpeg** hwMpeg, int drmFd, int performLocking){ HwMpeg* mpeg; dprintf("Initialise\n"); openRTC(); mpeg = (HwMpeg*)malloc(sizeof(HwMpeg)); memset(mpeg, 0, sizeof(HwMpeg)); mpeg->drmFd = drmFd; mpeg->performLocking = performLocking; printf("Regs: %x %x\n", VIA_REG_BASE, VIA_REG_SIZE); /* Setup HW Mpeg register address */ if(drmMap(mpeg->drmFd, VIA_REG_BASE, VIA_REG_SIZE, &(mpeg->mmioAddress)) < 0){ fprintf(stderr, "Unable to map the display chip mmio registers.\n"); free(mpeg); return HWMPEG_ERROR_UNSPECIFIED; } viaLoadDefaultQMatrices(mpeg); *hwMpeg = mpeg; return HWMPEG_ERROR_OK; } /* HwMpegLoadQMatrix: Loads Q Matrix */ int hwMpegLoadQMatrix(HwMpeg* mpeg, HwMpegQMatrix* qmatrix){ memcpy(&(mpeg->qmatrix), qmatrix, sizeof(mpeg->qmatrix)); dprintf("Load\n"); mpeg->intraLoaded = 0; mpeg->nonIntraLoaded = 0; return HWMPEG_ERROR_OK; } /* HwMpegBegin: Configures MPEG controller for next set of pictures */ int hwMpegFieldStart(HwMpeg* mpeg, HwMpegConfig* config){ /* Should check params */ /* Setup params */ memcpy(&(mpeg->config), config, sizeof(mpeg->config)); mpeg->yStride = (mpeg->config.width + 31) & ~31; viaMpegReset(mpeg); /* decoderWaitLocked(pViaXvMC,1); */ /* Setup YStride */ MPEGOUT(mpeg, 0x50, (mpeg->yStride >> 3) | ((mpeg->yStride >> 4) << 16)); /* Set up frame buffers */ viaMpegSetFB(mpeg, 0, mpeg->config.targetFrame.data); viaMpegSetFB(mpeg, 1, mpeg->config.pastFrame.data); viaMpegSetFB(mpeg, 2, mpeg->config.futureFrame.data); /* Start the picture */ viaMpegBeginPicture(mpeg); return HWMPEG_ERROR_OK; } /* HwMpegWriteSlice: Writes a slice, buffering in a FIFO as necessary and waiting for hardware */ int hwMpegWriteSlice(HwMpeg* mpeg, void* data, uint32_t nBytes, uint32_t sCode){ int i, n, r; uint32_t* buf; if(decoderWait(mpeg, HwMpegWaitSlice) & 0x70){ return HWMPEG_ERROR_UNSPECIFIED; } n = nBytes >> 2; if(sCode) nBytes += 4; r = nBytes & 3; buf = (uint32_t*)data; if(r) nBytes += 4 - r; nBytes += 8; MPEGOUT(mpeg, 0x9c, nBytes); if(sCode) MPEGOUT(mpeg, 0xa0, sCode); for(i = 0; i < n; i++){ MPEGOUT(mpeg, 0xa0, *buf++); } if(r){ MPEGOUT(mpeg, 0xa0, *buf & ((1 << (r << 3)) - 1)); } MPEGOUT(mpeg, 0xa0, 0); MPEGOUT(mpeg, 0xa0, 0); return HWMPEG_ERROR_OK; } /* HwMpegFieldEnd: End of slices for this field */ int hwMpegFieldEnd(HwMpeg* mpeg){ return HWMPEG_ERROR_OK; } /* HwMpegWaitComplete: Waits till MPEG controller has completed all outstanding work */ int hwMpegWaitComplete(HwMpeg* mpeg){ if(decoderWait(mpeg, HwMpegWaitFrame) & 0x70) return HWMPEG_ERROR_UNSPECIFIED; else return HWMPEG_ERROR_OK; } /* Closes the MPEG decoder */ int hwMpegClose(HwMpeg* mpeg){ free(mpeg); return HWMPEG_ERROR_OK; }