crop_rec.c 64.2 KB
Newer Older
1
2
3
4
5
6
7
8
#include <dryos.h>
#include <module.h>
#include <config.h>
#include <menu.h>
#include <beep.h>
#include <property.h>
#include <patch.h>
#include <bmp.h>
9
#include <lvinfo.h>
10
#include <powersave.h>
11
#include <raw.h>
12
#include <fps.h>
13
#include <shoot.h>
14

15
16
17
18
19
20
21
22
#undef CROP_DEBUG

#ifdef CROP_DEBUG
#define dbg_printf(fmt,...) { printf(fmt, ## __VA_ARGS__); }
#else
#define dbg_printf(fmt,...) {}
#endif

23
static int is_5D3 = 0;
24
static int is_6D = 0;
25
static int is_basic = 0;
26
27

static CONFIG_INT("crop.preset", crop_preset_index, 0);
28
static CONFIG_INT("crop.shutter_range", shutter_range, 0);
29
30
31
32

enum crop_preset {
    CROP_PRESET_OFF = 0,
    CROP_PRESET_3X,
33
    CROP_PRESET_3X_TALL,
34
    CROP_PRESET_3K,
35
36
    CROP_PRESET_4K_HFPS,
    CROP_PRESET_UHD,
37
    CROP_PRESET_FULLRES_LV,
38
    CROP_PRESET_3x3_1X,
39
    CROP_PRESET_3x3_1X_48p,
40
41
    CROP_PRESET_1x3,
    CROP_PRESET_3x1,
42
    CROP_PRESET_40_FPS,
43
    CROP_PRESET_CENTER_Z,
44
    NUM_CROP_PRESETS
45
};
46
47

/* presets are not enabled right away (we need to go to play mode and back)
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 * so we keep two variables: what's selected in menu and what's actually used.
 * note: the menu choices are camera-dependent */
static enum crop_preset crop_preset = 0;

/* must be assigned in crop_rec_init */
static enum crop_preset * crop_presets = 0;

/* current menu selection (*/
#define CROP_PRESET_MENU crop_presets[crop_preset_index]

/* menu choices for 5D3 */
static enum crop_preset crop_presets_5d3[] = {
    CROP_PRESET_OFF,
    CROP_PRESET_3X,
62
    CROP_PRESET_3X_TALL,
63
64
    CROP_PRESET_3x3_1X,
    CROP_PRESET_3x3_1X_48p,
65
    CROP_PRESET_3K,
66
67
    CROP_PRESET_UHD,
    CROP_PRESET_4K_HFPS,
68
    CROP_PRESET_CENTER_Z,
69
    CROP_PRESET_FULLRES_LV,
alex@thinkpad's avatar
alex@thinkpad committed
70
  //CROP_PRESET_1x3,
71
  //CROP_PRESET_3x1,
alex@thinkpad's avatar
alex@thinkpad committed
72
  //CROP_PRESET_40_FPS,
73
74
75
76
};

static const char * crop_choices_5d3[] = {
    "OFF",
alex@thinkpad's avatar
alex@thinkpad committed
77
    "1920 1:1",
78
    "1920 1:1 tall",
alex@thinkpad's avatar
alex@thinkpad committed
79
    "1920 50/60 3x3",
80
    "1080p45/1040p48 3x3",
81
82
83
    "3K 1:1",
    "UHD 1:1",
    "4K 1:1 half-fps",
84
    "3.5K 1:1 centered x5",
85
    "Full-res LiveView",
alex@thinkpad's avatar
alex@thinkpad committed
86
  //"1x3 binning",
87
  //"3x1 binning",      /* doesn't work well */
alex@thinkpad's avatar
alex@thinkpad committed
88
  //"40 fps",
89
90
91
92
};

static const char crop_choices_help_5d3[] =
    "Change 1080p and 720p movie modes into crop modes (select one)";
93

94
95
static const char crop_choices_help2_5d3[] =
    "\n"
96
    "1:1 sensor readout (square raw pixels, 3x crop, good preview in 1080p)\n"
97
98
    "1:1 crop, higher vertical resolution (1920x1920 @ 24p, cropped preview)\n"
    "1920x960 @ 50p, 1920x800 @ 60p (3x3 binning, cropped preview)\n"
99
    "1920x1080 @ 45p, 1920x1040 @ 48p, 3x3 binning (50/60 FPS in Canon menu)\n"
100
    "1:1 3K crop (3072x1920 @ 24p, square raw pixels, preview broken)\n"
101
    "1:1 4K UHD crop (3840x1600 @ 24p, square raw pixels, preview broken)\n"
102
    "1:1 4K crop (4096x3072 @ 12.5 fps, half frame rate, preview broken)\n"
103
    "1:1 readout in x5 zoom mode (centered raw, high res, cropped preview)\n"
104
    "Full resolution LiveView (5796x3870 @ 7.4 fps, 5784x3864, preview broken)\n"
105
    "1x3 binning: read all lines, bin every 3 columns (extreme anamorphic)\n"
106
107
    "3x1 binning: bin every 3 lines, read all columns (extreme anamorphic)\n"
    "FPS override test\n";
108

109
110
/* menu choices for cameras that only have the basic 3x3 crop_rec option */
static enum crop_preset crop_presets_basic[] = {
111
112
113
114
    CROP_PRESET_OFF,
    CROP_PRESET_3x3_1X,
};

115
static const char * crop_choices_basic[] = {
116
117
118
119
    "OFF",
    "3x3 720p",
};

120
static const char crop_choices_help_basic[] =
Daniel Fort's avatar
Daniel Fort committed
121
122
    "Change 1080p and 720p movie modes into crop modes (one choice)";

123
124
static const char crop_choices_help2_basic[] =
    "3x3 binning in 720p (square pixels in RAW, vertical crop)";
125
126


127
128
129
130
131
/* camera-specific parameters */
static uint32_t CMOS_WRITE      = 0;
static uint32_t MEM_CMOS_WRITE  = 0;
static uint32_t ADTG_WRITE      = 0;
static uint32_t MEM_ADTG_WRITE  = 0;
132
133
static uint32_t ENGIO_WRITE     = 0;
static uint32_t MEM_ENGIO_WRITE = 0;
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* video modes */
/* note: zoom mode is identified by checking registers directly */

static int is_1080p()
{
    /* note: on 5D2 and 5D3 (maybe also 6D, not sure),
     * sensor configuration in photo mode is identical to 1080p.
     * other cameras may be different */
    return !is_movie_mode() || video_mode_resolution == 0;
}

static int is_720p()
{
    return is_movie_mode() && video_mode_resolution == 1;
}

static int is_supported_mode()
{
    if (!lv) return 0;
154
155
156

    switch (crop_preset)
    {
157
158
        /* note: zoom check is also covered by check_cmos_vidmode */
        /* (we need to apply CMOS settings before PROP_LV_DISPSIZE fires) */
159
        case CROP_PRESET_CENTER_Z:
160
            return 1;
161
162
163
164

        default:
            return is_1080p() || is_720p();
    }
165
166
}

167
static int32_t  target_yres = 0;
alex@thinkpad's avatar
alex@thinkpad committed
168
169
170
171
static int32_t  delta_adtg0 = 0;
static int32_t  delta_adtg1 = 0;
static int32_t  delta_head3 = 0;
static int32_t  delta_head4 = 0;
172
static uint32_t cmos1_lo = 0, cmos1_hi = 0;
173
static uint32_t cmos2 = 0;
174

175
176
/* helper to allow indexing various properties of Canon's video modes */
static inline int get_video_mode_index()
177
{
178
179
180
181
182
    if (lv_dispsize > 1)
    {
        return 5;
    }

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    return
        (video_mode_fps == 24) ?  0 :
        (video_mode_fps == 25) ?  1 :
        (video_mode_fps == 30) ?  2 :
        (video_mode_fps == 50) ?  3 :
     /* (video_mode_fps == 60) */ 4 ;
}

/* optical black area sizes */
/* not sure how to adjust them from registers, so... hardcode them here */
static inline void FAST calc_skip_offsets(int * p_skip_left, int * p_skip_right, int * p_skip_top, int * p_skip_bottom)
{
    /* start from LiveView values */
    int skip_left       = 146;
    int skip_right      = 2;
    int skip_top        = 28;
    int skip_bottom     = 0;
200
201
202

    switch (crop_preset)
    {
203
204
205
206
207
208
        case CROP_PRESET_FULLRES_LV:
            /* photo mode values */
            skip_left       = 138;
            skip_right      = 2;
            skip_top        = 60;   /* fixme: this is different, why? */
            break;
209
210

        case CROP_PRESET_3K:
211
        case CROP_PRESET_UHD:
212
        case CROP_PRESET_4K_HFPS:
213
214
215
216
217
218
            skip_right      = 0;    /* required for 3840 - tight fit */
            /* fall-through */
        
        case CROP_PRESET_3X_TALL:
            skip_top        = 30;
            break;
219

220
221
222
223
        case CROP_PRESET_3X:
        case CROP_PRESET_1x3:
            skip_top        = 60;
            break;
224
225
226
227
228

        case CROP_PRESET_3x3_1X:
        case CROP_PRESET_3x3_1X_48p:
            if (is_720p()) skip_top = 0;
            break;
229
    }
230

231
232
233
234
235
236
    if (p_skip_left)   *p_skip_left    = skip_left;
    if (p_skip_right)  *p_skip_right   = skip_right;
    if (p_skip_top)    *p_skip_top     = skip_top;
    if (p_skip_bottom) *p_skip_bottom  = skip_bottom;
}

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/* to be in sync with 0xC0F06800 */
static int get_top_bar_adjustment()
{
    switch (crop_preset)
    {
        case CROP_PRESET_FULLRES_LV:
            return 0;                   /* 0x10018: photo mode value, unchanged */
        case CROP_PRESET_3x3_1X:
        case CROP_PRESET_3x3_1X_48p:
            if (is_720p()) return 28;   /* 0x1D0017 from 0x10017 */
        default:
            return 30;                  /* 0x1F0017 from 0x10017 */
    }
}

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/* Vertical resolution from current unmodified video mode */
/* (active area only, as seen by mlv_lite) */
static inline int get_default_yres()
{
    return 
        (video_mode_fps <= 30) ? 1290 : 672;
}

/* skip_top from unmodified video mode (raw.c, LiveView skip offsets) */
static inline int get_default_skip_top()
{
    return 
        (video_mode_fps <= 30) ? 28 : 20;
}

/* max resolution for each video mode (trial and error) */
268
269
/* it's usually possible to push the numbers a few pixels further,
 * at the risk of corrupted frames */
270
271
272
273
274
275
276
277
278
static int max_resolutions[NUM_CROP_PRESETS][6] = {
                                /*   24p   25p   30p   50p   60p   x5 */
    [CROP_PRESET_3X_TALL]       = { 1920, 1728, 1536,  960,  800, 1320 },
    [CROP_PRESET_3x3_1X]        = { 1290, 1290, 1290,  960,  800, 1320 },
    [CROP_PRESET_3x3_1X_48p]    = { 1290, 1290, 1290, 1080, 1040, 1320 }, /* 1080p45/48 */
    [CROP_PRESET_3K]            = { 1920, 1728, 1504,  760,  680, 1320 },
    [CROP_PRESET_UHD]           = { 1536, 1472, 1120,  640,  540, 1320 },
    [CROP_PRESET_4K_HFPS]       = { 3072, 3072, 2500, 1440, 1200, 1320 },
    [CROP_PRESET_FULLRES_LV]    = { 3870, 3870, 3870, 3870, 3870, 1320 },
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
};

/* 5D3 vertical resolution increments over default configuration */
/* note that first scanline may be moved down by 30 px (see reg_override_top_bar) */
static inline int FAST calc_yres_delta()
{
    int desired_yres = (target_yres) ? target_yres
        : max_resolutions[crop_preset][get_video_mode_index()];

    if (desired_yres)
    {
        /* user override */
        int skip_top;
        calc_skip_offsets(0, 0, &skip_top, 0);
        int default_yres = get_default_yres();
        int default_skip_top = get_default_skip_top();
295
        int top_adj = get_top_bar_adjustment();
296
        return desired_yres - default_yres + skip_top - default_skip_top + top_adj;
297
298
299
300
301
302
303
    }

    ASSERT(0);
    return 0;
}

#define YRES_DELTA calc_yres_delta()
304

305
306


307
static int cmos_vidmode_ok = 0;
308
309
310
311
312
313

/* return value:
 *  1: registers checked and appear OK (1080p/720p video mode)
 *  0: registers checked and they are not OK (other video mode)
 * -1: registers not checked
 */
314
static int FAST check_cmos_vidmode(uint16_t* data_buf)
315
316
317
318
319
320
321
322
{
    int ok = 1;
    int found = 1;
    while (*data_buf != 0xFFFF)
    {
        int reg = (*data_buf) >> 12;
        int value = (*data_buf) & 0xFFF;
        
323
        if (is_5D3)
324
        {
325
            if (reg == 1)
326
            {
327
                found = 1;
328
329

                switch (crop_preset)
330
                {
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
                    case CROP_PRESET_CENTER_Z:
                    {
                        /* detecting the zoom mode is tricky;
                         * let's just exclude 1080p and 720p for now ... */
                        if (value == 0x800 ||
                            value == 0xBC2)
                        {
                            ok = 0;
                        }
                        break;
                    }

                    default:
                    {
                        if (value != 0x800 &&   /* not 1080p? */
                            value != 0xBC2)     /* not 720p? */
                        {
                            ok = 0;
                        }
                        break;
                    }
352
                }
353
354
            }
        }
355
        
356
        if (is_basic && !is_6D)
357
358
359
360
361
362
363
364
365
366
367
368
        {
            if (reg == 7)
            {
                found = 1;
                /* prevent running in 600D hack crop mode */
                if (value != 0x800) 
                {
                    ok = 0;
                }
            }
        }
        
369
370
371
372
373
374
375
376
        data_buf++;
    }
    
    if (found) return ok;
    
    return -1;
}

377
378
/* pack two 6-bit values into a 12-bit one */
#define PACK12(lo,hi) ((((lo) & 0x3F) | ((hi) << 6)) & 0xFFF)
379

380
381
382
/* pack two 16-bit values into a 32-bit one */
#define PACK32(lo,hi) (((uint32_t)(lo) & 0xFFFF) | ((uint32_t)(hi) << 16))

383
384
385
/* pack two 16-bit values into a 32-bit one */
#define PACK32(lo,hi) (((uint32_t)(lo) & 0xFFFF) | ((uint32_t)(hi) << 16))

386
static void FAST cmos_hook(uint32_t* regs, uint32_t* stack, uint32_t pc)
387
388
{
    /* make sure we are in 1080p/720p mode */
389
    if (!is_supported_mode())
390
391
392
393
394
395
396
397
398
399
400
401
402
403
    {
        /* looks like checking properties works fine for detecting
         * changes in video mode, but not for detecting the zoom change */
        return;
    }
    
    /* also check CMOS registers; in zoom mode, we get different values
     * and this check is instant (no delays).
     * 
     * on 5D3, the 640x480 acts like 1080p during standby,
     * so properties are our only option for that one.
     */
     
    uint16_t* data_buf = (uint16_t*) regs[0];
404
    int ret = check_cmos_vidmode(data_buf);
405
406
407
    
    if (ret >= 0)
    {
408
        cmos_vidmode_ok = ret;
409
410
411
412
413
414
415
    }
    
    if (ret != 1)
    {
        return;
    }

416
    int cmos_new[10] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
417
418
419
    
    if (is_5D3)
    {
420
421
422
423
424
425
        switch (crop_preset)
        {
            /* 1:1 (3x) */
            case CROP_PRESET_3X:
                /* start/stop scanning line, very large increments */
                /* note: these are two values, 6 bit each, trial and error */
426
                cmos_new[1] = (is_720p())
427
                    ? PACK12(13,10)     /* 720p,  almost centered */
428
429
430
431
432
433
                    : PACK12(11,11);    /* 1080p, almost centered */
                
                cmos_new[2] = 0x10E;    /* read every column, centered crop */
                cmos_new[6] = 0x170;    /* pink highlights without this */
                break;
            
434
            case CROP_PRESET_3X_TALL:
435
436
437
438
                cmos_new[1] =           /* vertical centering (trial and error) */
                    (video_mode_fps == 24) ? PACK12(8,13)  :
                    (video_mode_fps == 25) ? PACK12(8,12)  :
                    (video_mode_fps == 30) ? PACK12(9,11)  :
439
440
                    (video_mode_fps == 50) ? PACK12(12,10) :
                    (video_mode_fps == 60) ? PACK12(13,10) :
441
                                             (uint32_t) -1 ;
442
443
444
445
                cmos_new[2] = 0x10E;    /* horizontal centering (trial and error) */
                cmos_new[6] = 0x170;    /* pink highlights without this */
                break;

446
447
448
449
450
451
452
453
454
455
456
457
            /* 3x3 binning in 720p */
            /* 1080p it's already 3x3, don't change it */
            case CROP_PRESET_3x3_1X:
            case CROP_PRESET_3x3_1X_48p:
                if (is_720p())
                {
                    /* start/stop scanning line, very large increments */
                    cmos_new[1] =
                        (crop_preset == CROP_PRESET_3x3_1X_48p) ? PACK12(3,15) :
                        (video_mode_fps == 50)                  ? PACK12(4,14) :
                        (video_mode_fps == 60)                  ? PACK12(6,14) :
                                                                 (uint32_t) -1 ;
458
                    cmos_new[6] = 0x370;    /* pink highlights without this */
459
460
461
                }
                break;

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
            case CROP_PRESET_3K:
                cmos_new[1] =           /* vertical centering (trial and error) */
                    (video_mode_fps == 24) ? PACK12(8,12)  :
                    (video_mode_fps == 25) ? PACK12(8,12)  :
                    (video_mode_fps == 30) ? PACK12(9,11)  :
                    (video_mode_fps == 50) ? PACK12(13,10) :
                    (video_mode_fps == 60) ? PACK12(14,10) :    /* 13,10 has better centering, but overflows */
                                             (uint32_t) -1 ;
                cmos_new[2] = 0x0BE;    /* horizontal centering (trial and error) */
                cmos_new[6] = 0x170;    /* pink highlights without this */
                break;

            case CROP_PRESET_UHD:
                cmos_new[1] =
                    (video_mode_fps == 24) ? PACK12(4,9)  :
                    (video_mode_fps == 25) ? PACK12(4,9)  :
                    (video_mode_fps == 30) ? PACK12(5,8)  :
                    (video_mode_fps == 50) ? PACK12(12,9) :
                    (video_mode_fps == 60) ? PACK12(13,9) :
                                            (uint32_t) -1 ;
                cmos_new[2] = 0x08E;    /* horizontal centering (trial and error) */
                cmos_new[6] = 0x170;    /* pink highlights without this */
                break;

            case CROP_PRESET_4K_HFPS:
                cmos_new[1] =
                    (video_mode_fps == 24) ? PACK12(4,15)  :
                    (video_mode_fps == 25) ? PACK12(4,15)  :
                    (video_mode_fps == 30) ? PACK12(6,14)  :
                    (video_mode_fps == 50) ? PACK12(10,11) :
                    (video_mode_fps == 60) ? PACK12(12,11) :
493
                                             (uint32_t) -1 ;
494
                cmos_new[2] = 0x07E;    /* horizontal centering (trial and error) */
495
496
                cmos_new[6] = 0x170;    /* pink highlights without this */
                break;
497
498
499

            case CROP_PRESET_FULLRES_LV:
                cmos_new[1] = 0x800;    /* from photo mode */
500
                cmos_new[2] = 0x00E;    /* 8 in photo mode; E enables shutter speed control from ADTG 805E */
501
502
503
                cmos_new[6] = 0x170;    /* pink highlights without this */
                break;

504
505
506
            /* 1x3 binning (read every line, bin every 3 columns) */
            case CROP_PRESET_1x3:
                /* start/stop scanning line, very large increments */
507
                cmos_new[1] = (is_720p())
508
509
510
511
512
513
514
515
516
517
                    ? PACK12(14,10)     /* 720p,  almost centered */
                    : PACK12(11,11);    /* 1080p, almost centered */
                
                cmos_new[6] = 0x170;    /* pink highlights without this */
                break;

            /* 3x1 binning (bin every 3 lines, read every column) */
            case CROP_PRESET_3x1:
                cmos_new[2] = 0x10E;    /* read every column, centered crop */
                break;
518
519
520
521
522
523

            /* raw buffer centered in zoom mode */
            case CROP_PRESET_CENTER_Z:
                cmos_new[1] = PACK12(9+2,42+1); /* vertical (first|last) */
                cmos_new[2] = 0x09E;            /* horizontal offset (mask 0xFF0) */
                break;
524
        }
525
    }
526

527
    if (is_basic)
528
529
530
531
532
    {
        switch (crop_preset)
        {
            case CROP_PRESET_3x3_1X:
                /* start/stop scanning line, very large increments */
533
                cmos_new[7] = (is_6D) ? PACK12(37,10) : PACK12(6,29);
534
535
536
537
                break;            
        }
    }

538
539
540
541
542
    /* menu overrides */
    if (cmos1_lo || cmos1_hi)
    {
        cmos_new[1] = PACK12(cmos1_lo,cmos1_hi);
    }
543
544
545
546
547

    if (cmos2)
    {
        cmos_new[2] = cmos2;
    }
548
549
550
551
552
553
554
555
556
557
558
    
    /* copy data into a buffer, to make the override temporary */
    /* that means: as soon as we stop executing the hooks, values are back to normal */
    static uint16_t copy[512];
    uint16_t* copy_end = &copy[COUNT(copy)];
    uint16_t* copy_ptr = copy;

    while (*data_buf != 0xFFFF)
    {
        *copy_ptr = *data_buf;

559
560
        int reg = (*data_buf) >> 12;
        if (cmos_new[reg] != -1)
561
        {
562
563
            *copy_ptr = (reg << 12) | cmos_new[reg];
            dbg_printf("CMOS[%x] = %x\n", reg, cmos_new[reg]);
564
        }
565

566
567
568
569
570
571
572
        data_buf++;
        copy_ptr++;
        if (copy_ptr > copy_end) while(1);
    }
    *copy_ptr = 0xFFFF;

    /* pass our modified register list to cmos_write */
573
    regs[0] = (uint32_t) copy;
574
575
}

576
577
578
579
static uint32_t nrzi_encode( uint32_t in_val )
{
    uint32_t out_val = 0;
    uint32_t old_bit = 0;
580
    for (int num = 0; num < 31; num++)
581
582
583
584
585
586
587
588
589
    {
        uint32_t bit = in_val & 1<<(30-num) ? 1 : 0;
        if (bit != old_bit)
            out_val |= (1 << (30-num));
        old_bit = bit;
    }
    return out_val;
}

590
591
592
593
594
595
596
597
598
599
600
601
602
static uint32_t nrzi_decode( uint32_t in_val )
{
    uint32_t val = 0;
    if (in_val & 0x8000)
        val |= 0x8000;
    for (int num = 0; num < 31; num++)
    {
        uint32_t old_bit = (val & 1<<(30-num+1)) >> 1;
        val |= old_bit ^ (in_val & 1<<(30-num));
    }
    return val;
}

603
604
605
606
607
608
609
610
611
612
613
614
615
static int FAST adtg_lookup(uint32_t* data_buf, int reg_needle)
{
    while(*data_buf != 0xFFFFFFFF)
    {
        int reg = (*data_buf) >> 16;
        if (reg == reg_needle)
        {
            return *(uint16_t*)data_buf;
        }
    }
    return -1;
}

616
617
/* from SENSOR_TIMING_TABLE (fps-engio.c) */
/* hardcoded for 5D3 */
618
619
620
const int default_timerA[] = { 0x1B8, 0x1E0, 0x1B8, 0x1E0, 0x1B8, 0x206 };
const int default_timerB[] = { 0x8E3, 0x7D0, 0x71C, 0x3E8, 0x38E, 0x614 };
const int default_fps_1k[] = { 23976, 25000, 29970, 50000, 59940, 29776 };
621

622
623
624
625
626
627
628
629
/* adapted from fps_override_shutter_blanking in fps-engio.c */
static int adjust_shutter_blanking(int old)
{
    /* sensor duty cycle: range 0 ... timer B */
    int current_blanking = nrzi_decode(old);

    int video_mode = get_video_mode_index();

630
    /* what value Canon firmware assumes for timer B? */
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
    int fps_timer_b_orig = default_timerB[video_mode];

    int current_exposure = fps_timer_b_orig - current_blanking;
    
    /* wrong assumptions? */
    if (current_exposure < 0)
    {
        return old;
    }

    int default_fps = default_fps_1k[video_mode];
    int current_fps = fps_get_current_x1000();

    dbg_printf("FPS %d->%d\n", default_fps, current_fps);

    float frame_duration_orig = 1000.0 / default_fps;
    float frame_duration_current = 1000.0 / current_fps;

    float orig_shutter = frame_duration_orig * current_exposure / fps_timer_b_orig;
650
651

    float new_shutter =
652
        (shutter_range == 0) ?
653
        ({
654
            /* original shutter speed from the altered video mode */
655
656
657
            orig_shutter;
        }) :
        ({
658
659
            /* map the available range of 1/4000...1/30 (24-30p) or 1/4000...1/60 (50-60p)
             * from minimum allowed (1/15000 with full-res LV) to 1/fps */
660
661
662
663
            int max_fps_shutter = (video_mode_fps <= 30) ? 33333 : 64000;
            int default_fps_adj = 1e9 / (1e9 / max_fps_shutter - 250);
            (orig_shutter - 250e-6) * default_fps_adj / current_fps;
        });
664

665
666
    /* what value is actually used for timer B? (possibly after our overrides) */
    int fps_timer_b = shamem_read(0xC0F06014) & 0xFFFF;
667
668
669
670

    dbg_printf("Timer B %d->%d\n", fps_timer_b_orig, fps_timer_b);

    int new_exposure = new_shutter * fps_timer_b / frame_duration_current;
671
    int new_blanking = COERCE(fps_timer_b - new_exposure, 10, fps_timer_b - 2);
672
673
674
675
676
677
678
679
680
681
682
683
684

    dbg_printf("Exposure %d->%d (timer B units)\n", current_exposure, new_exposure);

#ifdef CROP_DEBUG
    float chk_shutter = frame_duration_current * new_exposure / fps_timer_b;
    dbg_printf("Shutter %d->%d us\n", (int)(orig_shutter*1e6), (int)(chk_shutter*1e6));
#endif

    dbg_printf("Blanking %d->%d\n", current_blanking, new_blanking);

    return nrzi_encode(new_blanking);
}

685
686
extern WEAK_FUNC(ret_0) void fps_override_shutter_blanking();

687
static void FAST adtg_hook(uint32_t* regs, uint32_t* stack, uint32_t pc)
688
{
689
    if (!is_supported_mode() || !cmos_vidmode_ok)
690
691
692
693
    {
        /* don't patch other video modes */
        return;
    }
694
695
696
697
698
699
700
701
702
703
704

    if (is_5D3 && !is_720p())
    {
        if (crop_preset == CROP_PRESET_3x3_1X ||
            crop_preset == CROP_PRESET_3x3_1X_48p)
        {
            /* these presets only have effect in 720p mode */
            return;
        }
    }

705
706
707
708
    /* This hook is called from the DebugMsg's in adtg_write,
     * so if we change the register list address, it won't be able to override them.
     * Workaround: let's call it here. */
    fps_override_shutter_blanking();
709
710
711
712
713
714
715
716
717
718
719

    uint32_t cs = regs[0];
    uint32_t *data_buf = (uint32_t *) regs[1];
    int dst = cs & 0xF;
    
    /* copy data into a buffer, to make the override temporary */
    /* that means: as soon as we stop executing the hooks, values are back to normal */
    static uint32_t copy[512];
    uint32_t* copy_end = &copy[COUNT(copy)];
    uint32_t* copy_ptr = copy;
    
720
721
722
723
724
725
726
727
    struct adtg_new
    {
        int dst;
        int reg;
        int val;
    };
    
    /* expand this as required */
728
    struct adtg_new adtg_new[10] = {{0}};
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744

    /* scan for shutter blanking and make both zoom and non-zoom value equal */
    /* (the values are different when using FPS override with ADTG shutter override) */
    /* (fixme: might be better to handle this in ML core?) */
    int shutter_blanking = 0;
    int adtg_blanking_reg = (lv_dispsize == 1) ? 0x8060 : 0x805E;
    for (uint32_t * buf = data_buf; *buf != 0xFFFFFFFF; buf++)
    {
        int reg = (*buf) >> 16;
        if (reg == adtg_blanking_reg)
        {
            int val = (*buf) & 0xFFFF;
            shutter_blanking = val;
        }
    }

745
746
747
    /* some modes may need adjustments to maintain exposure */
    if (shutter_blanking)
    {
748
749
750
751
752
753
        /* FIXME: remove this kind of hardcoded conditions */
        if ((crop_preset == CROP_PRESET_CENTER_Z && lv_dispsize != 1) ||
            (crop_preset != CROP_PRESET_CENTER_Z && lv_dispsize == 1))
        {
            shutter_blanking = adjust_shutter_blanking(shutter_blanking);
        }
754
755
    }

756
    if (is_5D3)
757
    {
758
759
760
761
762
763
        /* all modes may want to override shutter speed */
        /* ADTG[0x8060]: shutter blanking for 3x3 mode  */
        /* ADTG[0x805E]: shutter blanking for zoom mode  */
        adtg_new[0] = (struct adtg_new) {6, 0x8060, shutter_blanking};
        adtg_new[1] = (struct adtg_new) {6, 0x805E, shutter_blanking};

764
765
        switch (crop_preset)
        {
766
            /* all 1:1 modes (3x, 3K, 4K...) */
767
            case CROP_PRESET_3X:
768
            case CROP_PRESET_3X_TALL:
769
            case CROP_PRESET_3K:
770
            case CROP_PRESET_UHD:
771
            case CROP_PRESET_4K_HFPS:
772
            case CROP_PRESET_FULLRES_LV:
773
774
                /* ADTG2/4[0x8000] = 5 (set in one call) */
                /* ADTG2[0x8806] = 0x6088 (artifacts without it) */
775
776
                adtg_new[2] = (struct adtg_new) {6, 0x8000, 5};
                adtg_new[3] = (struct adtg_new) {2, 0x8806, 0x6088};
777
778
                break;

779
780
            /* 3x3 binning in 720p (in 1080p it's already 3x3) */
            case CROP_PRESET_3x3_1X:
781
            case CROP_PRESET_3x3_1X_48p:
782
                /* ADTG2/4[0x800C] = 2: vertical binning factor = 3 */
783
                adtg_new[2] = (struct adtg_new) {6, 0x800C, 2};
784
785
786
787
788
                break;

            /* 1x3 binning (read every line, bin every 3 columns) */
            case CROP_PRESET_1x3:
                /* ADTG2/4[0x800C] = 0: read every line */
789
                adtg_new[2] = (struct adtg_new) {6, 0x800C, 0};
790
791
792
793
794
795
796
                break;

            /* 3x1 binning (bin every 3 lines, read every column) */
            /* doesn't work well, figure out why */
            case CROP_PRESET_3x1:
                /* ADTG2/4[0x800C] = 2: vertical binning factor = 3 */
                /* ADTG2[0x8806] = 0x6088 (artifacts worse without it) */
797
798
                adtg_new[2] = (struct adtg_new) {6, 0x800C, 2};
                adtg_new[3] = (struct adtg_new) {2, 0x8806, 0x6088};
799
                break;
800
        }
801

802
803
804
805
806
807
808
809
810
811
812
        /* all modes with higher vertical resolution */
        switch (crop_preset)
        {
            case CROP_PRESET_3X_TALL:
            case CROP_PRESET_3x3_1X:
            case CROP_PRESET_3x3_1X_48p:
            case CROP_PRESET_3K:
            case CROP_PRESET_UHD:
            case CROP_PRESET_4K_HFPS:
            case CROP_PRESET_FULLRES_LV:
                /* adjust vertical resolution */
813
814
815
                adtg_new[4] = (struct adtg_new) {6, 0x8178, nrzi_encode(0x529 + YRES_DELTA + delta_adtg0)};
                adtg_new[5] = (struct adtg_new) {6, 0x8196, nrzi_encode(0x529 + YRES_DELTA + delta_adtg0)};
                adtg_new[6] = (struct adtg_new) {6, 0x82F8, nrzi_encode(0x528 + YRES_DELTA + delta_adtg0)};
816
817
818
819
820
821
822
823
824
                break;
        }

        /* some modes require additional height adjustments */
        switch (crop_preset)
        {
            case CROP_PRESET_FULLRES_LV:
            case CROP_PRESET_UHD:
            case CROP_PRESET_4K_HFPS:
825
            case CROP_PRESET_40_FPS:
826
827
828
                /* the following are required for breaking the ~2100px barrier */
                /* (0x891, 0x891, 0x8E2 at 24p; lower values affect bottom lines) */
                /* see also http://www.magiclantern.fm/forum/index.php?topic=11965 */
829
830
831
                adtg_new[7] = (struct adtg_new) {6, 0x8179, nrzi_encode(0x535 + YRES_DELTA + delta_adtg1)};
                adtg_new[8] = (struct adtg_new) {6, 0x8197, nrzi_encode(0x535 + YRES_DELTA + delta_adtg1)};
                adtg_new[9] = (struct adtg_new) {6, 0x82F9, nrzi_encode(0x580 + YRES_DELTA + delta_adtg1)};
832
                break;
833
        }
834

835
    }
836

837
    if (is_basic)
838
839
840
841
842
843
844
845
846
847
848
    {
        switch (crop_preset)
        {
            /* 3x3 binning in 720p (in 1080p it's already 3x3) */
            case CROP_PRESET_3x3_1X:
                /* ADTG2/4[0x800C] = 2: vertical binning factor = 3 */
                adtg_new[0] = (struct adtg_new) {6, 0x800C, 2};
                break;
        }
    }

849
850
851
852
    while(*data_buf != 0xFFFFFFFF)
    {
        *copy_ptr = *data_buf;
        int reg = (*data_buf) >> 16;
853
        for (int i = 0; i < COUNT(adtg_new); i++)
854
        {
855
856
857
858
859
            if ((reg == adtg_new[i].reg) && (dst & adtg_new[i].dst))
            {
                int new_value = adtg_new[i].val;
                dbg_printf("ADTG%x[%x] = %x\n", dst, reg, new_value);
                *(uint16_t*)copy_ptr = new_value;
860
861
862
863
864
865
866

                if (reg == 0x805E || reg == 0x8060)
                {
                    /* also override in original data structure */
                    /* to be picked up on the screen indicators */
                    *(uint16_t*)data_buf = new_value;
                }
867
            }
868
869
870
871
872
873
874
875
        }
        data_buf++;
        copy_ptr++;
        if (copy_ptr >= copy_end) while(1);
    }
    *copy_ptr = 0xFFFFFFFF;
    
    /* pass our modified register list to adtg_write */
876
    regs[1] = (uint32_t) copy;
877
878
}

879
880
881
882
883
884
885
/* this is used to cover the black bar at the top of the image in 1:1 modes */
/* (used in most other presets) */
static inline uint32_t reg_override_top_bar(uint32_t reg, uint32_t old_val)
{
    switch (reg)
    {
        /* raw start line/column */
886
        /* move start line down by 30 pixels */
887
888
        /* not sure where this offset comes from */
        case 0xC0F06800:
889
            return 0x1F0017;
890
891
892
893
894
895
896
897
898
899
900
901
902
    }

    return 0;
}

/* these are required for increasing vertical resolution */
/* (used in most other presets) */
static inline uint32_t reg_override_HEAD34(uint32_t reg, uint32_t old_val)
{
    switch (reg)
    {
        /* HEAD3 timer */
        case 0xC0F0713C:
alex@thinkpad's avatar
alex@thinkpad committed
903
            return old_val + YRES_DELTA + delta_head3;
904
905
906

        /* HEAD4 timer */
        case 0xC0F07150:
alex@thinkpad's avatar
alex@thinkpad committed
907
            return old_val + YRES_DELTA + delta_head4;
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
    }

    return 0;
}

static inline uint32_t reg_override_common(uint32_t reg, uint32_t old_val)
{
    uint32_t a = reg_override_top_bar(reg, old_val);
    if (a) return a;

    uint32_t b = reg_override_HEAD34(reg, old_val);
    if (b) return b;

    return 0;
}

924
static inline uint32_t reg_override_fps(uint32_t reg, uint32_t timerA, uint32_t timerB, uint32_t old_val)
925
926
927
928
929
{
    /* hardware register requires timer-1 */
    timerA--;
    timerB--;

930
931
932
933
934
935
    /* only override FPS registers if the old value is what we expect
     * otherwise we may be in some different video mode for a short time
     * this race condition is enough to lock up LiveView in some cases
     * e.g. 5D3 3x3 50/60p when going from photo mode to video mode
     */

936
937
938
939
940
941
942
    switch (reg)
    {
        case 0xC0F06824:
        case 0xC0F06828:
        case 0xC0F0682C:
        case 0xC0F06830:
        case 0xC0F06010:
943
944
945
946
947
948
949
950
        {
            uint32_t expected = default_timerA[get_video_mode_index()] - 1;

            if (old_val == expected)
            {
                return timerA;
            }
        }
951
952
953
        
        case 0xC0F06008:
        case 0xC0F0600C:
954
955
956
957
958
959
960
961
962
        {
            uint32_t expected = default_timerA[get_video_mode_index()] - 1;
            expected |= (expected << 16);

            if (old_val == expected)
            {
                return timerA | (timerA << 16);
            }
        }
963
964

        case 0xC0F06014:
965
966
967
968
969
970
971
972
        {
            uint32_t expected = default_timerB[get_video_mode_index()] - 1;

            if (old_val == expected)
            {
                return timerB;
            }
        }
973
974
975
976
977
    }

    return 0;
}

978
979
980
981
982
983
984
985
986
987
988
989
static inline uint32_t reg_override_3X_tall(uint32_t reg, uint32_t old_val)
{
    /* change FPS timers to increase vertical resolution */
    if (video_mode_fps >= 50)
    {
        int timerA = 400;

        int timerB =
            (video_mode_fps == 50) ? 1200 :
            (video_mode_fps == 60) ? 1001 :
                                       -1 ;

990
        int a = reg_override_fps(reg, timerA, timerB, old_val);
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
        if (a) return a;
    }

    /* fine-tuning head timers appears to help
     * pushing the resolution a tiny bit further */
    int head_adj =
        (video_mode_fps == 50) ? -30 :
        (video_mode_fps == 60) ? -20 :
                                   0 ;

    switch (reg)
    {
        /* raw resolution (end line/column) */
        case 0xC0F06804:
            return old_val + (YRES_DELTA << 16);

        /* HEAD3 timer */
        case 0xC0F0713C:
alex@thinkpad's avatar
alex@thinkpad committed
1009
            return old_val + YRES_DELTA + delta_head3 + head_adj;
1010
1011
1012

        /* HEAD4 timer */
        case 0xC0F07150:
alex@thinkpad's avatar
alex@thinkpad committed
1013
            return old_val + YRES_DELTA + delta_head4 + head_adj;
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
    }

    return reg_override_common(reg, old_val);
}

static inline uint32_t reg_override_3x3_tall(uint32_t reg, uint32_t old_val)
{
    if (!is_720p())
    {
        /* 1080p not patched in 3x3 */
        return 0;
    }

    /* change FPS timers to increase vertical resolution */
    if (video_mode_fps >= 50)
    {
        int timerA = 400;

        int timerB =
            (video_mode_fps == 50) ? 1200 :
            (video_mode_fps == 60) ? 1001 :
                                       -1 ;

1037
        int a = reg_override_fps(reg, timerA, timerB, old_val);
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
        if (a) return a;
    }

    /* fine-tuning head timers appears to help
     * pushing the resolution a tiny bit further */
    int head_adj =
        (video_mode_fps == 50) ? -10 :
        (video_mode_fps == 60) ? -20 :
                                   0 ;

    switch (reg)
    {
        /* for some reason, top bar disappears with the common overrides */
        /* very tight fit - every pixel counts here */
        case 0xC0F06800:
            return 0x1D0017;

        /* raw resolution (end line/column) */
        case 0xC0F06804:
            return old_val + (YRES_DELTA << 16);

        /* HEAD3 timer */
        case 0xC0F0713C:
alex@thinkpad's avatar
alex@thinkpad committed
1061
            return old_val + YRES_DELTA + delta_head3 + head_adj;
1062
1063
1064

        /* HEAD4 timer */
        case 0xC0F07150:
alex@thinkpad's avatar
alex@thinkpad committed
1065
            return old_val + YRES_DELTA + delta_head4 + head_adj;
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
    }

    return reg_override_common(reg, old_val);
}

static inline uint32_t reg_override_3x3_48p(uint32_t reg, uint32_t old_val)
{
    if (!is_720p())
    {
        /* 1080p not patched in 3x3 */
        return 0;
    }

    /* change FPS timers to increase vertical resolution */
    if (video_mode_fps >= 50)
    {
        int timerA =
            (video_mode_fps == 50) ? 401 :
            (video_mode_fps == 60) ? 400 :
                                      -1 ;
        int timerB =
            (video_mode_fps == 50) ? 1330 : /* 45p */
            (video_mode_fps == 60) ? 1250 : /* 48p */
                                       -1 ;

1091
        int a = reg_override_fps(reg, timerA, timerB, old_val);
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
        if (a) return a;
    }

    switch (reg)
    {
        /* for some reason, top bar disappears with the common overrides */
        /* very tight fit - every pixel counts here */
        case 0xC0F06800:
            return 0x1D0017;

        /* raw resolution (end line/column) */
        case 0xC0F06804:
            return old_val + (YRES_DELTA << 16);

        /* HEAD3 timer */
1107
        /* 2E6 in 50p, 2B4 in 60p */
1108
        case 0xC0F0713C:
1109
            return 0x2B4 + YRES_DELTA + delta_head3;
1110
1111

        /* HEAD4 timer */
1112
        /* 2B4 in 50p, 26D in 60p */
1113
        case 0xC0F07150:
1114
            return 0x26D + YRES_DELTA + delta_head4;
1115
1116
1117
1118
1119
    }

    return reg_override_common(reg, old_val);
}

1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
static inline uint32_t reg_override_3K(uint32_t reg, uint32_t old_val)
{
    /* FPS timer A, for increasing horizontal resolution */
    /* 25p uses 480 (OK), 24p uses 440 (too small); */
    /* only override in 24p, 30p and 60p modes */
    if (video_mode_fps != 25 && video_mode_fps !=  50)
    {
        int timerA = 455;
        int timerB =
            (video_mode_fps == 24) ? 2200 :
            (video_mode_fps == 30) ? 1760 :
            (video_mode_fps == 60) ?  880 :
                                       -1 ;

1134
        int a = reg_override_fps(reg, timerA, timerB, old_val);
1135
1136
1137
1138
1139
1140
1141
1142
        if (a) return a;
    }

    switch (reg)
    {
        /* raw resolution (end line/column) */
        /* X: (3072+140)/8 + 0x17, adjusted for 3072 in raw_rec */
        case 0xC0F06804:
1143
            return (old_val & 0xFFFF0000) + 0x1AA + (YRES_DELTA << 16);
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167

    }

    return reg_override_common(reg, old_val);
}

static inline uint32_t reg_override_4K_hfps(uint32_t reg, uint32_t old_val)
{
    /* FPS timer A, for increasing horizontal resolution */
    /* trial and error to allow 4096; 572 is too low, 576 looks fine */
    /* pick some values with small roundoff error */
    int timerA =
        (video_mode_fps < 30)  ?  585 : /* for 23.976/2 and 25/2 fps */
                                  579 ; /* for all others */

    /* FPS timer B, tuned to get half of the frame rate from Canon menu */
    int timerB =
        (video_mode_fps == 24) ? 3422 :
        (video_mode_fps == 25) ? 3282 :
        (video_mode_fps == 30) ? 2766 :
        (video_mode_fps == 50) ? 1658 :
        (video_mode_fps == 60) ? 1383 :
                                   -1 ;

1168
    int a = reg_override_fps(reg, timerA, timerB, old_val);
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
    if (a) return a;

    switch (reg)
    {
        /* raw resolution (end line/column) */
        /* X: (4096+140)/8 + 0x18, adjusted for 4096 in raw_rec */
        case 0xC0F06804:
            return (old_val & 0xFFFF0000) + 0x22A + (YRES_DELTA << 16);
    }

    return reg_override_common(reg, old_val);
}

static inline uint32_t reg_override_UHD(uint32_t reg, uint32_t old_val)
{
    /* FPS timer A, for increasing horizontal resolution */
1185
1186
1187
1188
1189
    /* trial and error to allow 3840; 536 is too low */
    int timerA = 
        (video_mode_fps == 25) ? 547 :
        (video_mode_fps == 50) ? 546 :
                                 550 ;
1190
    int timerB =
1191
1192
1193
1194
1195
        (video_mode_fps == 24) ? 1820 :
        (video_mode_fps == 25) ? 1755 :
        (video_mode_fps == 30) ? 1456 :
        (video_mode_fps == 50) ?  879 :
        (video_mode_fps == 60) ?  728 :
1196
1197
                                   -1 ;

1198
    int a = reg_override_fps(reg, timerA, timerB, old_val);
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
    if (a) return a;

    switch (reg)
    {
        /* raw resolution (end line/column) */
        /* X: (3840+140)/8 + 0x18, adjusted for 3840 in raw_rec */
        case 0xC0F06804:
            return (old_val & 0xFFFF0000) + 0x20A + (YRES_DELTA << 16);
    }

    return reg_override_common(reg, old_val);
}

static inline uint32_t reg_override_fullres_lv(uint32_t reg, uint32_t old_val)
1213
1214
1215
1216
1217
1218
1219
{
    switch (reg)
    {
        case 0xC0F06800:
            return 0x10018;         /* raw start line/column, from photo mode */
        
        case 0xC0F06804:            /* 1080p 0x528011B, photo 0xF6E02FE */
1220
            return (old_val & 0xFFFF0000) + 0x2FE + (YRES_DELTA << 16);
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
        
        case 0xC0F06824:
        case 0xC0F06828:
        case 0xC0F0682C:
        case 0xC0F06830:
            return 0x312;           /* from photo mode */
        
        case 0xC0F06010:            /* FPS timer A, for increasing horizontal resolution */
            return 0x317;           /* from photo mode; lower values give black border on the right */
        
        case 0xC0F06008:
        case 0xC0F0600C:
            return 0x3170317;

        case 0xC0F06014:
1236
            return (video_mode_fps > 30 ? 856 : 1482) + YRES_DELTA;   /* up to 7.4 fps */
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
    }

    /* no need to adjust the black bar */
    return reg_override_HEAD34(reg, old_val);
}

/* just for testing */
/* (might be useful for FPS override on e.g. 70D) */
static inline uint32_t reg_override_40_fps(uint32_t reg, uint32_t old_val)
{
    switch (reg)
    {
        case 0xC0F06824:
        case 0xC0F06828:
        case 0xC0F0682C:
        case 0xC0F06830:
        case 0xC0F06010:
            return 0x18F;
        
        case 0xC0F06008:
        case 0xC0F0600C:
            return 0x18F018F;

        case 0xC0F06014:
            return 0x5DB;
1262
1263
1264
1265
1266
    }

    return 0;
}

1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
static inline uint32_t reg_override_fps_nocheck(uint32_t reg, uint32_t timerA, uint32_t timerB, uint32_t old_val)
{
    /* hardware register requires timer-1 */
    timerA--;
    timerB--;

    switch (reg)
    {
        case 0xC0F06824:
        case 0xC0F06828:
        case 0xC0F0682C:
        case 0xC0F06830:
        case 0xC0F06010:
        {
            return timerA;
        }
        
        case 0xC0F06008:
        case 0xC0F0600C:
        {
            return timerA | (timerA << 16);
        }

        case 0xC0F06014:
        {
            return timerB;
        }
    }

    return 0;
}

static inline uint32_t reg_override_zoom_fps(uint32_t reg, uint32_t old_val)
{
    /* attempt to reconfigure the x5 zoom at the FPS selected in Canon menu */
    int timerA = 
        (video_mode_fps == 24) ? 512 :
        (video_mode_fps == 25) ? 512 :
        (video_mode_fps == 30) ? 520 :
        (video_mode_fps == 50) ? 512 :  /* cannot get 50, use 25 */
        (video_mode_fps == 60) ? 520 :  /* cannot get 60, use 30 */
                                  -1 ;
    int timerB =
        (video_mode_fps == 24) ? 1955 :
        (video_mode_fps == 25) ? 1875 :
        (video_mode_fps == 30) ? 1540 :
        (video_mode_fps == 50) ? 1875 :
        (video_mode_fps == 60) ? 1540 :
                                   -1 ;

    return reg_override_fps_nocheck(reg, timerA, timerB, old_val);
}

1320
1321
static int engio_vidmode_ok = 0;

1322
static void * get_engio_reg_override_func()
1323
{
1324
    uint32_t (*reg_override_func)(uint32_t, uint32_t) = 
1325
      //(crop_preset == CROP_PRESET_3X)         ? reg_override_top_bar     : /* fixme: corrupted image */
1326
        (crop_preset == CROP_PRESET_3X_TALL)    ? reg_override_3X_tall    :
1327
1328
        (crop_preset == CROP_PRESET_3x3_1X)     ? reg_override_3x3_tall   :
        (crop_preset == CROP_PRESET_3x3_1X_48p) ? reg_override_3x3_48p    :
1329
        (crop_preset == CROP_PRESET_3K)         ? reg_override_3K         :
1330
1331
        (crop_preset == CROP_PRESET_4K_HFPS)    ? reg_override_4K_hfps    :
        (crop_preset == CROP_PRESET_UHD)        ? reg_override_UHD        :
1332
1333
        (crop_preset == CROP_PRESET_40_FPS)     ? reg_override_40_fps     :
        (crop_preset == CROP_PRESET_FULLRES_LV) ? reg_override_fullres_lv :
1334
        (crop_preset == CROP_PRESET_CENTER_Z)   ? reg_override_zoom_fps   :
1335
                                                  0                       ;
1336
1337
1338
1339
1340
1341
1342
    return reg_override_func;
}

static void FAST engio_write_hook(uint32_t* regs, uint32_t* stack, uint32_t pc)
{
    uint32_t (*reg_override_func)(uint32_t, uint32_t) = 
        get_engio_reg_override_func();
1343
1344

    if (!reg_override_func)
1345
1346
1347
1348
    {
        return;
    }

1349
1350
1351
1352
1353
1354
1355
1356
    /* cmos_vidmode_ok doesn't help;
     * we can identify the current video mode from 0xC0F06804 */
    for (uint32_t * buf = (uint32_t *) regs[0]; *buf != 0xFFFFFFFF; buf += 2)
    {
        uint32_t reg = *buf;
        uint32_t old = *(buf+1);
        if (reg == 0xC0F06804)
        {
1357
1358
1359
            engio_vidmode_ok = (crop_preset == CROP_PRESET_CENTER_Z)
                ? (old == 0x56601EB)                        /* x5 zoom */
                : (old == 0x528011B || old == 0x2B6011B);   /* 1080p or 720p */
1360
1361
1362
1363
        }
    }

    if (!is_supported_mode() || !engio_vidmode_ok)
1364
1365
1366
1367
1368
1369
1370
1371
    {
        /* don't patch other video modes */
        return;
    }

    for (uint32_t * buf = (uint32_t *) regs[0]; *buf != 0xFFFFFFFF; buf += 2)
    {
        uint32_t reg = *buf;
1372