mirror of https://github.com/mpv-player/mpv
385 lines
10 KiB
C
385 lines
10 KiB
C
/**
|
|
Driver for 3DLabs GLINT R3 and Permedia3 chips.
|
|
|
|
Copyright (C) 2002 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
**/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <unistd.h>
|
|
|
|
#include "vidix.h"
|
|
#include "vidixlib.h"
|
|
#include "fourcc.h"
|
|
#include "../libdha/libdha.h"
|
|
#include "../libdha/pci_ids.h"
|
|
#include "../libdha/pci_names.h"
|
|
#include "../config.h"
|
|
|
|
#include "pm3_regs.h"
|
|
|
|
#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
|
|
|
|
pciinfo_t pci_info;
|
|
|
|
void *pm3_reg_base;
|
|
void *pm3_mem;
|
|
|
|
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 }
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
static int pm3_probe(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 occurred 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\n", dname);
|
|
if ((lst[i].command & PCI_COMMAND_IO) == 0)
|
|
{
|
|
printf("[pm3] Device is disabled, ignoring\n");
|
|
continue;
|
|
}
|
|
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); \
|
|
}
|
|
|
|
static int pm3_init(void)
|
|
{
|
|
pm3_reg_base = map_phys_mem(pci_info.base0, 0x20000);
|
|
pm3_mem = map_phys_mem(pci_info.base2, 0x2000000);
|
|
return 0;
|
|
}
|
|
|
|
static void pm3_destroy(void)
|
|
{
|
|
unmap_phys_mem(pm3_reg_base, 0x20000);
|
|
unmap_phys_mem(pm3_mem, 0x2000000);
|
|
}
|
|
|
|
static int pm3_get_caps(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;
|
|
}
|
|
}
|
|
|
|
static int pm3_query_fourcc(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;
|
|
}
|
|
|
|
#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 don't overflow the deltas here ... */
|
|
static void
|
|
compute_scale_factor(
|
|
short* src_w, short* dst_w,
|
|
unsigned int* shrink_delta, unsigned 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 int frames[VID_PLAY_MAXFRAMES];
|
|
|
|
static long overlay_mode, overlay_control;
|
|
|
|
static int pm3_config_playback(vidix_playback_t *info)
|
|
{
|
|
int shrink, zoom;
|
|
short src_w, drw_w;
|
|
short src_h, drw_h;
|
|
long base0;
|
|
int pitch;
|
|
int format;
|
|
unsigned int i;
|
|
|
|
TRACE_ENTER();
|
|
|
|
if(!is_supported_fourcc(info->fourcc))
|
|
return -1;
|
|
|
|
switch(info->fourcc){
|
|
case IMGFMT_YUY2:
|
|
format = FORMAT_YUV422;
|
|
break;
|
|
case IMGFMT_UYVY:
|
|
format = FORMAT_VUY422;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
src_w = info->src.w;
|
|
src_h = info->src.h;
|
|
|
|
drw_w = info->dest.w;
|
|
drw_h = info->dest.h;
|
|
|
|
pitch = src_w;
|
|
|
|
/* Assume we have 16 MB to play with */
|
|
info->num_frames = 0x1000000 / (pitch * src_h * 2);
|
|
if(info->num_frames > VID_PLAY_MAXFRAMES)
|
|
info->num_frames = VID_PLAY_MAXFRAMES;
|
|
|
|
/* Start at 16 MB. Let's hope it's not in use. */
|
|
base0 = 0x1000000;
|
|
info->dga_addr = pm3_mem + base0;
|
|
|
|
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 = pitch * src_h * 2;
|
|
for(i = 0; i < info->num_frames; i++){
|
|
info->offsets[i] = info->frame_size * i;
|
|
frames[i] = (base0 + info->offsets[i]) >> 1;
|
|
}
|
|
|
|
compute_scale_factor(&src_w, &drw_w, &shrink, &zoom);
|
|
|
|
WRITE_REG(PM3VideoOverlayBase0, base0 >> 1);
|
|
WRITE_REG(PM3VideoOverlayStride, PM3VideoOverlayStride_STRIDE(pitch));
|
|
WRITE_REG(PM3VideoOverlayWidth, PM3VideoOverlayWidth_WIDTH(src_w));
|
|
WRITE_REG(PM3VideoOverlayHeight, PM3VideoOverlayHeight_HEIGHT(src_h));
|
|
WRITE_REG(PM3VideoOverlayOrigin, 0);
|
|
|
|
/* Scale the source to the destinationsize */
|
|
if (src_h == drw_h) {
|
|
WRITE_REG(PM3VideoOverlayYDelta, PM3VideoOverlayYDelta_NONE);
|
|
} else {
|
|
WRITE_REG(PM3VideoOverlayYDelta,
|
|
PM3VideoOverlayYDelta_DELTA(src_h, drw_h));
|
|
}
|
|
if (src_w == drw_w) {
|
|
WRITE_REG(PM3VideoOverlayShrinkXDelta, 1<<16);
|
|
WRITE_REG(PM3VideoOverlayZoomXDelta, 1<<16);
|
|
} else {
|
|
WRITE_REG(PM3VideoOverlayShrinkXDelta, shrink);
|
|
WRITE_REG(PM3VideoOverlayZoomXDelta, zoom);
|
|
}
|
|
WRITE_REG(PM3VideoOverlayIndex, 0);
|
|
|
|
/* Now set the ramdac video overlay region and mode */
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayXStartLow, (info->dest.x & 0xff));
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayXStartHigh, (info->dest.x & 0xf00)>>8);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayXEndLow, (info->dest.x+drw_w) & 0xff);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayXEndHigh,
|
|
((info->dest.x+drw_w) & 0xf00)>>8);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayYStartLow, (info->dest.y & 0xff));
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayYStartHigh, (info->dest.y & 0xf00)>>8);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayYEndLow, (info->dest.y+drw_h) & 0xff);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayYEndHigh,
|
|
((info->dest.y+drw_h) & 0xf00)>>8);
|
|
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayKeyR, 0xff);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayKeyG, 0x00);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayKeyB, 0xff);
|
|
|
|
overlay_mode =
|
|
1 << 5 |
|
|
format |
|
|
PM3VideoOverlayMode_FILTER_FULL |
|
|
PM3VideoOverlayMode_BUFFERSYNC_MANUAL |
|
|
PM3VideoOverlayMode_FLIP_VIDEO;
|
|
|
|
overlay_control =
|
|
PM3RD_VideoOverlayControl_KEY_COLOR |
|
|
PM3RD_VideoOverlayControl_MODE_MAINKEY |
|
|
PM3RD_VideoOverlayControl_DIRECTCOLOR_ENABLED;
|
|
|
|
TRACE_EXIT();
|
|
return 0;
|
|
}
|
|
|
|
static int pm3_playback_on(void)
|
|
{
|
|
TRACE_ENTER();
|
|
|
|
WRITE_REG(PM3VideoOverlayMode,
|
|
overlay_mode | PM3VideoOverlayMode_ENABLE);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayControl,
|
|
overlay_control | PM3RD_VideoOverlayControl_ENABLE);
|
|
WRITE_REG(PM3VideoOverlayUpdate,
|
|
PM3VideoOverlayUpdate_ENABLE);
|
|
|
|
TRACE_EXIT();
|
|
return 0;
|
|
}
|
|
|
|
static int pm3_playback_off(void)
|
|
{
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayControl,
|
|
PM3RD_VideoOverlayControl_DISABLE);
|
|
WRITE_REG(PM3VideoOverlayMode,
|
|
PM3VideoOverlayMode_DISABLE);
|
|
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayKeyR, 0x01);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayKeyG, 0x01);
|
|
RAMDAC_SET_REG(PM3RD_VideoOverlayKeyB, 0xfe);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pm3_frame_select(unsigned int frame)
|
|
{
|
|
WRITE_REG(PM3VideoOverlayBase0, frames[frame]);
|
|
return 0;
|
|
}
|
|
|
|
VDXDriver pm3_drv = {
|
|
"pm3",
|
|
NULL,
|
|
.probe = pm3_probe,
|
|
.get_caps = pm3_get_caps,
|
|
.query_fourcc = pm3_query_fourcc,
|
|
.init = pm3_init,
|
|
.destroy = pm3_destroy,
|
|
.config_playback = pm3_config_playback,
|
|
.playback_on = pm3_playback_on,
|
|
.playback_off = pm3_playback_off,
|
|
.frame_sel = pm3_frame_select,
|
|
};
|