1 /*
2  * VIDIX driver for VIA CLE266/Unichrome chipsets.
3  *
4  * Copyright (C) 2004 Timothy Lee
5  * Thanks to Gilles Frattini for bugfixes
6  * Doxygen documentation by Benjamin Zores <ben@geexbox.org>
7  * h/w revision detection by Timothy Lee <timothy.lee@siriushk.com>
8  *
9  * This file is part of MPlayer.
10  *
11  * MPlayer is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * MPlayer is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <inttypes.h>
31 #include <unistd.h>
32 
33 #include "config.h"
34 #include "vidix.h"
35 #include "fourcc.h"
36 #include "dha.h"
37 #include "pci_ids.h"
38 #include "pci_names.h"
39 #include "mp_msg.h"
40 
41 #include "unichrome_regs.h"
42 
43 /**
44  * @brief Information on PCI device.
45  */
46 static pciinfo_t pci_info;
47 
48 /**
49  * @brief Unichrome driver colorkey settings.
50  */
51 static vidix_grkey_t uc_grkey;
52 
53 static int frames[VID_PLAY_MAXFRAMES];
54 static uint8_t *vio;
55 static uint8_t *uc_mem;
56 static uint8_t mclk_save[3];
57 static uint8_t hwrev;
58 
59 #define VIA_OUT(hwregs, reg, val)	*(volatile uint32_t *)((hwregs) + (reg)) = (val)
60 #define VIA_IN(hwregs, reg)		*(volatile uint32_t *)((hwregs) + (reg))
61 #define VGA_OUT8(hwregs, reg, val)	*(volatile uint8_t *)((hwregs) + (reg) + 0x8000) = (val)
62 #define VGA_IN8(hwregs, reg)		*(volatile uint8_t *)((hwregs) + (reg) + 0x8000)
63 #define VIDEO_OUT(hwregs, reg, val)	VIA_OUT((hwregs)+0x200, reg, val)
64 #define VIDEO_IN(hwregs, reg)		VIA_IN((hwregs)+0x200, reg)
65 
66 #define outb(val,reg)	OUTPORT8(reg,val)
67 #define inb(reg)	INPORT8(reg)
68 
69 #define ALIGN_TO(v, n) (((v) + (n-1)) & ~(n-1))
70 #define UC_MAP_V1_FIFO_CONTROL(depth, pre_thr, thr) \
71     (((depth)-1) | ((thr) << 8) | ((pre_thr) << 24))
72 
73 #define VIDEOMEMORY_SIZE	(8 * 1024 * 1024)
74 #define FRAMEBUFFER_SIZE	0x200000
75 #define FRAMEBUFFER_START	(VIDEOMEMORY_SIZE - FRAMEBUFFER_SIZE)
76 
77 #ifdef DEBUG_LOGFILE
78 static FILE *logfile = 0;
79 #define LOGWRITE(x) {if(logfile) fprintf(logfile,x);}
80 #else
81 #define LOGWRITE(x)
82 #endif
83 
84 /**
85  * @brief Unichrome driver vidix capabilities.
86  */
87 static const vidix_capability_t uc_cap = {
88   "VIA CLE266 Unichrome driver",
89   "Timothy Lee <timothy@siriushk.com>",
90   TYPE_OUTPUT,
91   {0, 0, 0, 0},
92   4096,
93   4096,
94   4,
95   4,
96   -1,
97   FLAG_UPSCALER | FLAG_DOWNSCALER,
98   VENDOR_VIA2,
99   -1,
100   {0, 0, 0, 0}
101 };
102 
103 /**
104  * @brief list of card IDs compliant with the Unichrome driver .
105  */
106 static unsigned short uc_card_ids[] = {
107   DEVICE_VIA2_VT8623_APOLLO_CLE266,
108   DEVICE_VIA2_VT8378_S3_UNICHROME
109 };
110 
111 /**
112  * @brief Find chip index in Unichrome compliant devices list.
113  *
114  * @param chip_id PCI device ID.
115  *
116  * @returns index position in uc_card_ids if successful.
117  *          -1 if chip_id is not a compliant chipset ID.
118  */
119 static int
find_chip(unsigned chip_id)120 find_chip (unsigned chip_id)
121 {
122   unsigned i;
123   for (i = 0; i < sizeof (uc_card_ids) / sizeof (unsigned short); i++)
124     {
125       if (chip_id == uc_card_ids[i])
126 	return i;
127     }
128   return -1;
129 }
130 
131 /**
132  * @brief Map hardware settings for vertical scaling.
133  *
134  * @param sh source height.
135  * @param dh destination height.
136  * @param zoom will hold vertical setting of zoom register.
137  * @param mini will hold vertical setting of mini register.
138  *
139  * @returns 1 if successful.
140  *          0 if the zooming factor is too large or small.
141  *
142  * @note Derived from VIA's V4L driver.
143  *       See ddover.c, DDOVER_HQVCalcZoomHeight()
144  */
145 static int
uc_ovl_map_vzoom(uint32_t sh,uint32_t dh,uint32_t * zoom,uint32_t * mini)146 uc_ovl_map_vzoom (uint32_t sh, uint32_t dh, uint32_t * zoom, uint32_t * mini)
147 {
148   uint32_t sh1, tmp, d;
149   int zoom_ok = 1;
150 
151   if (sh == dh) /* No zoom */
152     {
153       /* Do nothing */
154     }
155   else if (sh < dh) /* Zoom in */
156     {
157       tmp = (sh * 0x0400) / dh;
158       zoom_ok = !(tmp > 0x3ff);
159 
160       *zoom |= (tmp & 0x3ff) | V1_Y_ZOOM_ENABLE;
161       *mini |= V1_Y_INTERPOLY | V1_YCBCR_INTERPOLY;
162     }
163   else /* sw > dh - Zoom out */
164     {
165       /* Find a suitable divider (1 << d) = {2, 4, 8 or 16} */
166       sh1 = sh;
167       for (d = 1; d < 5; d++)
168 	{
169 	  sh1 >>= 1;
170 	  if (sh1 <= dh)
171 	    break;
172 	}
173       if (d == 5) /* too small */
174 	{
175 	  d = 4;
176 	  zoom_ok = 0;
177 	}
178 
179       *mini |= ((d << 1) - 1) << 16;	/* <= {1,3,5,7} << 16 */
180 
181       /* Add scaling */
182       if (sh1 < dh)
183 	{
184 	  tmp = (sh1 * 0x400) / dh;
185 	  *zoom |= ((tmp & 0x3ff) | V1_Y_ZOOM_ENABLE);
186 	  *mini |= V1_Y_INTERPOLY | V1_YCBCR_INTERPOLY;
187 	}
188     }
189 
190   return zoom_ok;
191 }
192 
193 /**
194  * @brief Map hardware settings for horizontal scaling.
195  *
196  * @param sw source width.
197  * @param dw destination width.
198  * @param zoom will hold horizontal setting of zoom register.
199  * @param mini will hold horizontal setting of mini register.
200  * @param falign will hold fetch aligment.
201  * @param dcount will hold display count.
202  *
203  * @returns 1 if successful.
204  *          0 if the zooming factor is too large or small.
205  *
206  * @note Derived from VIA's V4L driver.
207  *       See ddover.c, DDOVER_HQVCalcZoomWidth() and DDOver_GetDisplayCount()
208  */
209 static int
uc_ovl_map_hzoom(uint32_t sw,uint32_t dw,uint32_t * zoom,uint32_t * mini,int * falign,int * dcount)210 uc_ovl_map_hzoom (uint32_t sw, uint32_t dw, uint32_t * zoom, uint32_t * mini,
211 		  int *falign, int *dcount)
212 {
213   uint32_t tmp, sw1, d;
214   int md; /* Minify-divider */
215   int zoom_ok = 1;
216 
217   md = 1;
218   *falign = 0;
219 
220   if (sw == dw) /* no zoom */
221     {
222       /* Do nothing */
223     }
224   else if (sw < dw) /* zoom in */
225     {
226       tmp = (sw * 0x0800) / dw;
227       zoom_ok = !(tmp > 0x7ff);
228 
229       *zoom |= ((tmp & 0x7ff) << 16) | V1_X_ZOOM_ENABLE;
230       *mini |= V1_X_INTERPOLY;
231     }
232   else /* sw > dw - Zoom out */
233     {
234       /* Find a suitable divider (1 << d) = {2, 4, 8 or 16} */
235       sw1 = sw;
236       for (d = 1; d < 5; d++)
237 	{
238 	  sw1 >>= 1;
239 	  if (sw1 <= dw)
240 	    break;
241 	}
242       if (d == 5) /* too small */
243 	{
244 	  d = 4;
245 	  zoom_ok = 0;
246 	}
247 
248       md = 1 << d; /* <= {2,4,8,16} */
249       *falign = ((md << 1) - 1) & 0xf; /* <= {3,7,15,15} */
250       *mini |= V1_X_INTERPOLY;
251       *mini |= ((d << 1) - 1) << 24; /* <= {1,3,5,7} << 24 */
252 
253       /* Add scaling */
254       if (sw1 < dw)
255 	{
256 	  /* CLE bug */
257 	  /* tmp = sw1*0x0800 / dw; */
258 	  tmp = (sw1 - 2) * 0x0800 / dw;
259 	  *zoom |= ((tmp & 0x7ff) << 16) | V1_X_ZOOM_ENABLE;
260 	}
261     }
262 
263   *dcount = sw - md;
264   return zoom_ok;
265 }
266 
267 /**
268  * @brief qword fetch register setting.
269  *
270  * @param format overlay pixel format.
271  * @param sw source width.
272  *
273  * @return qword fetch register setting
274  *
275  * @note Derived from VIA's V4L driver. See ddover.c, DDOver_GetFetch()
276  * @note Only call after uc_ovl_map_hzoom()
277  */
278 static uint32_t
uc_ovl_map_qwfetch(uint32_t format,int sw)279 uc_ovl_map_qwfetch (uint32_t format, int sw)
280 {
281   uint32_t fetch = 0;
282 
283   switch (format)
284     {
285     case IMGFMT_YV12:
286     case IMGFMT_I420:
287       fetch = ALIGN_TO (sw, 32) >> 4;
288       break;
289     case IMGFMT_UYVY:
290     case IMGFMT_YVYU:
291     case IMGFMT_YUY2:
292       fetch = (ALIGN_TO (sw << 1, 16) >> 4) + 1;
293       break;
294     case IMGFMT_BGR15:
295     case IMGFMT_BGR16:
296       fetch = (ALIGN_TO (sw << 1, 16) >> 4) + 1;
297       break;
298     case IMGFMT_BGR32:
299       fetch = (ALIGN_TO (sw << 2, 16) >> 4) + 1;
300       break;
301     default:
302       mp_msg(MSGT_VO, MSGL_STATUS, "[unichrome] Unexpected pixelformat!");
303       break;
304     }
305 
306   if (fetch < 4)
307     fetch = 4;
308 
309   return fetch;
310 }
311 
312 /**
313  * @brief Map pixel format.
314  *
315  * @param format pixel format.
316  *
317  * @return the mapped pixel format.
318  *
319  * @note Derived from VIA's V4L driver. See ddover.c, DDOver_GetV1Format()
320  */
321 static uint32_t
uc_ovl_map_format(uint32_t format)322 uc_ovl_map_format (uint32_t format)
323 {
324   switch (format)
325     {
326     case IMGFMT_UYVY:
327     case IMGFMT_YVYU:
328     case IMGFMT_YUY2:
329       return V1_COLORSPACE_SIGN | V1_YUV422;
330     case IMGFMT_IYUV:
331       return V1_COLORSPACE_SIGN | V1_YCbCr420 | V1_SWAP_SW;
332     case IMGFMT_YV12:
333     case IMGFMT_I420:
334       return V1_COLORSPACE_SIGN | V1_YCbCr420;
335     case IMGFMT_BGR15:
336       return V1_RGB15;
337     case IMGFMT_BGR16:
338       return V1_RGB16;
339     case IMGFMT_BGR32:
340       return V1_RGB32;
341     default:
342       mp_msg(MSGT_VO, MSGL_STATUS, "[unichrome] Unexpected pixelformat!");
343       return V1_YUV422;
344     }
345 }
346 
347 /**
348  * @brief Calculate V1 control and fifo-control register values.
349  *
350  * @param format pixel format.
351  * @param sw source width.
352  * @param hwrev CLE266 hardware revision.
353  * @param extfifo_on set this 1 if the extended FIFO is enabled.
354  * @param control will hold value for V1_CONTROL.
355  * @param fifo will hold value for V1_FIFO_CONTROL.
356  */
357 static void
uc_ovl_map_v1_control(uint32_t format,int sw,int hwrev,int extfifo_on,uint32_t * control,uint32_t * fifo)358 uc_ovl_map_v1_control (uint32_t format, int sw,
359 		       int hwrev, int extfifo_on,
360 		       uint32_t * control, uint32_t * fifo)
361 {
362   *control = V1_BOB_ENABLE | uc_ovl_map_format (format);
363 
364   if (hwrev == 0x10)
365     {
366       *control |= V1_EXPIRE_NUM_F;
367     }
368   else
369     {
370       if (extfifo_on)
371 	{
372 	  *control |= V1_EXPIRE_NUM_A | V1_FIFO_EXTENDED;
373 	}
374       else
375 	{
376 	  *control |= V1_EXPIRE_NUM;
377 	}
378     }
379 
380   if ((format == IMGFMT_YV12) || (format == IMGFMT_I420))
381     {
382       /* Minified video will be skewed without this workaround. */
383       if (sw <= 80) /* Fetch count <= 5 */
384 	{
385 	  *fifo = UC_MAP_V1_FIFO_CONTROL (16, 0, 0);
386 	}
387       else
388 	{
389 	  if (hwrev == 0x10)
390 	    *fifo = UC_MAP_V1_FIFO_CONTROL (64, 56, 56);
391 	  else
392 	    *fifo = UC_MAP_V1_FIFO_CONTROL (16, 12, 8);
393 	}
394     }
395   else
396     {
397       if (hwrev == 0x10)
398 	{
399 	  *fifo = UC_MAP_V1_FIFO_CONTROL (64, 56, 56); /* Default rev 0x10 */
400 	}
401       else
402 	{
403 	  if (extfifo_on)
404 	    *fifo = UC_MAP_V1_FIFO_CONTROL (48, 40, 40);
405 	  else
406 	    *fifo = UC_MAP_V1_FIFO_CONTROL (32, 29, 16); /* Default */
407 	}
408     }
409 }
410 
411 /**
412  * @brief Setup extended FIFO.
413  *
414  * @param extfifo_on pointer determining if extended fifo is enable or not.
415  * @param dst_w destination width.
416  */
417 static void
uc_ovl_setup_fifo(int * extfifo_on,int dst_w)418 uc_ovl_setup_fifo (int *extfifo_on, int dst_w)
419 {
420   if (dst_w <= 1024) /* Disable extended FIFO */
421     {
422       outb (0x16, 0x3c4);
423       outb (mclk_save[0], 0x3c5);
424       outb (0x17, 0x3c4);
425       outb (mclk_save[1], 0x3c5);
426       outb (0x18, 0x3c4);
427       outb (mclk_save[2], 0x3c5);
428       *extfifo_on = 0;
429     }
430   else /* Enable extended FIFO */
431     {
432       outb (0x17, 0x3c4);
433       outb (0x2f, 0x3c5);
434       outb (0x16, 0x3c4);
435       outb ((mclk_save[0] & 0xf0) | 0x14, 0x3c5);
436       outb (0x18, 0x3c4);
437       outb (0x56, 0x3c5);
438       *extfifo_on = 1;
439     }
440 }
441 
442 static void
uc_ovl_vcmd_wait(volatile uint8_t * vio)443 uc_ovl_vcmd_wait (volatile uint8_t * vio)
444 {
445   while ((VIDEO_IN (vio, V_COMPOSE_MODE)
446 	  & (V1_COMMAND_FIRE | V3_COMMAND_FIRE)));
447 }
448 
449 /**
450  * @brief Probe hardware to find some useable chipset.
451  *
452  * @param verbose specifies verbose level.
453  * @param force specifies force mode : driver should ignore
454  *              device_id (danger but useful for new devices)
455  *
456  * @returns 0 if it can handle something in PC.
457  *          a negative error code otherwise.
458  */
459 static int
unichrome_probe(int verbose,int force)460 unichrome_probe (int verbose, int force)
461 {
462   pciinfo_t lst[MAX_PCI_DEVICES];
463   unsigned i, num_pci;
464   int err;
465   err = pci_scan (lst, &num_pci);
466   if (err)
467     {
468       mp_msg(MSGT_VO, MSGL_STATUS, "[unichrome] Error occurred during pci scan: %s\n",
469 	      strerror (err));
470       return err;
471     }
472   else
473     {
474       err = ENXIO;
475       for (i = 0; i < num_pci; i++)
476 	{
477 	  if (lst[i].vendor == VENDOR_VIA2)
478 	    {
479 	      int idx;
480 	      const char *dname;
481 	      idx = find_chip (lst[i].device);
482 	      if (idx == -1)
483 		continue;
484 	      dname = pci_device_name (VENDOR_VIA2, lst[i].device);
485 	      dname = dname ? dname : "Unknown chip";
486 	      mp_msg(MSGT_VO, MSGL_STATUS, "[unichrome] Found chip: %s\n", dname);
487 	      if ((lst[i].command & PCI_COMMAND_IO) == 0)
488 		{
489 		  mp_msg(MSGT_VO, MSGL_STATUS, "[unichrome] Device is disabled, ignoring\n");
490 		  continue;
491 		}
492 	      err = 0;
493 	      memcpy (&pci_info, &lst[i], sizeof (pciinfo_t));
494 	      break;
495 	    }
496 	}
497     }
498 
499   if (err && verbose)
500     mp_msg(MSGT_VO, MSGL_STATUS, "[unichrome] Can't find chip\n");
501   return err;
502 }
503 
504 /**
505  * @brief Initializes driver.
506  *
507  * @returns 0 if ok.
508  *          a negative error code otherwise.
509  */
510 static int
unichrome_init(void)511 unichrome_init (void)
512 {
513   long tmp;
514   uc_mem = map_phys_mem (pci_info.base0, VIDEOMEMORY_SIZE);
515   enable_app_io ();
516 
517   outb (0x2f, 0x3c4);
518   tmp = (unsigned)(inb (0x3c5)) << 0x18;
519   vio = map_phys_mem (tmp, 0x1000);
520 
521   outb (0x16, 0x3c4);
522   mclk_save[0] = inb (0x3c5);
523   outb (0x17, 0x3c4);
524   mclk_save[1] = inb (0x3c5);
525   outb (0x18, 0x3c4);
526   mclk_save[2] = inb (0x3c5);
527 
528   uc_grkey.ckey.blue = 0x00;
529   uc_grkey.ckey.green = 0x00;
530   uc_grkey.ckey.red = 0x00;
531 
532   /* Detect whether we have a CLE266Ax or CLE266Cx */
533   outb (0x4f, 0x3d4);
534   tmp = inb (0x3d5);
535   outb (0x4f, 0x3d4);
536   outb (0x55, 0x3d5);
537   outb (0x4f, 0x3d4);
538   if (0x55 == inb (0x3d5))
539   {
540     /* Only CLE266Cx supports CR4F */
541     hwrev = 0x11;
542   }
543   else
544   {
545     /* Otherwise assume to be a CLE266Ax */
546     hwrev = 0x00;
547   }
548   outb (0x4f, 0x3d4);
549   outb (tmp, 0x3d5);
550 
551 #ifdef DEBUG_LOGFILE
552   logfile = fopen ("/tmp/uc_vidix.log", "w");
553 #endif
554   return 0;
555 }
556 
557 /**
558  * @brief Destroys driver.
559  */
560 static void
unichrome_destroy(void)561 unichrome_destroy (void)
562 {
563 #ifdef DEBUG_LOGFILE
564   if (logfile)
565     fclose (logfile);
566 #endif
567   outb (0x16, 0x3c4);
568   outb (mclk_save[0], 0x3c5);
569   outb (0x17, 0x3c4);
570   outb (mclk_save[1], 0x3c5);
571   outb (0x18, 0x3c4);
572   outb (mclk_save[2], 0x3c5);
573 
574   disable_app_io ();
575   unmap_phys_mem (uc_mem, VIDEOMEMORY_SIZE);
576   unmap_phys_mem (vio, 0x1000);
577 }
578 
579 /**
580  * @brief Get chipset's hardware capabilities.
581  *
582  * @param to Pointer to the vidix_capability_t structure to be filled.
583  *
584  * @returns 0.
585  */
586 static int
unichrome_get_caps(vidix_capability_t * to)587 unichrome_get_caps (vidix_capability_t * to)
588 {
589   memcpy (to, &uc_cap, sizeof (vidix_capability_t));
590   to->device_id = pci_info.device;
591   return 0;
592 }
593 
594 /**
595  * @brief Report if the video FourCC is supported by hardware.
596  *
597  * @param fourcc input image format.
598  *
599  * @returns 1 if the fourcc is supported.
600  *          0 otherwise.
601  */
602 static int
is_supported_fourcc(uint32_t fourcc)603 is_supported_fourcc (uint32_t fourcc)
604 {
605   switch (fourcc)
606     {
607     case IMGFMT_YV12:
608     case IMGFMT_I420:
609     case IMGFMT_UYVY:
610     case IMGFMT_YVYU:
611     case IMGFMT_YUY2:
612     case IMGFMT_BGR15:
613     case IMGFMT_BGR16:
614     case IMGFMT_BGR32:
615       return 1;
616     default:
617       return 0;
618     }
619 }
620 
621 /**
622  * @brief Try to configure video memory for given fourcc.
623  *
624  * @param to Pointer to the vidix_fourcc_t structure to be filled.
625  *
626  * @returns 0 if ok.
627  *          errno otherwise.
628  */
629 static int
unichrome_query_fourcc(vidix_fourcc_t * to)630 unichrome_query_fourcc (vidix_fourcc_t * to)
631 {
632   if (is_supported_fourcc (to->fourcc))
633     {
634       to->depth = VID_DEPTH_ALL;
635       to->flags = VID_CAP_EXPAND | VID_CAP_SHRINK | VID_CAP_COLORKEY;
636       return 0;
637     }
638   return ENOSYS;
639 }
640 
641 /**
642  * @brief Get the GrKeys
643  *
644  * @param grkey Pointer to the vidix_grkey_t structure to be filled by driver.
645  *
646  * @return 0.
647  */
648 static int
unichrome_get_gkey(vidix_grkey_t * grkey)649 unichrome_get_gkey (vidix_grkey_t * grkey)
650 {
651   memcpy (grkey, &uc_grkey, sizeof (vidix_grkey_t));
652   return 0;
653 }
654 
655 /**
656  * @brief Set the GrKeys
657  *
658  * @param grkey Colorkey to be set.
659  *
660  * @return 0.
661  */
662 static int
unichrome_set_gkey(const vidix_grkey_t * grkey)663 unichrome_set_gkey (const vidix_grkey_t * grkey)
664 {
665   unsigned long dwCompose = VIDEO_IN (vio, V_COMPOSE_MODE) & ~0x0f;
666   memcpy (&uc_grkey, grkey, sizeof (vidix_grkey_t));
667   if (uc_grkey.ckey.op != CKEY_FALSE)
668     {
669       /* Set colorkey (how do I detect BPP in hardware ??) */
670       unsigned long ckey;
671       if (1) /* Assume 16-bit graphics */
672 	{
673 	  ckey = (grkey->ckey.blue & 0x1f)
674 	    | ((grkey->ckey.green & 0x3f) << 5)
675 	    | ((grkey->ckey.red & 0x1f) << 11);
676 	}
677       else
678 	{
679 	  ckey = (grkey->ckey.blue)
680 	    | (grkey->ckey.green << 8) | (grkey->ckey.red << 16);
681 	}
682       VIDEO_OUT (vio, V_COLOR_KEY, ckey);
683       dwCompose |= SELECT_VIDEO_IF_COLOR_KEY;
684     }
685 
686   /* Execute the changes */
687   VIDEO_OUT (vio, V_COMPOSE_MODE, dwCompose | V1_COMMAND_FIRE);
688   return 0;
689 }
690 
691 /**
692  * @brief Unichrome driver equalizer capabilities.
693  */
694 static vidix_video_eq_t equal = {
695   VEQ_CAP_BRIGHTNESS | VEQ_CAP_SATURATION | VEQ_CAP_HUE,
696   300, 100, 0, 0, 0, 0, 0, 0
697 };
698 
699 
700 /**
701  * @brief Get the equalizer capabilities.
702  *
703  * @param eq Pointer to the vidix_video_eq_t structure to be filled by driver.
704  *
705  * @return 0.
706  */
707 static int
unichrome_get_eq(vidix_video_eq_t * eq)708 unichrome_get_eq (vidix_video_eq_t * eq)
709 {
710   memcpy (eq, &equal, sizeof (vidix_video_eq_t));
711   return 0;
712 }
713 
714 /**
715  * @brief Set the equalizer capabilities for color correction
716  *
717  * @param eq equalizer capabilities to be set.
718  *
719  * @return 0.
720  */
721 static int
unichrome_set_eq(const vidix_video_eq_t * eq)722 unichrome_set_eq (const vidix_video_eq_t * eq)
723 {
724   return 0;
725 }
726 
727 /**
728  * @brief Y, U, V offsets.
729  */
730 static int YOffs, UOffs, VOffs;
731 
732 static int unichrome_frame_select (unsigned int frame);
733 
734 /**
735  * @brief Configure driver for playback. Driver should prepare BES.
736  *
737  * @param info configuration description for playback.
738  *
739  * @returns  0 in case of success.
740  *          -1 otherwise.
741  */
742 static int
unichrome_config_playback(vidix_playback_t * info)743 unichrome_config_playback (vidix_playback_t * info)
744 {
745   int src_w, drw_w;
746   int src_h, drw_h;
747   long base0, pitch = 0;
748   int uv_size = 0, swap_uv;
749   unsigned int i;
750   int extfifo_on;
751 
752   /* Overlay register settings */
753   uint32_t win_start, win_end;
754   uint32_t zoom, mini;
755   uint32_t dcount, falign, qwfetch;
756   uint32_t v_ctrl, fifo_ctrl;
757 
758   if (!is_supported_fourcc (info->fourcc))
759     return -1;
760 
761   src_w = info->src.w;
762   src_h = info->src.h;
763 
764   drw_w = info->dest.w;
765   drw_h = info->dest.h;
766 
767   /* Setup FIFO */
768   uc_ovl_setup_fifo (&extfifo_on, src_w);
769 
770   /* Get image format, FIFO size, etc. */
771   uc_ovl_map_v1_control (info->fourcc, src_w, hwrev, extfifo_on,
772 			 &v_ctrl, &fifo_ctrl);
773 
774   /* Setup layer window */
775   win_start = (info->dest.x << 16) | info->dest.y;
776   win_end = ((info->dest.x + drw_w - 1) << 16) | (info->dest.y + drw_h - 1);
777 
778   /* Get scaling and data-fetch parameters */
779   zoom = 0;
780   mini = 0;
781   uc_ovl_map_vzoom (src_h, drw_h, &zoom, &mini);
782   uc_ovl_map_hzoom (src_w, drw_w, &zoom, &mini, (int *) &falign, (int *) &dcount);
783   qwfetch = uc_ovl_map_qwfetch (info->fourcc, src_w);
784 
785   /* Calculate buffer sizes */
786   swap_uv = 0;
787   switch (info->fourcc)
788     {
789     case IMGFMT_YV12:
790       swap_uv = 1;
791       /* Fallthrough, same as the following otherwise */
792     case IMGFMT_I420:
793     case IMGFMT_UYVY:
794     case IMGFMT_YVYU:
795       pitch = ALIGN_TO (src_w, 32);
796       uv_size = (pitch >> 1) * (src_h >> 1);
797       break;
798 
799     case IMGFMT_YUY2:
800     case IMGFMT_BGR15:
801     case IMGFMT_BGR16:
802       pitch = ALIGN_TO (src_w << 1, 32);
803       uv_size = 0;
804       break;
805 
806     case IMGFMT_BGR32:
807       pitch = ALIGN_TO (src_w << 2, 32);
808       uv_size = 0;
809       break;
810 
811     default: // should have been caught by is_supported_fourcc above
812       return -1;
813     }
814   if ((src_w > 4096) || (src_h > 4096) ||
815       (src_w < 32) || (src_h < 1) || (pitch > 0x1fff))
816     {
817       mp_msg(MSGT_VO, MSGL_STATUS, "[unichrome] Layer size out of bounds\n");
818     }
819 
820   /* Calculate offsets */
821   info->offset.y = 0;
822   info->offset.v = info->offset.y + pitch * src_h;
823   info->offset.u = info->offset.v + uv_size;
824   info->frame_size = info->offset.u + uv_size;
825   YOffs = info->offset.y;
826   UOffs = (swap_uv ? info->offset.v : info->offset.u);
827   VOffs = (swap_uv ? info->offset.u : info->offset.v);
828 
829   /* Assume we have 2 MB to play with */
830   info->num_frames = FRAMEBUFFER_SIZE / info->frame_size;
831   if (info->num_frames > VID_PLAY_MAXFRAMES)
832     info->num_frames = VID_PLAY_MAXFRAMES;
833 
834   /* Start at 6 MB. Let's hope it's not in use. */
835   base0 = FRAMEBUFFER_START;
836   info->dga_addr = uc_mem + base0;
837 
838   info->dest.pitch.y = 32;
839   info->dest.pitch.u = 32;
840   info->dest.pitch.v = 32;
841 
842   for (i = 0; i < info->num_frames; i++)
843     {
844       info->offsets[i] = info->frame_size * i;
845       frames[i] = base0 + info->offsets[i];
846     }
847 
848   /* Write to the hardware */
849   uc_ovl_vcmd_wait (vio);
850 
851   /* Configure diy_pitchlay parameters now */
852   if (v_ctrl & V1_COLORSPACE_SIGN)
853     {
854       if (hwrev >= 0x10)
855 	{
856 	  VIDEO_OUT (vio, V1_ColorSpaceReg_2, ColorSpaceValue_2_3123C0);
857 	  VIDEO_OUT (vio, V1_ColorSpaceReg_1, ColorSpaceValue_1_3123C0);
858 	}
859       else
860 	{
861       VIDEO_OUT (vio, V1_ColorSpaceReg_2, ColorSpaceValue_2);
862       VIDEO_OUT (vio, V1_ColorSpaceReg_1, ColorSpaceValue_1);
863     }
864     }
865 
866   VIDEO_OUT (vio, V1_CONTROL, v_ctrl);
867   VIDEO_OUT (vio, V_FIFO_CONTROL, fifo_ctrl);
868 
869   VIDEO_OUT (vio, V1_WIN_START_Y, win_start);
870   VIDEO_OUT (vio, V1_WIN_END_Y, win_end);
871 
872   VIDEO_OUT (vio, V1_SOURCE_HEIGHT, (src_h << 16) | dcount);
873 
874   VIDEO_OUT (vio, V12_QWORD_PER_LINE, qwfetch << 20);
875   VIDEO_OUT (vio, V1_STRIDE, pitch | ((pitch >> 1) << 16));
876 
877   VIDEO_OUT (vio, V1_MINI_CONTROL, mini);
878   VIDEO_OUT (vio, V1_ZOOM_CONTROL, zoom);
879 
880   /* Configure buffer address and execute the changes now! */
881   unichrome_frame_select (0);
882 
883   return 0;
884 }
885 
886 /**
887  * @brief Set playback on : driver should activate BES on this call.
888  *
889  * @return 0.
890  */
891 static int
unichrome_playback_on(void)892 unichrome_playback_on (void)
893 {
894   LOGWRITE ("Enable overlay\n");
895 
896   /* Turn on overlay */
897   VIDEO_OUT (vio, V1_CONTROL, VIDEO_IN (vio, V1_CONTROL) | V1_ENABLE);
898 
899   /* Execute the changes */
900   VIDEO_OUT (vio, V_COMPOSE_MODE,
901 	     VIDEO_IN (vio, V_COMPOSE_MODE) | V1_COMMAND_FIRE);
902 
903   return 0;
904 }
905 
906 /**
907  * @brief Set playback off : driver should deactivate BES on this call.
908  *
909  * @return 0.
910  */
911 static int
unichrome_playback_off(void)912 unichrome_playback_off (void)
913 {
914   LOGWRITE ("Disable overlay\n");
915 
916   uc_ovl_vcmd_wait (vio);
917 
918   /* Restore FIFO */
919   VIDEO_OUT (vio, V_FIFO_CONTROL, UC_MAP_V1_FIFO_CONTROL (16, 12, 8));
920 
921   /* Turn off overlay */
922   VIDEO_OUT (vio, V1_CONTROL, VIDEO_IN (vio, V1_CONTROL) & ~V1_ENABLE);
923 
924   /* Execute the changes */
925   VIDEO_OUT (vio, V_COMPOSE_MODE,
926 	     VIDEO_IN (vio, V_COMPOSE_MODE) | V1_COMMAND_FIRE);
927 
928   return 0;
929 }
930 
931 /**
932  * @brief Driver should prepare and activate corresponded frame.
933  *
934  * @param frame the frame index.
935  *
936  * @return 0.
937  *
938  * @note This function is used only for double and triple buffering
939  *       and never used for single buffering playback.
940  */
941 static int
unichrome_frame_select(unsigned int frame)942 unichrome_frame_select (unsigned int frame)
943 {
944   LOGWRITE ("Frame select\n");
945 
946   uc_ovl_vcmd_wait (vio);
947 
948   /* Configure buffer address */
949   VIDEO_OUT (vio, V1_STARTADDR_Y0, frames[frame] + YOffs);
950   VIDEO_OUT (vio, V1_STARTADDR_CB0, frames[frame] + UOffs);
951   VIDEO_OUT (vio, V1_STARTADDR_CR0, frames[frame] + VOffs);
952 
953   /* Execute the changes */
954   VIDEO_OUT (vio, V_COMPOSE_MODE,
955 	     VIDEO_IN (vio, V_COMPOSE_MODE) | V1_COMMAND_FIRE);
956 
957   return 0;
958 }
959 
960 const VDXDriver unichrome_drv = {
961   "unichrome",
962   NULL,
963   .probe = unichrome_probe,
964   .get_caps = unichrome_get_caps,
965   .query_fourcc = unichrome_query_fourcc,
966   .init = unichrome_init,
967   .destroy = unichrome_destroy,
968   .config_playback = unichrome_config_playback,
969   .playback_on = unichrome_playback_on,
970   .playback_off = unichrome_playback_off,
971   .frame_sel = unichrome_frame_select,
972   .get_eq = unichrome_get_eq,
973   .set_eq = unichrome_set_eq,
974   .get_gkey = unichrome_get_gkey,
975   .set_gkey = unichrome_set_gkey,
976 };
977