1 /*
2  * VIDIX driver for 3DLabs Permedia 2 chipsets.
3  * Copyright (C) 2002 Måns Rullgård
4  *
5  * This file is part of MPlayer.
6  *
7  * MPlayer is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * MPlayer is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <inttypes.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
30 #include "config.h"
31 #include "vidix.h"
32 #include "fourcc.h"
33 #include "dha.h"
34 #include "pci_ids.h"
35 #include "pci_names.h"
36 #include "mp_msg.h"
37 
38 #include "glint_regs.h"
39 
40 /* MBytes of video memory to use */
41 #define PM2_VIDMEM 6
42 
43 #if 0
44 #define TRACE_ENTER() mp_msg(MSGT_VO, MSGL_DBG2, "[pm2] %s: enter\n", __FUNCTION__)
45 #define TRACE_EXIT() mp_msg(MSGT_VO, MSGL_DBG2, "[pm2] %s: exit\n", __FUNCTION__)
46 #else
47 #define TRACE_ENTER()
48 #define TRACE_EXIT()
49 #endif
50 
51 #define WRITE_REG(offset,val)						\
52     *(volatile unsigned long *)(((unsigned char *)(pm2_reg_base)) + offset) = (val)
53 #define READ_REG(offset)						    \
54     *(volatile unsigned long *)(((unsigned char *)(pm2_reg_base)) + offset)
55 
56 static pciinfo_t pci_info;
57 
58 static void *pm2_reg_base;
59 static void *pm2_mem;
60 
61 static int pm2_vidmem = PM2_VIDMEM;
62 
63 static const vidix_capability_t pm2_cap =
64 {
65     "3DLabs Permedia2 driver",
66     "Måns Rullgård <mru@users.sf.net>",
67     TYPE_OUTPUT,
68     { 0, 0, 0, 0 },
69     2048,
70     2048,
71     4,
72     4,
73     -1,
74     FLAG_UPSCALER|FLAG_DOWNSCALER,
75     VENDOR_3DLABS,
76     -1,
77     { 0, 0, 0, 0 }
78 };
79 
80 static unsigned int pm2_card_ids[] =
81 {
82     (VENDOR_3DLABS << 16) | DEVICE_3DLABS_PERMEDIA2,
83     (VENDOR_TEXAS << 16) | DEVICE_TEXAS_TVP4020_PERMEDIA_2
84 };
85 
find_chip(unsigned int vendor,uint32_t chip_id)86 static int find_chip(unsigned int vendor, uint32_t chip_id)
87 {
88     unsigned int vci = (vendor << 16) | chip_id;
89     unsigned i;
90     for(i = 0; i < sizeof(pm2_card_ids)/sizeof(unsigned int); i++){
91 	if(vci == pm2_card_ids[i]) return i;
92     }
93     return -1;
94 }
95 
pm2_probe(int verbose,int force)96 static int pm2_probe(int verbose, int force)
97 {
98     pciinfo_t lst[MAX_PCI_DEVICES];
99     unsigned i,num_pci;
100     int err;
101 
102     err = pci_scan(lst,&num_pci);
103     if(err)
104     {
105 	mp_msg(MSGT_VO, MSGL_STATUS, "[pm2] Error occurred during pci scan: %s\n",strerror(err));
106 	return err;
107     }
108     else
109     {
110 	err = ENXIO;
111 	for(i=0; i < num_pci; i++)
112 	{
113 	    int idx;
114 	    const char *dname;
115 	    idx = find_chip(lst[i].vendor, lst[i].device);
116 	    if(idx == -1)
117 		continue;
118 	    dname = pci_device_name(lst[i].vendor, lst[i].device);
119 	    dname = dname ? dname : "Unknown chip";
120 	    mp_msg(MSGT_VO, MSGL_STATUS, "[pm2] Found chip: %s\n", dname);
121 	    err = 0;
122 	    memcpy(&pci_info, &lst[i], sizeof(pciinfo_t));
123 	    break;
124 	}
125     }
126     if(err && verbose) mp_msg(MSGT_VO, MSGL_STATUS, "[pm2] Can't find chip.\n");
127     return err;
128 }
129 
130 #define PRINT_REG(reg)							\
131 {									\
132     long _foo = READ_REG(reg);						\
133     mp_msg(MSGT_VO, MSGL_STATUS, "[pm2] " #reg " (%x) = %#lx (%li)\n", reg, _foo, _foo);	\
134 }
135 
pm2_init(void)136 static int pm2_init(void)
137 {
138     char *vm;
139     pm2_reg_base = map_phys_mem(pci_info.base0, 0x10000);
140     pm2_mem = map_phys_mem(pci_info.base1, 1 << 23);
141     if((vm = getenv("PM2_VIDMEM"))){
142 	pm2_vidmem = strtol(vm, NULL, 0);
143     }
144     return 0;
145 }
146 
pm2_destroy(void)147 static void pm2_destroy(void)
148 {
149     unmap_phys_mem(pm2_reg_base, 0x10000);
150     unmap_phys_mem(pm2_mem, 1 << 23);
151 }
152 
pm2_get_caps(vidix_capability_t * to)153 static int pm2_get_caps(vidix_capability_t *to)
154 {
155     memcpy(to, &pm2_cap, sizeof(vidix_capability_t));
156     to->device_id = pci_info.device;
157     return 0;
158 }
159 
is_supported_fourcc(uint32_t fourcc)160 static int is_supported_fourcc(uint32_t fourcc)
161 {
162     switch(fourcc){
163     case IMGFMT_YUY2:
164 	return 1;
165     default:
166 	return 0;
167     }
168 }
169 
pm2_query_fourcc(vidix_fourcc_t * to)170 static int pm2_query_fourcc(vidix_fourcc_t *to)
171 {
172     if(is_supported_fourcc(to->fourcc))
173     {
174 	to->depth = VID_DEPTH_ALL;
175 	to->flags = VID_CAP_EXPAND | VID_CAP_SHRINK | VID_CAP_COLORKEY;
176 	return 0;
177     }
178     return ENOSYS;
179 }
180 
181 #define FORMAT_YUV422	((1 << 6) | 3 | (1 << 4))
182 
183 #define PPROD(a,b,c) (a | (b << 3) | (c << 6))
184 
185 static unsigned int ppcodes[][2] = {
186     {0, 0},
187     {32, PPROD(1, 0, 0)},
188     {64, PPROD(1, 1, 0)},
189     {96, PPROD(1, 1, 1)},
190     {128, PPROD(2, 1, 1)},
191     {160, PPROD(2, 2, 1)},
192     {192, PPROD(2, 2, 2)},
193     {224, PPROD(3, 2, 1)},
194     {256, PPROD(3, 2, 2)},
195     {288, PPROD(3, 3, 1)},
196     {320, PPROD(3, 3, 2)},
197     {384, PPROD(3, 3, 3)},
198     {416, PPROD(4, 3, 1)},
199     {448, PPROD(4, 3, 2)},
200     {512, PPROD(4, 3, 3)},
201     {544, PPROD(4, 4, 1)},
202     {576, PPROD(4, 4, 2)},
203     {640, PPROD(4, 4, 3)},
204     {768, PPROD(4, 4, 4)},
205     {800, PPROD(5, 4, 1)},
206     {832, PPROD(5, 4, 2)},
207     {896, PPROD(5, 4, 3)},
208     {1024, PPROD(5, 4, 4)},
209     {1056, PPROD(5, 5, 1)},
210     {1088, PPROD(5, 5, 2)},
211     {1152, PPROD(5, 5, 3)},
212     {1280, PPROD(5, 5, 4)},
213     {1536, PPROD(5, 5, 5)},
214     {1568, PPROD(6, 5, 1)},
215     {1600, PPROD(6, 5, 2)},
216     {1664, PPROD(6, 5, 3)},
217     {1792, PPROD(6, 5, 4)},
218     {2048, PPROD(6, 5, 5)}
219 };
220 
221 static int frames[VID_PLAY_MAXFRAMES];
222 
pm2_config_playback(vidix_playback_t * info)223 static int pm2_config_playback(vidix_playback_t *info)
224 {
225     unsigned int src_w, drw_w;
226     unsigned int src_h, drw_h;
227     long base0;
228     unsigned int stride, sstr;
229     unsigned int format;
230     unsigned int i;
231     unsigned int ppcode = 0, sppc = 0;
232     unsigned int pitch = 0;
233 
234     TRACE_ENTER();
235 
236     switch(info->fourcc){
237     case IMGFMT_YUY2:
238 	format = FORMAT_YUV422;
239 	break;
240     default:
241 	return -1;
242     }
243 
244     src_w = info->src.w;
245     src_h = info->src.h;
246 
247     drw_w = info->dest.w;
248     drw_h = info->dest.h;
249 
250     sstr = READ_REG(PMScreenStride) * 2;
251 
252     stride = 0;
253     for(i = 1; i < sizeof(ppcodes) / sizeof(ppcodes[0]); i++){
254 	if((!stride) && (ppcodes[i][0] >= src_w)){
255 	    stride = ppcodes[i][0];
256 	    ppcode = ppcodes[i][1];
257 	    pitch = ppcodes[i][0] - ppcodes[i-1][0];
258 	}
259 	if(ppcodes[i][0] == sstr)
260 	    sppc = ppcodes[i][1];
261     }
262 
263     if(!stride)
264 	return -1;
265 
266     info->num_frames = pm2_vidmem*1024*1024 / (stride * src_h * 2);
267     if(info->num_frames > VID_PLAY_MAXFRAMES)
268 	info->num_frames = VID_PLAY_MAXFRAMES;
269 
270     /* Use end of video memory. Assume the card has 8 MB */
271     base0 = (8 - pm2_vidmem)*1024*1024;
272     info->dga_addr = pm2_mem + base0;
273 
274     info->dest.pitch.y = pitch*2;
275     info->dest.pitch.u = 0;
276     info->dest.pitch.v = 0;
277     info->offset.y = 0;
278     info->offset.v = 0;
279     info->offset.u = 0;
280     info->frame_size = stride * src_h * 2;
281 
282     for(i = 0; i < info->num_frames; i++){
283 	info->offsets[i] = info->frame_size * i;
284 	frames[i] = (base0 + info->offsets[i]) >> 1;
285     }
286 
287     WRITE_REG(WindowOrigin, 0);
288     WRITE_REG(dY, 1 << 16);
289     WRITE_REG(RasterizerMode, 0);
290     WRITE_REG(ScissorMode, 0);
291     WRITE_REG(AreaStippleMode, 0);
292     WRITE_REG(StencilMode, 0);
293     WRITE_REG(TextureAddressMode, 1);
294 
295     WRITE_REG(dSdyDom, 0);
296     WRITE_REG(dTdx, 0);
297 
298     WRITE_REG(PMTextureMapFormat, (1 << 19) | ppcode);
299     WRITE_REG(PMTextureDataFormat, format);
300     WRITE_REG(PMTextureReadMode, (1 << 17) | /* FilterMode */
301 	      (11 << 13) | (11 << 9) /* TextureSize log2 */ | 1);
302     WRITE_REG(ColorDDAMode, 0);
303     WRITE_REG(TextureColorMode, (0 << 4) /* RGB */ | (3 << 1) /* Copy */ | 1);
304     WRITE_REG(AlphaBlendMode, 0);
305     WRITE_REG(DitherMode, (1 << 10) | 1);
306     WRITE_REG(LogicalOpMode, 0);
307     WRITE_REG(FBReadMode, sppc);
308     WRITE_REG(FBHardwareWriteMask, 0xFFFFFFFF);
309     WRITE_REG(FBWriteMode, 1);
310     WRITE_REG(YUVMode, 1);
311 
312     WRITE_REG(SStart, 0);
313     WRITE_REG(TStart, 0);
314 
315     WRITE_REG(dSdx, (src_w << 20) / drw_w);
316     WRITE_REG(dTdyDom, (src_h << 20) / drw_h);
317     WRITE_REG(RectangleOrigin, info->dest.x | (info->dest.y << 16));
318     WRITE_REG(RectangleSize, (drw_h << 16) | drw_w);
319 
320     TRACE_EXIT();
321     return 0;
322 }
323 
pm2_playback_on(void)324 static int pm2_playback_on(void)
325 {
326     TRACE_ENTER();
327 
328     TRACE_EXIT();
329     return 0;
330 }
331 
pm2_playback_off(void)332 static int pm2_playback_off(void)
333 {
334     WRITE_REG(YUVMode, 0);
335     WRITE_REG(TextureColorMode, 0);
336     WRITE_REG(TextureAddressMode, 0);
337     WRITE_REG(TextureReadMode, 0);
338     return 0;
339 }
340 
pm2_frame_select(unsigned int frame)341 static int pm2_frame_select(unsigned int frame)
342 {
343     WRITE_REG(PMTextureBaseAddress, frames[frame]);
344     WRITE_REG(Render, PrimitiveRectangle | XPositive | YPositive |
345 	      TextureEnable);
346     return 0;
347 }
348 
349 const VDXDriver pm2_drv = {
350   "pm2",
351   NULL,
352   .probe = pm2_probe,
353   .get_caps = pm2_get_caps,
354   .query_fourcc = pm2_query_fourcc,
355   .init = pm2_init,
356   .destroy = pm2_destroy,
357   .config_playback = pm2_config_playback,
358   .playback_on = pm2_playback_on,
359   .playback_off = pm2_playback_off,
360   .frame_sel = pm2_frame_select,
361 };
362