summaryrefslogtreecommitdiff
path: root/Source/DirectFB/gfxdrivers/cle266/uc_ovl_hwmap.c
blob: 890b9bcda24da34bd1efa6f403c9b730c27e175c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
/*
   Copyright (c) 2003 Andreas Robinson, All rights reserved.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
*/

#include <config.h>

#include <direct/messages.h>

#include "unichrome.h"
#include "uc_overlay.h"
#include "vidregs.h"
#include "mmio.h"
#include <math.h>

/**
 * Map hw settings for vertical scaling.
 *
 * @param sh        source height
 * @param dh        destination height
 * @param zoom      will hold vertical setting of zoom register.
 * @param mini      will hold vertical setting of mini register.
 *
 * @returns true if successful.
 *          false if the zooming factor is too large or small.
 *
 * @note Derived from VIA's V4L driver.
 *       See ddover.c, DDOVER_HQVCalcZoomHeight()
 */

bool uc_ovl_map_vzoom(int sh, int dh, u32* zoom, u32* mini)
{
    u32 sh1, tmp, d;
    bool zoom_ok = true;

    if (sh == dh) { // No zoom
        // Do nothing
    }
    else if (sh < dh) { // Zoom in

        tmp = (sh * 0x0400) / dh;
        zoom_ok = !(tmp > 0x3ff);

        *zoom |= (tmp & 0x3ff) | V1_Y_ZOOM_ENABLE;
        *mini |= V1_Y_INTERPOLY | V1_YCBCR_INTERPOLY;
    }
    else { // sw > dh - Zoom out

        // Find a suitable divider (1 << d) = {2, 4, 8 or 16}

        sh1 = sh;
        for (d = 1; d < 5; d++) {
            sh1 >>= 1;
            if (sh1 <= dh) break;
        }
        if (d == 5) { // Too small.
            d = 4;
            zoom_ok = false;
        }

        *mini |= ((d<<1)-1) << 16;  // <= {1,3,5,7} << 16

        // Add scaling

        if (sh1 < dh)  {
            tmp = (sh1 * 0x400) / dh;
            *zoom |= ((tmp & 0x3ff) | V1_Y_ZOOM_ENABLE);
            *mini |= V1_Y_INTERPOLY | V1_YCBCR_INTERPOLY;
        }
    }

    return zoom_ok;
}


/**
 * Map hw settings for horizontal scaling.
 *
 * @param sw        source width
 * @param dw        destination width
 *
 * @param zoom      will hold horizontal setting of zoom register.
 * @param mini      will hold horizontal setting of mini register.
 * @param falign    will hold fetch aligment
 * @param dcount    will hold display count
 *
 * @returns true if successful.
 *          false if the zooming factor is too large or small.
 *
 * @note Derived from VIA's V4L driver.
 *       See ddover.c, DDOVER_HQVCalcZoomWidth() and DDOver_GetDisplayCount()
 */
bool uc_ovl_map_hzoom(int sw, int dw,  u32* zoom, u32* mini,
                      u32* falign, u32* dcount)
{
    u32 tmp, sw1, d;
    int md; // Minify-divider
    bool zoom_ok = true;

    md = 1;
    *falign = 0;

    if (sw == dw) { // No zoom
        // Do nothing
    }
    else if (sw < dw) { // Zoom in

        tmp = (sw * 0x0800) / dw;
        zoom_ok = !(tmp > 0x7ff);

        *zoom |= ((tmp & 0x7ff) << 16) | V1_X_ZOOM_ENABLE;
        *mini |= V1_X_INTERPOLY;
    }
    else { // sw > dw - Zoom out

        // Find a suitable divider (1 << d) = {2, 4, 8 or 16}

        sw1 = sw;
        for (d = 1; d < 5; d++) {
            sw1 >>= 1;
            if (sw1 <= dw) break;
        }
        if (d == 5) { // Too small.
            d = 4;
            zoom_ok = false;
        }

        md = 1 << d;                    // <= {2,4,8,16}
        *falign = ((md<<1)-1) & 0xf;    // <= {3,7,15,15}
        *mini |= V1_X_INTERPOLY;
        *mini |= ((d<<1)-1) << 24;      // <= {1,3,5,7} << 24

        // Add scaling

        if (sw1 < dw) {
            //CLE bug
            //tmp = sw1*0x0800 / dw;
            tmp = (sw1 - 2) * 0x0800 / dw;
            *zoom |= ((tmp & 0x7ff) << 16) | V1_X_ZOOM_ENABLE;
        }
    }

    *dcount = sw - md;

    return zoom_ok;
}


/**
 * @param falign    fetch alignment
 * @param format    overlay pixel format
 * @param sw        source width
 *
 * @returns qword pitch register setting
 *
 * @note Derived from VIA's V4L driver. See ddover.c, DDOver_GetFetch()
 * @note Only call after uc_ovl_map_hzoom()
 */
u32 uc_ovl_map_qwpitch(int falign, DFBSurfacePixelFormat format, int sw)
{
    int fetch = 0;

    switch (format) {
    case DSPF_YV12:
        fetch = ALIGN_TO(sw, 32) >> 4;
        break;
    case DSPF_I420:
        fetch = (ALIGN_TO(sw, 16) >> 4) + 1;
        break;
    case DSPF_UYVY:
    case DSPF_YUY2:
        fetch = (ALIGN_TO(sw << 1, 16) >> 4) + 1;
        break;
    case DSPF_ARGB1555:
    case DSPF_RGB16:
        fetch = (ALIGN_TO(sw << 1, 16) >> 4) + 1;
        break;
    case DSPF_RGB32:
    case DSPF_ARGB:
        fetch = (ALIGN_TO(sw << 2, 16) >> 4) + 1;
        break;
    default:
        D_BUG("Unexpected pixelformat!");
        break;
    }

    if (fetch < 4) fetch = 4;

    // Note: Unsure if alignment is needed or is in the way.
    fetch = ALIGN_TO(fetch, falign + 1);
    return fetch << 20; // V12_QWORD_PER_LINE
}


/**
 * Map pixel format.
 *
 * @note Derived from VIA's V4L driver. See ddover.c, DDOver_GetV1Format()
 */
u32 uc_ovl_map_format(DFBSurfacePixelFormat format)
{
    switch (format) {
    case DSPF_YV12:
    case DSPF_I420:
        return V1_COLORSPACE_SIGN | V1_YUV420;
    case DSPF_UYVY:
    case DSPF_YUY2:
        return V1_COLORSPACE_SIGN | V1_YUV422;
    case DSPF_ARGB1555:
        return V1_RGB15;
    case DSPF_RGB16:
        return V1_RGB16;
    case DSPF_RGB32:
    case DSPF_ARGB:
        return V1_RGB32;
    default :
        D_BUG("Unexpected pixelformat!");
        return V1_YUV422;
    }
}


/**
 * Map overlay window.
 *
 * @param scrw      screen width (eg. 800)
 * @param scrh      screen height (eg. 600)
 * @param win       destination window
 * @param sw        source surface width
 * @param sh        source surface height
 *
 * @param win_start will hold window start register setting
 * @param win_end   will hold window end register setting
 *
 * @parm ox         will hold new leftmost coordinate in source surface
 * @parm oy         will hold new topmost coordinate in source surface
 */
void uc_ovl_map_window(int scrw, int scrh, DFBRectangle* win, int sw, int sh,
                       u32* win_start, u32* win_end, int* ox, int* oy)
{
    int x1, y1, x2, y2;
    int x,y,dw,dh;      // These help making the code readable...

    *ox = 0;
    *oy = 0;
    *win_start = 0;
    *win_end = 0;

    x = win->x;
    y = win->y;
    dw = win->w;
    dh = win->h;

    // For testing the clipping
    //scrw -= 100;
    //scrh -= 100;

    // Handle invisible case.
    if ((x > scrw) || (y > scrh) || (x+dw < 0) || (y+dh < 0)) return;

    // Vertical clipping

    if ((y >= 0) && (y+dh < scrh)) {
        // No clipping
        y1 = y;
        y2 = y+dh-1;
    }
    else if ((y < 0) && (y+dh < scrh)) {
        // Top clip
        y1 = 0;
        y2 = y+dh-1;
        *oy = (int) (((float) (sh * -y)) / ((float) dh) + 0.5);
    }
    else if ((y >= 0) && (y+dh >= scrh)) {
        // Bottom clip
        y1 = y;
        y2 = scrh-1;
    }
    else { // if (y < 0) && (y+dh >= scrh)
        // Top and bottom clip
        y1 = 0;
        y2 = scrh-1;
        *oy = (int) (((float) (sh * -y)) / ((float) dh) + 0.5);
    }

    // Horizontal clipping

    if ((x >= 0) && (x+dw < scrw)) {
        // No clipping
        x1 = x;
        x2 = x+dw-1;
    }
    else if ((x < 0) && (x+dw < scrw)) {
        // Left clip
        x1 = 0;
        x2 = x+dw-1;
        *ox = (int) (((float) (sw * -x)) / ((float) dw) + 0.5);
    }
    else if ((x >= 0) && (x+dw >= scrw)) {
        // Right clip
        x1 = x;
        x2 = scrw-1;
    }
    else { // if (x < 0) && (x+dw >= scrw)
        // Left and right clip
        x1 = 0;
        x2 = scrw-1;
        *ox = (int) (((float) (sw * -x)) / ((float) dw) + 0.5);
    }

    *win_start = (x1 << 16) | y1;
    *win_end = (x2 << 16) | y2;

    // For testing the clipping
    //*win_start = ((x1+50) << 16) | (y1+50);
    //*win_end = ((x2+50) << 16) | (y2+50);
}


/**
 * Map overlay buffer address.
 *
 * @param format    pixel format
 * @param buf       Framebuffer address of surface (0 = start of framebuffer)
 * @param ox        leftmost pixel to show (used when clipping, else set to zero)
 * @param oy        topmost pixel to show (used when clipping, else set to zero)
 * @param w         total surface width (does *not* depend on the x parameter)
 * @param h         total surface height (does *not* depend on the y parameter)
 * @param pitch     source surface pitch (bytes per pixel)
 *
 * @param y_start   will hold start address of Y(UV) or RGB buffer
 * @param u_start   will hold start address of Cb buffer (planar modes only)
 * @param v_start   will hold start address of Cr buffer (planar modes only)
 *
 * @note Derived from VIA's V4L driver. See ddover.c,
 *       DDOver_GetSrcStartAddress() and DDOVer_GetYCbCrStartAddress()
 */
void uc_ovl_map_buffer(DFBSurfacePixelFormat format, u32 buf,
                       int ox, int oy, int sw, int sh, int sp, int field,
                       u32* y_start, u32* u_start, u32* v_start)
{
    int swap_cb_cr = 0;

    u32 tmp;
    u32 y_offset, uv_offset = 0;

    switch (format) {

    case DSPF_YUY2:
    case DSPF_UYVY:
        y_offset = ((oy * sp) + ((ox << 1) & ~15));
        break;

    case DSPF_YV12:
        swap_cb_cr = 1;
    case DSPF_I420:
        y_offset = ((((oy & ~3) * sp) + ox) & ~31) ;
        if (oy > 0)
            uv_offset = (((((oy & ~3) >> 1) * sp) + ox) & ~31) >> 1;
        else
            uv_offset = y_offset >> 1;
        break;

    case DSPF_ARGB1555:
    case DSPF_RGB16:
        y_offset = (oy * sp) + ((ox * 16) >> 3);
        break;

    case DSPF_RGB32:
    case DSPF_ARGB:
        y_offset = (oy * sp) + ((ox * 32) >> 3);
        break;

    default:
        y_offset = 0;
        uv_offset = 0;
        D_BUG("Unexpected pixelformat!");
    }

    if (field) {
         y_offset  += sp;
         uv_offset += sp >> 1;
    }

    *y_start = buf + y_offset;

    if (u_start && v_start) {
        *u_start = buf + sp * sh + uv_offset;
        *v_start = buf + sp * sh + sp * (sh >> 2) + uv_offset;

        if (swap_cb_cr) {
            tmp = *u_start;
            *u_start = *v_start;
            *v_start = tmp;
        }
    }
}


/**
 * Map alpha mode and opacity.
 *
 * @param opacity   Alpha opacity: 0 = transparent, 255 = opaque.
 *                  -1 = Use alpha from underlying graphics.
 *
 * @returns alpha control register setting.
 *
 * @note: Unfortunately, if using alpha from underlying graphics,
 *        the video is opaque if alpha = 255 and transparent if = 0.
 *        The inverse would have made more sense ...
 *
 * @note: The hardware supports a separate alpha plane as well,
 *        but it is not implemented here.
 *
 * @note: Derived from ddmpeg.c, VIAAlphaWin()
 */

u32 uc_ovl_map_alpha(int opacity)
{
    u32 ctrl = 0x00080000;      // Not sure what this number is, supposedly
                                // it is the "expire number divided by 4".

    if (opacity > 255) opacity = 255;

    if (opacity < 0) {
        ctrl |= ALPHA_WIN_BLENDING_GRAPHIC;
    }
    else {
        opacity = opacity >> 4; // Throw away bits 0 - 3
        ctrl |= (opacity << 12) | ALPHA_WIN_BLENDING_CONSTANT;
    }

    return ctrl; // V_ALPHA_CONTROL
}

/**
 * Calculate V1 control and fifo-control register values
 * @param format        pixel format
 * @param sw            source width
 * @param hwrev         CLE266 hardware revision
 * @param extfifo_on    set this true if the extended FIFO is enabled
 * @param control       will hold value for V1_CONTROL
 * @param fifo          will hold value for V1_FIFO_CONTROL
 */
void uc_ovl_map_v1_control(DFBSurfacePixelFormat format, int sw,
                           int hwrev, bool extfifo_on,
                           u32* control, u32* fifo)
{
    *control = V1_BOB_ENABLE | V1_ENABLE | uc_ovl_map_format(format);

    if (hwrev == 0x10) {
        *control |= V1_EXPIRE_NUM_F;
    }
    else {
        if (extfifo_on) {
            *control |= V1_EXPIRE_NUM_A | V1_FIFO_EXTENDED;
        }
        else {
            *control |= V1_EXPIRE_NUM;
        }
    }

    if ((format == DSPF_YV12) || (format == DSPF_I420)) {
        //Minified video will be skewed without this workaround.
        if (sw <= 80) { //Fetch count <= 5
            *fifo = UC_MAP_V1_FIFO_CONTROL(16,0,0);
        }
        else {
            if (hwrev == 0x10)
                *fifo = UC_MAP_V1_FIFO_CONTROL(64,56,56);
            else
                *fifo = UC_MAP_V1_FIFO_CONTROL(16,12,8);
        }
    }
    else {
        if (hwrev == 0x10) {
            *fifo = UC_MAP_V1_FIFO_CONTROL(64,56,56);   // Default rev 0x10
        }
        else {
            if (extfifo_on)
                *fifo = UC_MAP_V1_FIFO_CONTROL(48,40,40);
            else
                *fifo = UC_MAP_V1_FIFO_CONTROL(32,29,16);   // Default
        }
    }
}

/** uc_ovl_map_adjustment() helper - clamp x to [lo, hi] */
static float clamp(float x, float lo, float hi)
{
    return (x < lo) ? lo : ((x > hi) ? hi : x); /* 2 nested if's. */
}

/**
 * uc_ovl_map_adjustment() helper - format x for the hardware.
 *
 * @param x     The value to format.
 * @param ndec  Number of binary decimals.
 * @param sbit  sign bit position.
 *              =0: use two's complement representation
 *              >0: use a sign bit + positive value.
 * @param mask  Bitmask
 * @param shift Position in hardware register.
 */
static int fmt(float x, int ndec, int sbit, u32 mask, int shift)
{
    int y = (x * (1 << ndec));
    if (sbit && (y < 0)) y = -y | (1 << sbit);
    return (((u32) y) & mask) << shift;
}

/**
 * Map color adjustment to CLE266 hardware.
 *
 * @param adj   DirectFB color adjustment. All fields are assumed valid.
 * @param a1    Will hold value for V1_ColorSpaceReg_1
 * @param a2    Will hold value for V1_ColorSpaceReg_2
 */
void uc_ovl_map_adjustment(DFBColorAdjustment* adj, u32* a1, u32* a2)
{
    float con, sat, bri, hue;
    float c, s;
    float A, B1, C1, D, B2, C2, B3, C3;

    // Map contrast to [0, 2.0] (preferred: [0, 1.66]), default: 1.0.
    con = (float) adj->contrast / 32768.0;
    // Map saturation to [0, 2.0], default: 1.0.
    sat = (float) adj->saturation / 32768.0;
    // Map brightness to [-121, 125], (preferred: [-94, 125.1]), default: 3.97.
    bri = (float) (adj->brightness - 31696) / 270.48;
    // Map hue to [-pi, pi], default is 0.0.
    hue = (float) (adj->hue - 32768) / 10430.378;
    // Note: The limits are estimates that need testing.

    // Map parameters to hw registers.

    s = sin(hue) * con * sat;
    c = cos(hue) * con * sat;

    A = clamp(1.164*con, 0, 1.9375);
    B1 = clamp(-1.596*s, -0.75, 0.75);
    C1 = clamp(1.596*c, 1, 2.875);
    B2 = clamp( (0.813*s - 0.391*c), 0, -0.875);
    C2 = clamp(-(0.813*c + 0.391*s), 0, -1.875);
    B3 = clamp(2.018*c, 0, 3.75);
    C3 = clamp(2.018*s, -1.25, 1.25);
    D = clamp(1.164*(bri-16), -128, 127);

    *a1 =
        fmt(A,  4, 0, 0x1f, 24) | fmt(B1, 2, 2, 0x07, 18) |
        fmt(C1, 3, 0, 0x1f,  9) | fmt(D,  0, 0, 0xff,  0);

    *a2 =
        fmt(B2, 3, 4, 0x7, 25) | fmt(C2, 3, 4, 0xf, 17) |
        fmt(B3, 2, 0, 0xf, 10) | fmt(C3, 2, 3, 0xf,  2);
}