summaryrefslogtreecommitdiff
path: root/contrib/vidix/drivers/pm3_vid.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/vidix/drivers/pm3_vid.c')
-rw-r--r--contrib/vidix/drivers/pm3_vid.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/contrib/vidix/drivers/pm3_vid.c b/contrib/vidix/drivers/pm3_vid.c
new file mode 100644
index 000000000..72a6523b3
--- /dev/null
+++ b/contrib/vidix/drivers/pm3_vid.c
@@ -0,0 +1,573 @@
+/**
+ Driver for 3DLabs GLINT R3 and Permedia3 chips.
+
+ Copyright (C) 2002, 2003 Måns Rullgård
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+**/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "vidix.h"
+#include "fourcc.h"
+#include "libdha.h"
+#include "pci_ids.h"
+#include "pci_names.h"
+
+#include "pm3_regs.h"
+
+#define VIDIX_STATIC pm3_
+
+/* MBytes of video memory to use */
+#define PM3_VIDMEM 24
+
+#if 0
+#define TRACE_ENTER() fprintf(stderr, "%s: enter\n", __FUNCTION__)
+#define TRACE_EXIT() fprintf(stderr, "%s: exit\n", __FUNCTION__)
+#else
+#define TRACE_ENTER()
+#define TRACE_EXIT()
+#endif
+
+static pciinfo_t pci_info;
+
+void *pm3_reg_base;
+static void *pm3_mem;
+
+static int pm3_vidmem = PM3_VIDMEM;
+static int pm3_blank = 0;
+static int pm3_dma = 0;
+
+static int pm3_ckey_red, pm3_ckey_green, pm3_ckey_blue;
+
+static u_int page_size;
+
+static vidix_capability_t pm3_cap =
+{
+ "3DLabs GLINT R3/Permedia3 driver",
+ "Måns Rullgård <mru@users.sf.net>",
+ TYPE_OUTPUT,
+ { 0, 0, 0, 0 },
+ 2048,
+ 2048,
+ 4,
+ 4,
+ -1,
+ FLAG_UPSCALER | FLAG_DOWNSCALER,
+ VENDOR_3DLABS,
+ -1,
+ { 0, 0, 0, 0 }
+};
+
+
+unsigned int VIDIX_NAME(vixGetVersion)(void)
+{
+ return(VIDIX_VERSION);
+}
+
+static unsigned short pm3_card_ids[] =
+{
+ DEVICE_3DLABS_GLINT_R3
+};
+
+static int find_chip(unsigned chip_id)
+{
+ unsigned i;
+ for(i = 0;i < sizeof(pm3_card_ids)/sizeof(unsigned short);i++)
+ {
+ if(chip_id == pm3_card_ids[i]) return i;
+ }
+ return -1;
+}
+
+int VIDIX_NAME(vixProbe)(int verbose, int force)
+{
+ pciinfo_t lst[MAX_PCI_DEVICES];
+ unsigned i,num_pci;
+ int err;
+
+ err = pci_scan(lst,&num_pci);
+ if(err)
+ {
+ printf("[pm3] Error occured during pci scan: %s\n",strerror(err));
+ return err;
+ }
+ else
+ {
+ err = ENXIO;
+ for(i=0; i < num_pci; i++)
+ {
+ if(lst[i].vendor == VENDOR_3DLABS)
+ {
+ int idx;
+ const char *dname;
+ idx = find_chip(lst[i].device);
+ if(idx == -1)
+ continue;
+ dname = pci_device_name(VENDOR_3DLABS, lst[i].device);
+ dname = dname ? dname : "Unknown chip";
+ printf("[pm3] Found chip: %s with IRQ %i\n",
+ dname, lst[i].irq);
+ pm3_cap.device_id = lst[i].device;
+ err = 0;
+ memcpy(&pci_info, &lst[i], sizeof(pciinfo_t));
+ break;
+ }
+ }
+ }
+ if(err && verbose) printf("[pm3] Can't find chip\n");
+ return err;
+}
+
+#define PRINT_REG(reg) \
+{ \
+ long _foo = READ_REG(reg); \
+ printf("[pm3] " #reg " (%x) = %#lx (%li)\n", reg, _foo, _foo); \
+}
+
+int VIDIX_NAME(vixInit)(const char *args)
+{
+ if(args != NULL){
+ char *ac = strdup(args), *s, *opt;
+
+ opt = strtok_r(ac, ",", &s);
+ while(opt){
+ char *a = strchr(opt, '=');
+
+ if(a)
+ *a++ = 0;
+ if(!strcmp(opt, "mem")){
+ if(a)
+ pm3_vidmem = strtol(a, NULL, 0);
+ } else if(!strcmp(opt, "blank")){
+ pm3_blank = a? strtol(a, NULL, 0): 1;
+ }
+
+ opt = strtok_r(NULL, ",", &s);
+ }
+
+ free(ac);
+ }
+
+ pm3_reg_base = map_phys_mem(pci_info.base0, 0x20000);
+ pm3_mem = map_phys_mem(pci_info.base1, 0x2000000);
+
+ if(bm_open() == 0){
+ fprintf(stderr, "[pm3] DMA available.\n");
+ pm3_cap.flags |= FLAG_DMA | FLAG_SYNC_DMA;
+ page_size = sysconf(_SC_PAGESIZE);
+ hwirq_install(pci_info.bus, pci_info.card, pci_info.func,
+ 0, PM3IntFlags, -1);
+ WRITE_REG(PM3IntEnable, (1 << 7));
+ pm3_dma = 1;
+ }
+
+ RAMDAC_GET_REG(PM3RD_VideoOverlayKeyR, pm3_ckey_red);
+ RAMDAC_GET_REG(PM3RD_VideoOverlayKeyG, pm3_ckey_green);
+ RAMDAC_GET_REG(PM3RD_VideoOverlayKeyB, pm3_ckey_blue);
+
+ return 0;
+}
+
+void VIDIX_NAME(vixDestroy)(void)
+{
+ if(pm3_dma)
+ WRITE_REG(PM3IntEnable, 0);
+
+ RAMDAC_SET_REG(PM3RD_VideoOverlayKeyR, pm3_ckey_red);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayKeyG, pm3_ckey_green);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayKeyB, pm3_ckey_blue);
+
+ unmap_phys_mem(pm3_reg_base, 0x20000);
+ unmap_phys_mem(pm3_mem, 0x2000000);
+ hwirq_uninstall(pci_info.bus, pci_info.card, pci_info.func);
+ bm_close();
+}
+
+int VIDIX_NAME(vixGetCapability)(vidix_capability_t *to)
+{
+ memcpy(to, &pm3_cap, sizeof(vidix_capability_t));
+ return 0;
+}
+
+static int is_supported_fourcc(uint32_t fourcc)
+{
+ switch(fourcc){
+ case IMGFMT_YUY2:
+ case IMGFMT_UYVY:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int VIDIX_NAME(vixQueryFourcc)(vidix_fourcc_t *to)
+{
+ if(is_supported_fourcc(to->fourcc))
+ {
+ to->depth = VID_DEPTH_1BPP | VID_DEPTH_2BPP |
+ VID_DEPTH_4BPP | VID_DEPTH_8BPP |
+ VID_DEPTH_12BPP| VID_DEPTH_15BPP|
+ VID_DEPTH_16BPP| VID_DEPTH_24BPP|
+ VID_DEPTH_32BPP;
+ to->flags = VID_CAP_EXPAND | VID_CAP_SHRINK | VID_CAP_COLORKEY;
+ return 0;
+ }
+ else to->depth = to->flags = 0;
+ return ENOSYS;
+}
+
+static int frames[VID_PLAY_MAXFRAMES], vid_base;
+static int overlay_mode, overlay_control, video_control, int_enable;
+static int rdoverlay_mode;
+static int src_w, drw_w;
+static int src_h, drw_h;
+static int drw_x, drw_y;
+
+#define FORMAT_RGB8888 PM3VideoOverlayMode_COLORFORMAT_RGB8888
+#define FORMAT_RGB4444 PM3VideoOverlayMode_COLORFORMAT_RGB4444
+#define FORMAT_RGB5551 PM3VideoOverlayMode_COLORFORMAT_RGB5551
+#define FORMAT_RGB565 PM3VideoOverlayMode_COLORFORMAT_RGB565
+#define FORMAT_RGB332 PM3VideoOverlayMode_COLORFORMAT_RGB332
+#define FORMAT_BGR8888 PM3VideoOverlayMode_COLORFORMAT_BGR8888
+#define FORMAT_BGR4444 PM3VideoOverlayMode_COLORFORMAT_BGR4444
+#define FORMAT_BGR5551 PM3VideoOverlayMode_COLORFORMAT_BGR5551
+#define FORMAT_BGR565 PM3VideoOverlayMode_COLORFORMAT_BGR565
+#define FORMAT_BGR332 PM3VideoOverlayMode_COLORFORMAT_BGR332
+#define FORMAT_CI8 PM3VideoOverlayMode_COLORFORMAT_CI8
+#define FORMAT_VUY444 PM3VideoOverlayMode_COLORFORMAT_VUY444
+#define FORMAT_YUV444 PM3VideoOverlayMode_COLORFORMAT_YUV444
+#define FORMAT_VUY422 PM3VideoOverlayMode_COLORFORMAT_VUY422
+#define FORMAT_YUV422 PM3VideoOverlayMode_COLORFORMAT_YUV422
+
+/* Notice, have to check that we dont overflow the deltas here ... */
+static void
+compute_scale_factor(int* src_w, int* dst_w,
+ u_int* shrink_delta, u_int* zoom_delta)
+{
+ /* NOTE: If we don't return reasonable values here then the video
+ * unit can potential shut off and won't display an image until re-enabled.
+ * Seems as though the zoom_delta is o.k, and I've not had the problem.
+ * The 'shrink_delta' is prone to this the most - FIXME ! */
+
+ if (*src_w >= *dst_w) {
+ *src_w &= ~0x3;
+ *dst_w &= ~0x3;
+ *shrink_delta = (((*src_w << 16) / *dst_w) + 0x0f) & 0x0ffffff0;
+ *zoom_delta = 1<<16;
+ if ( ((*shrink_delta * *dst_w) >> 16) & 0x03 )
+ *shrink_delta += 0x10;
+ } else {
+ *src_w &= ~0x3;
+ *dst_w &= ~0x3;
+ *zoom_delta = (((*src_w << 16) / *dst_w) + 0x0f) & 0x0001fff0;
+ *shrink_delta = 1<<16;
+ if ( ((*zoom_delta * *dst_w) >> 16) & 0x03 )
+ *zoom_delta += 0x10;
+ }
+}
+
+static void
+pm3_setup_overlay(vidix_playback_t *info)
+{
+ u_int shrink, zoom;
+ int format = 0;
+ int filter = 0;
+ int sw = src_w;
+
+ switch(info->fourcc){
+ case IMGFMT_YUY2:
+ format = FORMAT_YUV422;
+ break;
+ case IMGFMT_UYVY:
+ format = FORMAT_VUY422;
+ break;
+ }
+
+ compute_scale_factor(&sw, &drw_w, &shrink, &zoom);
+
+ WAIT_FIFO(9);
+ WRITE_REG(PM3VideoOverlayBase0, vid_base >> 1);
+ WRITE_REG(PM3VideoOverlayStride, PM3VideoOverlayStride_STRIDE(src_w));
+ WRITE_REG(PM3VideoOverlayWidth, PM3VideoOverlayWidth_WIDTH(sw));
+ WRITE_REG(PM3VideoOverlayHeight, PM3VideoOverlayHeight_HEIGHT(src_h));
+ WRITE_REG(PM3VideoOverlayOrigin, 0);
+
+ /* Scale the source to the destinationsize */
+ if (src_w == drw_w) {
+ WRITE_REG(PM3VideoOverlayShrinkXDelta, 1<<16);
+ WRITE_REG(PM3VideoOverlayZoomXDelta, 1<<16);
+ } else {
+ WRITE_REG(PM3VideoOverlayShrinkXDelta, shrink);
+ WRITE_REG(PM3VideoOverlayZoomXDelta, zoom);
+ filter = PM3VideoOverlayMode_FILTER_PARTIAL;
+ }
+ if (src_h == drw_h) {
+ WRITE_REG(PM3VideoOverlayYDelta, PM3VideoOverlayYDelta_NONE);
+ } else {
+ WRITE_REG(PM3VideoOverlayYDelta,
+ PM3VideoOverlayYDelta_DELTA(src_h, drw_h));
+ filter = PM3VideoOverlayMode_FILTER_FULL;
+ }
+
+ WRITE_REG(PM3VideoOverlayIndex, 0);
+
+ /* Now set the ramdac video overlay region and mode */
+ RAMDAC_SET_REG(PM3RD_VideoOverlayXStartLow, (drw_x & 0xff));
+ RAMDAC_SET_REG(PM3RD_VideoOverlayXStartHigh, (drw_x & 0xf00)>>8);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayXEndLow, (drw_x+drw_w) & 0xff);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayXEndHigh,
+ ((drw_x+drw_w) & 0xf00)>>8);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayYStartLow, (drw_y & 0xff));
+ RAMDAC_SET_REG(PM3RD_VideoOverlayYStartHigh, (drw_y & 0xf00)>>8);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayYEndLow, (drw_y+drw_h) & 0xff);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayYEndHigh,
+ ((drw_y+drw_h) & 0xf00)>>8);
+
+ overlay_mode =
+ 1 << 5 |
+ format |
+ filter |
+ PM3VideoOverlayMode_BUFFERSYNC_MANUAL |
+ PM3VideoOverlayMode_FLIP_VIDEO;
+
+ overlay_control =
+ PM3RD_VideoOverlayControl_KEY_COLOR |
+ PM3RD_VideoOverlayControl_DIRECTCOLOR_ENABLED;
+}
+
+extern int
+VIDIX_NAME(vixSetGrKeys)(const vidix_grkey_t *key)
+{
+ if(key->ckey.op == CKEY_TRUE){
+ RAMDAC_SET_REG(PM3RD_VideoOverlayKeyR, key->ckey.red);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayKeyG, key->ckey.green);
+ RAMDAC_SET_REG(PM3RD_VideoOverlayKeyB, key->ckey.blue);
+ rdoverlay_mode = PM3RD_VideoOverlayControl_MODE_MAINKEY;
+ } else {
+ rdoverlay_mode = PM3RD_VideoOverlayControl_MODE_ALWAYS;
+ }
+ RAMDAC_SET_REG(PM3RD_VideoOverlayControl,
+ overlay_control | rdoverlay_mode);
+
+ return 0;
+}
+
+extern int
+VIDIX_NAME(vixGetGrKeys)(vidix_grkey_t *key)
+{
+ RAMDAC_GET_REG(PM3RD_VideoOverlayKeyR, key->ckey.red);
+ RAMDAC_GET_REG(PM3RD_VideoOverlayKeyG, key->ckey.green);
+ RAMDAC_GET_REG(PM3RD_VideoOverlayKeyB, key->ckey.blue);
+ return 0;
+}
+
+extern int
+VIDIX_NAME(vixConfigPlayback)(vidix_playback_t *info)
+{
+ unsigned int i;
+ u_int frame_size;
+ u_int vidmem_size;
+ u_int max_frames;
+
+ TRACE_ENTER();
+
+ src_w = info->src.w;
+ src_h = info->src.h;
+ drw_w = info->dest.w;
+ drw_h = info->dest.h;
+ drw_x = info->dest.x;
+ drw_y = info->dest.y;
+
+ frame_size = src_w * src_h * 2;
+ vidmem_size = pm3_vidmem*1024*1024;
+ max_frames = vidmem_size / frame_size;
+ if(max_frames > VID_PLAY_MAXFRAMES)
+ max_frames = VID_PLAY_MAXFRAMES;
+
+ src_h--; /* ugh */
+
+ if(info->num_frames > max_frames)
+ info->num_frames = max_frames;
+ vidmem_size = info->num_frames * frame_size;
+
+ /* Use end of video memory. Assume the card has 32 MB */
+ vid_base = 32*1024*1024 - vidmem_size;
+ info->dga_addr = pm3_mem + vid_base;
+
+ info->dest.pitch.y = 2;
+ info->dest.pitch.u = 0;
+ info->dest.pitch.v = 0;
+ info->offset.y = 0;
+ info->offset.v = 0;
+ info->offset.u = 0;
+ info->frame_size = frame_size;
+
+ for(i = 0; i < info->num_frames; i++){
+ info->offsets[i] = frame_size * i;
+ frames[i] = (vid_base + info->offsets[i]) >> 1;
+ }
+
+ pm3_setup_overlay(info);
+
+ video_control = READ_REG(PM3VideoControl);
+ int_enable = READ_REG(PM3IntEnable);
+
+ TRACE_EXIT();
+ return 0;
+}
+
+int VIDIX_NAME(vixPlaybackOn)(void)
+{
+ TRACE_ENTER();
+
+ WRITE_REG(PM3VideoOverlayMode,
+ overlay_mode | PM3VideoOverlayMode_ENABLE);
+ overlay_control |= PM3RD_VideoOverlayControl_ENABLE;
+ RAMDAC_SET_REG(PM3RD_VideoOverlayControl,
+ overlay_control | rdoverlay_mode);
+ WRITE_REG(PM3VideoOverlayUpdate, PM3VideoOverlayUpdate_ENABLE);
+
+ if(pm3_blank)
+ WRITE_REG(PM3VideoControl,
+ video_control | PM3VideoControl_DISPLAY_ENABLE);
+
+ TRACE_EXIT();
+ return 0;
+}
+
+int VIDIX_NAME(vixPlaybackOff)(void)
+{
+ overlay_control &= ~PM3RD_VideoOverlayControl_ENABLE;
+ RAMDAC_SET_REG(PM3RD_VideoOverlayControl,
+ PM3RD_VideoOverlayControl_DISABLE);
+ WRITE_REG(PM3VideoOverlayMode,
+ PM3VideoOverlayMode_DISABLE);
+
+ if(video_control)
+ WRITE_REG(PM3VideoControl,
+ video_control & ~PM3VideoControl_DISPLAY_ENABLE);
+
+ return 0;
+}
+
+int VIDIX_NAME(vixPlaybackFrameSelect)(unsigned int frame)
+{
+ WRITE_REG(PM3VideoOverlayBase0, frames[frame]);
+
+ return 0;
+}
+
+struct pm3_bydma_cmd {
+ uint32_t bus_addr;
+ uint32_t fb_addr;
+ uint32_t mask;
+ uint32_t count;
+};
+
+struct pm3_bydma_frame {
+ struct pm3_bydma_cmd *cmds;
+ u_long bus_addr;
+ uint32_t count;
+};
+
+static struct pm3_bydma_frame *
+pm3_setup_bydma(vidix_dma_t *dma, struct pm3_bydma_frame *bdf)
+{
+ u_int size = dma->size;
+ u_int pages = (size + page_size-1) / page_size;
+ unsigned long baddr[pages];
+ u_int i;
+ uint32_t dest;
+
+ if(bm_virt_to_bus(dma->src, dma->size, baddr))
+ return NULL;
+
+ if(!bdf){
+ bdf = malloc(sizeof(*bdf));
+ bdf->cmds = valloc(pages * sizeof(struct pm3_bydma_cmd));
+ if(dma->flags & BM_DMA_FIXED_BUFFS){
+ mlock(bdf->cmds, page_size);
+ }
+ }
+
+ dest = vid_base + dma->dest_offset;
+ for(i = 0; i < pages; i++, dest += page_size, size -= page_size){
+ bdf->cmds[i].bus_addr = baddr[i];
+ bdf->cmds[i].fb_addr = dest;
+ bdf->cmds[i].mask = ~0;
+ bdf->cmds[i].count = ((size > page_size)? page_size: size) / 16;
+ }
+
+ bdf->count = pages;
+
+ if(bm_virt_to_bus(bdf->cmds, page_size, &bdf->bus_addr) != 0){
+ free(bdf->cmds);
+ free(bdf);
+ return NULL;
+ }
+
+ return bdf;
+}
+
+extern int
+VIDIX_NAME(vixPlaybackCopyFrame)(vidix_dma_t *dma)
+{
+ u_int frame = dma->idx;
+ struct pm3_bydma_frame *bdf;
+
+ bdf = dma->internal[frame];
+ if(!bdf || !(dma->flags & BM_DMA_FIXED_BUFFS))
+ bdf = pm3_setup_bydma(dma, bdf);
+ if(!bdf)
+ return -1;
+
+ if(!dma->internal[frame])
+ dma->internal[frame] = bdf;
+
+ if(dma->flags & BM_DMA_SYNC){
+ hwirq_wait(pci_info.irq);
+ }
+
+ WAIT_FIFO(3);
+ WRITE_REG(PM3ByDMAReadCommandBase, bdf->bus_addr);
+ WRITE_REG(PM3ByDMAReadCommandCount, bdf->count);
+ WRITE_REG(PM3ByDMAReadMode,
+ PM3ByDMAReadMode_ByteSwap_NONE |
+ PM3ByDMAReadMode_Format_RAW |
+ PM3ByDMAReadMode_PixelSize(16) |
+ PM3ByDMAReadMode_Active |
+ PM3ByDMAReadMode_Burst(7) |
+ PM3ByDMAReadMode_Align);
+
+ if(dma->flags & BM_DMA_BLOCK){
+ hwirq_wait(pci_info.irq);
+ }
+
+ return 0;
+}
+
+extern int
+VIDIX_NAME(vixQueryDMAStatus)(void)
+{
+ uint32_t bdm = READ_REG(PM3ByDMAReadMode);
+ return (bdm & PM3ByDMAReadMode_Active)? 1: 0;
+}