crop_rec on steroids: 3K, 4K, 1080p48, full-resolution LiveView

Started by a1ex, April 01, 2017, 11:15:41 AM

Previous topic - Next topic

0 Members and 3 Guests are viewing this topic.

Levas

Oeps, gotta read the posts better here in this forum  :P

Levas

Can somebody help with adding an extra crop_rec preset for the 6d ?

Crop_rec_4K branch already has one preset for the canon 6d(50fps with 3x3 readout), I would like to add another preset for the 6d.
Source file - Crop_rec.c - Crop_rec_4K branch:
https://bitbucket.org/hudson/magic-lantern/src/0e38b89bd0ad76a02fa8ef3a89961ddb115eb7bf/modules/crop_rec/crop_rec.c?at=crop_rec_4k&fileviewer=file-view-default

The preset is for 5x zoom mode 1:1 pixel readout.
Possible name for the preset -> 2688 x 1222 - 24fps
The registers to alter are:

CMOS 7 -> 268
8000 -> 5
800c -> 0
8172N -> 695 (695 is the hexadecimal value I enter in adtg_gui -> gives 4e6 value for NRZI)
8178N -> 695 (695 is the hexadecimal value I enter in adtg_gui -> gives 4e6 value for NRZI)
8179N -> 51c (51c is the hexadecimal value I enter in adtg_gui -> gives 617 value for NRZI)
81F8N -> 697 (697 is the hexadecimal value I enter in adtg_gui -> gives 4e5 value for NRZI)
81F9N -> 560 (560 is the hexadecimal value I enter in adtg_gui -> gives 640 value for NRZI)
713C -> 516
7150 -> 4e5
6014 -> 5b4
6804 -> 4e502c0

If somebody can add this preset and share the Crop_rec.c file, I have a good starting point for testing.
And I'm probably savvy enough to alter the preset with other values if I find some other nice values.




a1ex

Small update - g3gg0 just reworked sound recording support for mlv_lite: https://bitbucket.org/hudson/magic-lantern/pull-requests/837/mlv_snd-and-dual_iso-support-for-mlv_lite

Current experimental build contains his changes (it's build from the crop_rec_4k_mlv_snd branch). I'm not the best person to test it (not exactly a video user), so... please give it a try. Comparisons with the old approach, used in previous builds, are welcome.

Meanwhile I'm working on integrating the changes from Bilal, Levas, Danne and Nikfreak, using the 60D as second camera (almost got full-res LiveView working there as well). The current experimental build doesn't have any of the recent resolution updates (yet).

Already committed an update to adtg_gui that solves lock-ups in 1080p 50/60 on 5D3, and possibly on other models as well.

Danne

Nice a1ex!
Reference x5 zoom tweaks tested working:
EOSM:
https://www.magiclantern.fm/forum/index.php?topic=9741.msg202983#msg202983

100D:
https://www.magiclantern.fm/forum/index.php?topic=16040.msg202808#msg202808

@g3gg0
Nice to see progress in your mlv_snd branch. Difficult now to now what to do next. Working with current mlv_lite implementation or focus on your changes and switch branch. Guess tests will tell. Only had very short time to record a few MLV files on a 100D. Short summary:
1 - Records fine on both builds
2 - g3gg0 implementation breaks compatibility with MlRawViewver(metadata related?). Works fine in Mlv App, Switch, MLVFS
3 - rearrangements in mlv sound menu progress lags behind. The sound setting is since recently among RAW video settings which seems more logical. It´s also set to on per default when enabling mlv_snd.mo.

Comparing with this branch:
https://bitbucket.org/hudson/magic-lantern/branch/crop_rec_4k_mlv_lite_snd

What is your take on above branch g3gg0?

Levas

Quote from: Levas on June 19, 2018, 01:46:32 PM
Can somebody help with adding an extra crop_rec preset for the 6d ?

CMOS 7 -> 268
8000 -> 5
800c -> 0
8172N -> 695 (695 is the hexadecimal value I enter in adtg_gui -> gives 4e6 value for NRZI)
8178N -> 695 (695 is the hexadecimal value I enter in adtg_gui -> gives 4e6 value for NRZI)
8179N -> 51c (51c is the hexadecimal value I enter in adtg_gui -> gives 617 value for NRZI)
81F8N -> 697 (697 is the hexadecimal value I enter in adtg_gui -> gives 4e5 value for NRZI)
81F9N -> 560 (560 is the hexadecimal value I enter in adtg_gui -> gives 640 value for NRZI)
713C -> 516
7150 -> 4e5
6014 -> 5b4
6804 -> 4e502c0
6804 -> 4e502b0 double checked, this works in adtg_gui

mk11174 made a version of crop_rec.c with an 2K preset for the 6d.
Now fiddling with it, but can't get it to work.
first question is about the NRZI registers, do I write down the NRZI value in crop_rec.c or do I write down the hexadecimal values I put in these registers with adtg_gui ?
Furthermore, the crop_rec.c from mk11174 does something to some registers, but not to all.
Looking at the liveview it looks like it does set lineskipping to 0 and CMOS 7 settings are done too, but resolution doesn't change (6804), fps doesn't change (6014) ???
I think it doesn't do the register settings that are starting in line 920 of crop_rec.c

Can somebody take a look at this and tell me where the problem is ?
Link to file:
https://drive.google.com/open?id=1ZJSjmNXS6hMRPTCmkGLiwWuGy_OPAH1T


#include <dryos.h>
#include <module.h>
#include <config.h>
#include <menu.h>
#include <beep.h>
#include <property.h>
#include <patch.h>
#include <bmp.h>
#include <lvinfo.h>
#include <powersave.h>
#include <raw.h>
#include <fps.h>
#include <shoot.h>

#undef CROP_DEBUG

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

static int is_700D = 0;
static int is_6D = 0;

static CONFIG_INT("crop.preset", crop_preset_index, 0);
static CONFIG_INT("crop.shutter_range", shutter_range, 0);

enum crop_preset {
    CROP_PRESET_OFF = 0,
    CROP_PRESET_HD,
    CROP_PRESET_2K,
    CROP_PRESET_ULTRA_HD,
    CROP_PRESET_4K,
    CROP_PRESET_FullRes,
    CROP_PRESET_3x3_1X,
    CROP_PRESET_3x3_1X_48p,
    NUM_CROP_PRESETS
};

/* presets are not enabled right away (we need to go to play mode and back)
* 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 700D */
static enum crop_preset crop_presets_700d[] = {
    CROP_PRESET_OFF,
    CROP_PRESET_HD,
    CROP_PRESET_2K,
    CROP_PRESET_ULTRA_HD,
    CROP_PRESET_4K,
    CROP_PRESET_FullRes,
    CROP_PRESET_3x3_1X,

};

static const char * crop_choices_700d[] = {
    "OFF",
    "HD 1920x1080",
    "2K 2520x1384",
    "UltraHD 3840x2160",
    "4K 4096x2560",
    "FullRes 4464x2976",
    "3x3 720p",
};

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

static const char crop_choices_help2_700d[] =
    "HD crop (1920x1080 @ 24p, square raw pixels, preview broken)\n"
    "2K crop (2520x1384 @ 24p, square raw pixels, preview broken)\n"
    "UltraHD (3840x2160 @ 12p, square raw pixels, preview broken)\n"
    "4K      (4096x2560 @ 9.477p, square raw pixels, preview broken)\n"
    "FullRes (4464x2976 @ 6.527p, square raw pixels, preview broken)\n"
    "3x3 binning in 720p (square pixels in RAW, vertical crop)\n";

/* menu choices for 6D */
static enum crop_preset crop_presets_6d[] = {
    CROP_PRESET_OFF,
    CROP_PRESET_2K,
    CROP_PRESET_3x3_1X,
};

static const char * crop_choices_6d[] = {
    "OFF",
    "2K 2688x1222",
    "3x3 720p",
};

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

static const char crop_choices_help2_6d[] =
    "2K crop (2688x1222 @ 24p, square raw pixels, preview broken)\n"
    "3x3 binning in 720p (square pixels in RAW, vertical crop)\n";

/* 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;
static uint32_t ENGIO_WRITE     = 0;
static uint32_t MEM_ENGIO_WRITE = 0;

/* 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;

    switch (crop_preset)
    {
        default:
            return is_1080p() || is_720p();
    }
}

static int32_t  target_yres = 0;
static int32_t  delta_adtg0 = 0;
static int32_t  delta_adtg1 = 0;
static int32_t  delta_head3 = 0;
static int32_t  delta_head4 = 0;
static uint32_t cmos1_lo = 0, cmos1_hi = 0;
static uint32_t cmos2 = 0;

/* helper to allow indexing various properties of Canon's video modes */
static inline int get_video_mode_index()
{
    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       = 72;
    int skip_right      = 0;
    int skip_top        = 28;
    int skip_bottom     = 0;

    switch (crop_preset)
    {
        case CROP_PRESET_HD:
        case CROP_PRESET_2K:
        case CROP_PRESET_ULTRA_HD:
        case CROP_PRESET_4K:
        case CROP_PRESET_FullRes:
        case CROP_PRESET_3x3_1X:
        case CROP_PRESET_3x3_1X_48p:
            if (is_720p()) skip_top = 0;
            break;
    }

    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;
}

/* to be in sync with 0xC0F06800 */
static int get_top_bar_adjustment()
{
    switch (crop_preset)
    {
        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 */                /* 0x1F0017 from 0x10017 */
    }
}

/* 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) */
/* it's usually possible to push the numbers a few pixels further,
* at the risk of corrupted frames */
static int max_resolutions[NUM_CROP_PRESETS][5] = {
                                /*   24p   25p   30p   50p   60p */
    [CROP_PRESET_3x3_1X]        = { 1290, 1290, 1290,  960,  800 },
    [CROP_PRESET_3x3_1X_48p]    = { 1290, 1290, 1290, 1080, 1040 }, /* 1080p45/48 */
    [CROP_PRESET_HD]            = { 1920, 1728, 1070,  760,  680 },
    [CROP_PRESET_2K]            = { 1920, 1728, 1384,  760,  680 },
    [CROP_PRESET_ULTRA_HD]      = { 1920, 1728, 2160,  760,  680 },
    [CROP_PRESET_4K]            = { 1920, 1728, 2560,  760,  680 },
    [CROP_PRESET_FullRes]       = { 1920, 1728, 3490,  760,  680 },
};

/* 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();
        int top_adj = get_top_bar_adjustment();
        return desired_yres - default_yres + skip_top - default_skip_top + top_adj;
    }

    ASSERT(0);
    return 0;
}

#define YRES_DELTA calc_yres_delta()

/* 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
*/
static int FAST check_cmos_vidmode(uint16_t* data_buf)
{
    int ok = 1;
    int found = 1;
    while (*data_buf != 0xFFFF)
    {
        int reg = (*data_buf) >> 12;
        int value = (*data_buf) & 0xFFF;

        if (is_700D)
        {
            if (reg == 7)
            {
                found = 1;
                /* prevent running in 600D hack crop mode */
                if (value != 0x800)
                {
                    ok = 0;
                }
            }
        }
       
        data_buf++;
    }
   
    if (found) return ok;
   
    return -1;
}

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

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

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

static void FAST cmos_hook(uint32_t* regs, uint32_t* stack, uint32_t pc)
{
     /* 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];

    int cmos_new[10] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};

    if (is_700D)
    {
        switch (crop_preset)
        {
            case CROP_PRESET_HD:
                cmos_new[5] = 0x380 + delta_head3;    /* horizontal centering (trial and error) */
                cmos_new[7] = 0xAAA + delta_head4;    /* pink highlights without this */
                break;

            case CROP_PRESET_2K:
                cmos_new[5] = 0x302 + delta_head3;    /* horizontal centering (trial and error) */
                cmos_new[7] = 0xaa9 + delta_head4;    /* pink highlights without this */
                break;

            case CROP_PRESET_ULTRA_HD:
                cmos_new[5] = 0x180 + delta_head3;    /* horizontal centering (trial and error) */
                cmos_new[7] = 0x306 + delta_head4;    /* pink highlights without this */
                break;

            case CROP_PRESET_4K:
                cmos_new[5] = 0x100 + delta_head3;    /* horizontal centering (trial and error) */
                cmos_new[7] = 0xf84 + delta_head4;    /* pink highlights without this */
                break;

            case CROP_PRESET_FullRes:
                cmos_new[5] = 0x0DE + delta_head3;    /* horizontal centering (trial and error) */
                cmos_new[7] = 0x802 + delta_head4;    /* pink highlights without this */
                break;

            case CROP_PRESET_3x3_1X:
                /* start/stop scanning line, very large increments */
                cmos_new[7] = PACK12(6,29);
                break;           
        }
    }

    else if (is_6D)
    {
        switch (crop_preset)
        {
            case CROP_PRESET_2K:
                cmos_new[7] = 0x268 + delta_head4;    /* pink highlights without this */
                break;

            case CROP_PRESET_3x3_1X:
                /* start/stop scanning line, very large increments */
                cmos_new[7] = PACK12(37,10);
                break;           
        }
    }

    /* menu overrides */
    if (cmos1_lo || cmos1_hi)
    {
        cmos_new[1] = PACK12(cmos1_lo,cmos1_hi);
    }

    if (cmos2)
    {
        cmos_new[2] = cmos2;
    }
   
    /* 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;

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

        data_buf++;
        copy_ptr++;
        if (copy_ptr > copy_end) while(1);
    }
    *copy_ptr = 0xFFFF;

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

static uint32_t nrzi_encode( uint32_t in_val )
{
    uint32_t out_val = 0;
    uint32_t old_bit = 0;
    for (int num = 0; num < 31; num++)
    {
        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;
}

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;
}

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;
}

static void * get_engio_reg_override_func();

/* from SENSOR_TIMING_TABLE (fps-engio.c) */
/* hardcoded for 5D3 */
const int default_timerA[] = { 0x20F, 0x27F, 0x20f, 0x27F, 0x20F };
const int default_timerB[] = { 0x9DE, 0x7CF, 0x7E5, 0x3D7, 0x3F2 };
const int default_fps_1k[] = { 23976, 25000, 29973, 50000, 59940 };

/* 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();

    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;

    float new_shutter =
        (shutter_range == 0) ?
        ({
            /* original shutter speed from the altered video mode */
            orig_shutter;
        }) :
        ({
            /* 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 */
            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;
        });

    uint32_t (*reg_override_func)(uint32_t, uint32_t) =
        get_engio_reg_override_func();

    /* what value we are going to use for overriding timer B? */
    int fps_timer_b = (reg_override_func)
        ? (int) reg_override_func(0xC0F06014, fps_timer_b_orig - 1)
        : fps_timer_b_orig;

    /* will we actually override it? */
    fps_timer_b = fps_timer_b ? fps_timer_b + 1 : fps_timer_b_orig;

    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;
    int new_blanking = COERCE(fps_timer_b - new_exposure, 10, fps_timer_b - 2);

    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);
}

extern WEAK_FUNC(ret_0) void fps_override_shutter_blanking();

static void FAST adtg_hook(uint32_t* regs, uint32_t* stack, uint32_t pc)
{
    /* 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();

    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;
   
    struct adtg_new
    {
        int dst;
        int reg;
        int val;
    };
   
    /* expand this as required */
    struct adtg_new adtg_new[10] = {{0}};

    /* 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) ? 0x8061 : 0x805F;
    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;
        }
    }

    /* some modes may need adjustments to maintain exposure */
    if (shutter_blanking)
    {
        shutter_blanking = adjust_shutter_blanking(shutter_blanking);
    }

    if (is_700D)
    {
        /* all modes may want to override shutter speed */
        /* ADTG[0x8061]: shutter blanking for 3x3 mode  */
        /* ADTG[0x805F]: shutter blanking for zoom mode  */
        adtg_new[0] = (struct adtg_new) {6, 0x8061, shutter_blanking};
        adtg_new[1] = (struct adtg_new) {6, 0x805F, shutter_blanking};

        switch (crop_preset)
        {
            /* all 1:1 modes (3x, 2K, 4K...) */
            //case CROP_PRESET_HD:
            //case CROP_PRESET_2K:
            //case CROP_PRESET_ULTRA_HD:
            //case CROP_PRESET_4K:
            case CROP_PRESET_FullRes:
                /* ADTG2/4[0x8000] = 5 (set in one call) */
                /* ADTG2[0x8806] = 0x6088 (artifacts without it) */
                adtg_new[2] = (struct adtg_new) {6, 0x8000, 5};
                break;
        }

        switch (crop_preset)
        {
            case CROP_PRESET_FullRes:
                /* ADTG2/4[0x8000] = 5 (set in one call) */
                /* ADTG2[0x8806] = 0x6088 (artifacts without it) */
                adtg_new[3] = (struct adtg_new) {6, 0x800C, 0};
                break;
        }

        /* all modes with higher vertical resolution */
        switch (crop_preset)
        {
            case CROP_PRESET_HD:
            case CROP_PRESET_2K:
            case CROP_PRESET_ULTRA_HD:
                /* adjust vertical resolution */
                adtg_new[2] = (struct adtg_new) {6, 0x8172, nrzi_encode(0x88E + delta_adtg0)};
                adtg_new[3] = (struct adtg_new) {6, 0x8178, nrzi_encode(0x88E + delta_adtg0)};
                adtg_new[4] = (struct adtg_new) {6, 0x82B6, nrzi_encode(0x906 + delta_adtg0)};
                break;

            case CROP_PRESET_4K:
                /* adjust vertical resolution */
                adtg_new[2] = (struct adtg_new) {6, 0x8172, nrzi_encode(0xC19 + delta_adtg0)};
                adtg_new[3] = (struct adtg_new) {6, 0x8178, nrzi_encode(0xC19 + delta_adtg0)};
                adtg_new[4] = (struct adtg_new) {6, 0x82B6, nrzi_encode(0xC10 + delta_adtg0)};
                break;

            case CROP_PRESET_FullRes:
                /* adjust vertical resolution */
                adtg_new[4] = (struct adtg_new) {6, 0x8172, nrzi_encode(0xBEB + delta_adtg0)};
                adtg_new[5] = (struct adtg_new) {6, 0x8178, nrzi_encode(0xBEB + delta_adtg0)};
                adtg_new[6] = (struct adtg_new) {6, 0x82B6, nrzi_encode(0xC6D + delta_adtg0)};
                break;

            /* 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;
        }

    }

    if (is_6D)
    {
        /* all modes may want to override shutter speed */
        /* ADTG[0x8061]: shutter blanking for 3x3 mode  */
        /* ADTG[0x805F]: shutter blanking for zoom mode  */
        adtg_new[0] = (struct adtg_new) {6, 0x8061, shutter_blanking};
        adtg_new[1] = (struct adtg_new) {6, 0x805F, shutter_blanking};

        switch (crop_preset)
        {
            /* all 1:1 modes (3x, 2K, 4K...) */
            case CROP_PRESET_2K:
                /* ADTG2/4[0x8000] = 5 (set in one call) */
                /* ADTG2[0x8806] = 0x6088 (artifacts without it) */
                adtg_new[2] = (struct adtg_new) {6, 0x8000, 5};
                break;
        }

        switch (crop_preset)
        {
            case CROP_PRESET_2K:
                /* ADTG2/4[0x8000] = 5 (set in one call) */
                /* ADTG2[0x8806] = 0x6088 (artifacts without it) */
                adtg_new[3] = (struct adtg_new) {6, 0x800C, 0};
                break;
        }

        /* all modes with higher vertical resolution */
        switch (crop_preset)
        {
            case CROP_PRESET_2K:
                /* adjust vertical resolution */
                adtg_new[2] = (struct adtg_new) {6, 0x8172, nrzi_encode(0x4e6 + delta_adtg0)};
                adtg_new[3] = (struct adtg_new) {6, 0x8178, nrzi_encode(0x4e6 + delta_adtg0)};
                adtg_new[4] = (struct adtg_new) {6, 0x8179, nrzi_encode(0x617 + delta_adtg0)};
                adtg_new[5] = (struct adtg_new) {6, 0x81F8, nrzi_encode(0x4e5 + delta_adtg0)};
                adtg_new[6] = (struct adtg_new) {6, 0x81F9, nrzi_encode(0x640 + delta_adtg0)};
                break;

            /* 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;
        }

    }

    while(*data_buf != 0xFFFFFFFF)
    {
        *copy_ptr = *data_buf;
        int reg = (*data_buf) >> 16;
        for (int i = 0; i < COUNT(adtg_new); i++)
        {
            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;

                if (reg == 0x805F || reg == 0x8061)
                {
                    /* also override in original data structure */
                    /* to be picked up on the screen indicators */
                    *(uint16_t*)data_buf = new_value;
                }
            }
        }
        data_buf++;
        copy_ptr++;
        if (copy_ptr >= copy_end) while(1);
    }
    *copy_ptr = 0xFFFFFFFF;
   
    /* pass our modified register list to adtg_write */
    regs[1] = (uint32_t) copy;
}

/* 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:
            return old_val + YRES_DELTA + delta_head3;

        /* HEAD4 timer */
        case 0xC0F07150:
            return old_val + YRES_DELTA + delta_head4;
    }

    return 0;
}

static inline uint32_t reg_override_common(uint32_t reg, uint32_t old_val)
{
    uint32_t b = reg_override_HEAD34(reg, old_val);
    if (b) return b;

    return 0;
}

static inline uint32_t reg_override_fps(uint32_t reg, uint32_t timerA, uint32_t timerB)
{
    /* hardware register requires timer-1 */
    timerA--;
    timerB--;

    /* 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
     */

    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_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 ;

        int a = reg_override_fps(reg, timerA, timerB);
        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 0x30010;

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

        /* HEAD3 timer */
        case 0xC0F0713C:
            return old_val + YRES_DELTA + delta_head3 + head_adj;

        /* HEAD4 timer */
        case 0xC0F07150:
            return old_val + YRES_DELTA + delta_head4 + head_adj;
    }

    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 ;

        int a = reg_override_fps(reg, timerA, timerB);
        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 0x30010;

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

        /* HEAD3 timer */
        case 0xC0F0713C:
            return old_val + YRES_DELTA + delta_head3;

        /* HEAD4 timer */
        case 0xC0F07150:
            return old_val + YRES_DELTA + delta_head4;
    }

    return reg_override_common(reg, old_val);
}

static inline uint32_t reg_override_HD(uint32_t reg, uint32_t old_val)
{
    switch (reg)
    {
        /* raw resolution (end line/column) */
        /* X: (3072+140)/8 + 0x17, adjusted for 3072 in raw_rec */
        case 0xC0F06804: return 0x4540202; // 1920x1080  x5 Mode;

        case 0xC0F06824: return 0x2ca;
        case 0xC0F06828: return 0x2ca;
        case 0xC0F0682C: return 0x2ca;
        case 0xC0F06830: return 0x2ca;
       
        case 0xC0F06010: return 0x2cb;
        case 0xC0F06008: return 0x2cb02cb;
        case 0xC0F0600C: return 0x2cb02cb;

        case 0xC0F06014: return 0x747;

        case 0xC0F0713c: return 0x5C7;
        case 0xC0F07150: return 0x59A;
    }

    return 0;
}

static inline uint32_t reg_override_2K(uint32_t reg, uint32_t old_val)
{
    if (is_700D)
    {
       switch (reg)
       {
           /* raw resolution (end line/column) */
           /* X: (3072+140)/8 + 0x17, adjusted for 3072 in raw_rec */
           case 0xC0F06804: return 0x5840298; // 2520x1386  x5 Mode;

           case 0xC0F06824: return 0x2ca;
           case 0xC0F06828: return 0x2ca;
           case 0xC0F0682C: return 0x2ca;
           case 0xC0F06830: return 0x2ca;
       
           case 0xC0F06010: return 0x2cb;
           case 0xC0F06008: return 0x2cb02cb;
           case 0xC0F0600C: return 0x2cb02cb;

           case 0xC0F06014: return 0x747;

           case 0xC0F0713c: return 0x5C7;
           case 0xC0F07150: return 0x59A;
       }
    }
    else if (is_6D)
    {
       switch (reg)
       {
           /* raw resolution (end line/column) */
           /* X: (3072+140)/8 + 0x17, adjusted for 3072 in raw_rec */
           case 0xC0F06804: return 0x4e502b0; // 2688x1222  x5 Mode;

           case 0xC0F06014: return 0x5b4;

           case 0xC0F0713c: return 0x516;
           case 0xC0F07150: return 0x4e5;
       }
    }

    return 0;
}

static inline uint32_t reg_override_ULTRA_HD(uint32_t reg, uint32_t old_val)
{
    switch (reg)
    {
        /* raw resolution (end line/column) */
        /* X: (3072+140)/8 + 0x17, adjusted for 3072 in raw_rec */
        case 0xC0F06804: return 0x88D03E2; // 2520x1386  x5 Mode;

        case 0xC0F06824: return 0x4ca;
        case 0xC0F06828: return 0x4ca;
        case 0xC0F0682C: return 0x4ca;
        case 0xC0F06830: return 0x4ca;
       
        case 0xC0F06010: return 0x43b;
        case 0xC0F06008: return 0x43b043b;
        case 0xC0F0600C: return 0x43b043b;

        case 0xC0F06014: return 0x993;

        case 0xC0F0713c: return 0x8E7;
        case 0xC0F07150: return 0x8BA;
    }

    return 0;
}

static inline uint32_t reg_override_4K(uint32_t reg, uint32_t old_val)
{
    switch (reg)
    {
        /* raw resolution (end line/column) */
        /* X: (3072+140)/8 + 0x17, adjusted for 3072 in raw_rec */
        case 0xC0F06804: return 0xA1C0422; // 2520x1386  x5 Mode;

        case 0xC0F06824: return 0x4ca;
        case 0xC0F06828: return 0x4ca;
        case 0xC0F0682C: return 0x4ca;
        case 0xC0F06830: return 0x4ca;
       
        case 0xC0F06010: return 0x45b;
        case 0xC0F06008: return 0x45b045b;
        case 0xC0F0600C: return 0x45b045b;

        case 0xC0F06014: return 0xBD3;

        case 0xC0F0713c: return 0xAE7;
        case 0xC0F07150: return 0xABA;
    }

    return 0;
}

static inline uint32_t reg_override_FullRes(uint32_t reg, uint32_t old_val)
{
    switch (reg)
    {
        /* raw resolution (end line/column) */
        /* X: (3072+140)/8 + 0x17, adjusted for 3072 in raw_rec */
        case 0xC0F06804: return 0xC12047E; // 2520x1386  x5 Mode;

        case 0xC0F06824: return 0x56a;
        case 0xC0F06828: return 0x56a;
        case 0xC0F0682C: return 0x56a;
        case 0xC0F06830: return 0x56a;
       
        case 0xC0F06010: return 0x56b;
        case 0xC0F06008: return 0x56b056b;
        case 0xC0F0600C: return 0x56b056b;

        case 0xC0F06014: return 0xDCB;
        case 0xC0F06800: return 0x30010;

        case 0xC0F0713c: return 0xCA7;
        case 0xC0F07150: return 0xC7A;
    }

    return 0;
}

static int engio_vidmode_ok = 0;

static void * get_engio_reg_override_func()
{
    uint32_t (*reg_override_func)(uint32_t, uint32_t) =
        (crop_preset == CROP_PRESET_3x3_1X)     ? reg_override_3x3_tall   :
        (crop_preset == CROP_PRESET_3x3_1X_48p) ? reg_override_3x3_48p    :
        (crop_preset == CROP_PRESET_HD)         ? reg_override_HD         :
        (crop_preset == CROP_PRESET_2K)         ? reg_override_2K         :
        (crop_preset == CROP_PRESET_ULTRA_HD)   ? reg_override_ULTRA_HD   :
        (crop_preset == CROP_PRESET_4K)         ? reg_override_4K         :
        (crop_preset == CROP_PRESET_FullRes)    ? reg_override_FullRes    :
                                                  0                       ;
    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();

    if (!reg_override_func)
    {
        return;
    }

    /* 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)
        {
            engio_vidmode_ok =
                  (old == 0x4540298) ||/* x5 zoom */ (old == 0x4a601d4 || old == 0x2d701d4);   /* 1080p or 720p */
        }
    }

    if (!is_supported_mode() || !engio_vidmode_ok)
    {
        /* don't patch other video modes */
        return;
    }

    for (uint32_t * buf = (uint32_t *) regs[0]; *buf != 0xFFFFFFFF; buf += 2)
    {
        uint32_t reg = *buf;
        uint32_t old = *(buf+1);
       
        int new = reg_override_func(reg, old);
        if (new)
        {
            dbg_printf("[%x] %x: %x -> %x\n", regs[0], reg, old, new);
            *(buf+1) = new;
        }
    }
}

static int patch_active = 0;

static void update_patch()
{
    if (CROP_PRESET_MENU)
    {
        /* update preset */
        crop_preset = CROP_PRESET_MENU;

        /* install our hooks, if we haven't already do so */
        if (!patch_active)
        {
            patch_hook_function(CMOS_WRITE, MEM_CMOS_WRITE, &cmos_hook, "crop_rec: CMOS[5,7] parameters hook");
            patch_hook_function(ADTG_WRITE, MEM_ADTG_WRITE, &adtg_hook, "crop_rec: ADTG[8000,8806] parameters hook");
            if (ENGIO_WRITE)
            {
                patch_hook_function(ENGIO_WRITE, MEM_ENGIO_WRITE, engio_write_hook, "crop_rec: video timers hook");
            }
            patch_active = 1;
        }
    }
    else
    {
        /* undo active patches, if any */
        if (patch_active)
        {
            unpatch_memory(CMOS_WRITE);
            unpatch_memory(ADTG_WRITE);
            if (ENGIO_WRITE)
            {
                unpatch_memory(ENGIO_WRITE);
            }
            patch_active = 0;
            crop_preset = 0;
        }
    }
}

/* enable patch when switching LiveView (not in the middle of LiveView) */
/* otherwise you will end up with a halfway configured video mode that looks weird */
PROP_HANDLER(PROP_LV_ACTION)
{
    update_patch();
}

/* also try when switching zoom modes */
PROP_HANDLER(PROP_LV_DISPSIZE)
{
    update_patch();
}

static MENU_UPDATE_FUNC(crop_update)
{
    if (CROP_PRESET_MENU && lv)
    {
            if (!is_supported_mode())
            {
                MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "This preset only works in 1080p and 720p video modes.");
            }
            else if (lv_dispsize != 1)
            {
                MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "To use this mode, exit ML menu and press the zoom button (set to x1).");
            }
            else if (!is_720p())
            {
                if (CROP_PRESET_MENU == CROP_PRESET_3x3_1X ||
                    CROP_PRESET_MENU == CROP_PRESET_3x3_1X_48p)
                {
                    /* these presets only have effect in 720p mode */
                    MENU_SET_WARNING(MENU_WARN_NOT_WORKING, "This preset only works in the 720p 50/60 fps modes from Canon menu.");
                    return;
                }
            }
    }
}

static MENU_UPDATE_FUNC(target_yres_update)
{
    MENU_SET_RINFO("from %d", max_resolutions[crop_preset][get_video_mode_index()]);
}

static struct menu_entry crop_rec_menu[] =
{
    {
        .name       = "Crop mode",
        .priv       = &crop_preset_index,
        .update     = crop_update,
        .depends_on = DEP_LIVEVIEW,
        .children =  (struct menu_entry[]) {
            {
                .name       = "Shutter range",
                .priv       = &shutter_range,
                .max        = 1,
                .choices    = CHOICES("Original", "Full range"),
                .help       = "Choose the available shutter speed range:",
                .help2      = "Original: default range used by Canon in selected video mode.\n"
                              "Full range: from 1/FPS to minimum exposure time allowed by hardware."
            },
            {
                .name   = "Target YRES",
                .priv   = &target_yres,
                .update = target_yres_update,
                .max    = 3870,
                .unit   = UNIT_DEC,
                .help   = "Desired vertical resolution (only for presets with higher resolution).",
                .help2  = "Decrease if you get corrupted frames (dial the desired resolution here).",
            },
            {
                .name   = "Delta ADTG 0",
                .priv   = &delta_adtg0,
                .min    = -500,
                .max    = 500,
                .unit   = UNIT_DEC,
                .help   = "ADTG 0x8178, 0x8196, 0x82F8",
                .help2  = "May help pushing the resolution a little. Start with small increments.",
                .advanced = 1,
            },
            {
                .name   = "Delta ADTG 1",
                .priv   = &delta_adtg1,
                .min    = -500,
                .max    = 500,
                .unit   = UNIT_DEC,
                .help   = "ADTG 0x8179, 0x8197, 0x82F9",
                .help2  = "May help pushing the resolution a little. Start with small increments.",
                .advanced = 1,
            },
            {
                .name   = "Delta HEAD3",
                .priv   = &delta_head3,
                .min    = -500,
                .max    = 500,
                .unit   = UNIT_DEC,
                .help2  = "May help pushing the resolution a little. Start with small increments.",
                .advanced = 1,
            },
            {
                .name   = "Delta HEAD4",
                .priv   = &delta_head4,
                .min    = -500,
                .max    = 500,
                .unit   = UNIT_DEC,
                .help2  = "May help pushing the resolution a little. Start with small increments.",
                .advanced = 1,
            },
            {
                .name   = "CMOS[1] lo",
                .priv   = &cmos1_lo,
                .max    = 63,
                .unit   = UNIT_DEC,
                .help   = "Start scanline (very rough). Use for vertical positioning.",
                .advanced = 1,
            },
            {
                .name   = "CMOS[1] hi",
                .priv   = &cmos1_hi,
                .max    = 63,
                .unit   = UNIT_DEC,
                .help   = "End scanline (very rough). Increase if white bar at bottom.",
                .help2  = "Decrease if you get strange colors as you move the camera.",
                .advanced = 1,
            },
            {
                .name   = "CMOS[2]",
                .priv   = &cmos2,
                .max    = 0xFFF,
                .unit   = UNIT_HEX,
                .help   = "Horizontal position / binning.",
                .help2  = "Use for horizontal centering.",
                .advanced = 1,
            },
            MENU_ADVANCED_TOGGLE,
            MENU_EOL,
        },
    },
};

static int crop_rec_needs_lv_refresh()
{
    if (!lv)
    {
        return 0;
    }

    if (CROP_PRESET_MENU)
    {
        if (is_supported_mode())
        {
            if (!patch_active || CROP_PRESET_MENU != crop_preset)
            {
                return 1;
            }
        }
    }
    else /* crop disabled */
    {
        if (patch_active)
        {
            return 1;
        }
    }

    return 0;
}

/* faster version than the one from ML core */
static void set_zoom(int zoom)
{
    if (!lv) return;
    if (RECORDING) return;
    if (is_movie_mode() && video_mode_crop) return;
    zoom = COERCE(zoom, 1, 10);
    if (zoom > 1 && zoom < 10) zoom = 5;
    prop_request_change_wait(PROP_LV_DISPSIZE, &zoom, 4, 1000);
}


/* when closing ML menu, check whether we need to refresh the LiveView */
static unsigned int crop_rec_polling_cbr(unsigned int unused)
{
    /* also check at startup */
    static int lv_dirty = 1;

    int menu_shown = gui_menu_shown();
    if (lv && menu_shown)
    {
        lv_dirty = 1;
    }
   
    if (!lv || menu_shown || RECORDING_RAW)
    {
        /* outside LV: no need to do anything */
        /* don't change while browsing the menu, but shortly after closing it */
        /* don't change while recording raw, but after recording stops
         * (H.264 should tolerate this pretty well, except maybe 50D) */
        return CBR_RET_CONTINUE;
    }

    if (lv_dirty)
    {
        /* do we need to refresh LiveView? */
        if (crop_rec_needs_lv_refresh())
        {
            /* let's check this once again, just in case */
            /* (possible race condition that would result in unnecessary refresh) */
            msleep(500);
            if (crop_rec_needs_lv_refresh())
            {
                info_led_on();
                gui_uilock(UILOCK_EVERYTHING);
                int old_zoom = lv_dispsize;
                set_zoom(lv_dispsize == 1 ? 5 : 1);
                set_zoom(old_zoom);
                gui_uilock(UILOCK_NONE);
                info_led_off();
            }
        }
        lv_dirty = 0;
    }

    return CBR_RET_CONTINUE;
}

/* Display recording status in top info bar */
static LVINFO_UPDATE_FUNC(crop_info)
{
    LVINFO_BUFFER(16);
   
    if (patch_active)
    {
        if (lv_dispsize > 1)
        {
            switch (crop_preset)
            {
                case CROP_PRESET_HD:
                    snprintf(buffer, sizeof(buffer), "HD");
                    break;
                case CROP_PRESET_2K:
                    snprintf(buffer, sizeof(buffer), "2K");
                case CROP_PRESET_ULTRA_HD:
                    snprintf(buffer, sizeof(buffer), "ULTRA HD");
                case CROP_PRESET_4K:
                    snprintf(buffer, sizeof(buffer), "4K");
                case CROP_PRESET_FullRes:
                    snprintf(buffer, sizeof(buffer), "FullRes");
                    break;
            }
        }
    }

    /* append info about current binning mode */

    if (raw_lv_is_enabled())
    {
        /* fixme: raw_capture_info is only updated when LV RAW is active */

        if (raw_capture_info.binning_x + raw_capture_info.skipping_x == 1 &&
            raw_capture_info.binning_y + raw_capture_info.skipping_y == 1)
        {
            STR_APPEND(buffer, "%s1:1", buffer[0] ? " " : "");
        }
        else
        {
            STR_APPEND(buffer, "%s%dx%d",
                buffer[0] ? " " : "",
                raw_capture_info.binning_x + raw_capture_info.skipping_x,
                raw_capture_info.binning_y + raw_capture_info.skipping_y
            );
        }
    }

    if (crop_rec_needs_lv_refresh())
    {
        if (!streq(buffer, SYM_WARNING))
        {
            STR_APPEND(buffer, " " SYM_WARNING);
        }
        item->color_fg = COLOR_YELLOW;
    }
}

static struct lvinfo_item info_items[] = {
    {
        .name = "Crop info",
        .which_bar = LV_BOTTOM_BAR_ONLY,
        .update = crop_info,
        .preferred_position = -50,  /* near the focal length display */
        .priority = 1,
    }
};

static unsigned int raw_info_update_cbr(unsigned int unused)
{
    if (patch_active)
    {
        /* not implemented yet */
        raw_capture_info.offset_x = raw_capture_info.offset_y   = SHRT_MIN;

        if (lv_dispsize > 1)
        {
            /* raw backend gets it right */
            return 0;
        }

        /* update horizontal pixel binning parameters */
        switch (crop_preset)
        {
            case CROP_PRESET_HD:
            case CROP_PRESET_2K:
            case CROP_PRESET_ULTRA_HD:
            case CROP_PRESET_4K:
            case CROP_PRESET_FullRes:
                raw_capture_info.binning_x    = raw_capture_info.binning_y  = 1;
                raw_capture_info.skipping_x   = raw_capture_info.skipping_y = 0;
                break;

            case CROP_PRESET_3x3_1X:
            case CROP_PRESET_3x3_1X_48p:
                break;
        }

        /* update vertical pixel binning / line skipping parameters */
        switch (crop_preset)
        {
            case CROP_PRESET_HD:
            case CROP_PRESET_2K:
            case CROP_PRESET_ULTRA_HD:
            case CROP_PRESET_4K:
            case CROP_PRESET_FullRes:
                raw_capture_info.binning_y = 1; raw_capture_info.skipping_y = 0;
                break;
            case CROP_PRESET_3x3_1X:
            case CROP_PRESET_3x3_1X_48p:
                break;
        }

        if (is_700D || is_6D)
        {
            /* update skip offsets */
            int skip_left, skip_right, skip_top, skip_bottom;
            calc_skip_offsets(&skip_left, &skip_right, &skip_top, &skip_bottom);
            raw_set_geometry(raw_info.width, raw_info.height, skip_left, skip_right, skip_top, skip_bottom);
        }
    }
    return 0;
}

static unsigned int crop_rec_init()
{

    if (is_camera("700D", "1.1.5") || is_camera("650D", "1.0.4"))
    {
        CMOS_WRITE = 0x17A1C;
        MEM_CMOS_WRITE = 0xE92D41F0;
       
        ADTG_WRITE = 0x178FC;
        MEM_ADTG_WRITE = 0xE92D43F8;

        ENGIO_WRITE = 0xFF2C2D00;
        MEM_ENGIO_WRITE = 0xE51FC15C;
       
        is_700D = 1;
        crop_presets                = crop_presets_700d;
        crop_rec_menu[0].choices    = crop_choices_700d;
        crop_rec_menu[0].max        = COUNT(crop_choices_700d) - 1;
        crop_rec_menu[0].help       = crop_choices_help_700d;
        crop_rec_menu[0].help2      = crop_choices_help2_700d;
    }
    else if (is_camera("6D", "1.1.6"))
    {
        CMOS_WRITE = 0x2420C;
        MEM_CMOS_WRITE = 0xE92D41F0;       
       
        ADTG_WRITE = 0x24108;
        MEM_ADTG_WRITE = 0xE92D41F0;

        ENGIO_WRITE = 0xFF2ADE1C;
        MEM_ENGIO_WRITE = 0xE51FC15C;
       
        is_6D = 1;
        crop_presets                = crop_presets_6d;
        crop_rec_menu[0].choices    = crop_choices_6d;
        crop_rec_menu[0].max        = COUNT(crop_choices_6d) - 1;
        crop_rec_menu[0].help       = crop_choices_help_6d;
        crop_rec_menu[0].help2      = crop_choices_help2_6d;
    }

    menu_add("Movie", crop_rec_menu, COUNT(crop_rec_menu));
    lvinfo_add_items (info_items, COUNT(info_items));

    return 0;
}

static unsigned int crop_rec_deinit()
{
    return 0;
}

MODULE_INFO_START()
    MODULE_INIT(crop_rec_init)
    MODULE_DEINIT(crop_rec_deinit)
MODULE_INFO_END()

MODULE_CONFIGS_START()
    MODULE_CONFIG(crop_preset_index)
    MODULE_CONFIG(shutter_range)
MODULE_CONFIGS_END()

MODULE_CBRS_START()
    MODULE_CBR(CBR_SHOOT_TASK, crop_rec_polling_cbr, 0)
    MODULE_CBR(CBR_RAW_INFO_UPDATE, raw_info_update_cbr, 0)
MODULE_CBRS_END()

MODULE_PROPHANDLERS_START()
    MODULE_PROPHANDLER(PROP_LV_ACTION)
    MODULE_PROPHANDLER(PROP_LV_DISPSIZE)
MODULE_PROPHANDLERS_END()
[\code]

a1ex

For NRZI registers, the preferred format is with nrzi_encode, so you supply something human-readable (hex or decimal, doesn't matter much). The NRZI format is machine-readable.

For this line:
Quote
8172N -> 695 (695 is the hexadecimal value I enter in adtg_gui -> gives 4e6 value for NRZI)

you would write: nrzi_encode(0x4e6); it will return 0x695.

So this line is correct (delta_adtg0 is 0 by default; it's used to let you fine-tune this from menu).

+                adtg_new[2] = (struct adtg_new) {6, 0x8172, nrzi_encode(0x4e6 + delta_adtg0)};


Tip: for powersave timing registers, you can just set them to 0 for troubleshooting (tested on 5D3 and 60D - 0 works on all resolutions, but the sensor might run a bit hotter). Readout timing registers will affect the image if you set them to 0, but you should still be able to see something.

You can't use adtg_gui to check whether the overrides were correct (they both use the same hooks in Canon code, and only one hook can be installed at a time). You can, however, use the dbg_printf calls to print what is overridden. For example this one:

                dbg_printf("ADTG%x[%x] = %x\n", dst, reg, new_value);


Change it to printf so it will go to the console. Turn off raw video, as that will also print a lot of stuff.

Same for cmos_hook and engio_write_hook - each one has a similar dbg_printf.

Caveat: printf is slow; calling it too many times from LiveView tasks (where these hooks are inserted) may lock up the camera. Workaround: enable one printf at a time, or skip printing registers that are changed on every LiveView frame, after making sure they are good.

Levas

Thanks for the help Alex,
Still fiddling with the crop_rec.c, I think the problem is that it doesn't set B timer, 6014.
I see some stuff about checking B timer original values and it checks with hardcoded values for 5d3.
Could that be the problem ?

a1ex

The B timer is visible in the FPS override submenu (don't enable FPS override, just look in the submenu at the timer values).

There is a check at the beginning of engio_write_hook, used to identify the video mode. The values hardcoded there appear to be for 700D. Add some printf to find out the right values, or take them from adtg_gui.

If that check fails, no other ENGIO registers will be set, so the resolution will not be changed at all.


Levas

Found the video check, altered it with 6d values:

    /* cmos_vidmode_ok doesn't help;
     * we can identify the current video mode from 0xC0F06804  SET TO 6D_Values*/
    for (uint32_t * buf = (uint32_t *) regs[0]; *buf != 0xFFFFFFFF; buf += 2)
    {
        uint32_t reg = *buf;
        uint32_t old = *(buf+1);
        if (reg == 0xC0F06804)
        {
            engio_vidmode_ok =
                  (old == 0x3dd02c0) ||/* x5 zoom */ (old == 0x4e501ec || old == 0x29801ec);   /* 1080p or 720p */
        }
    }


But still no luck  :-\
Probably found the problem, ENGIO address values are taken from 700d. (CMOS and ADTG are good and for 6d)
What are the correct ENGIO address values for 6d ?


    else if (is_camera("6D", "1.1.6"))
    {
        CMOS_WRITE = 0x2420C;
        MEM_CMOS_WRITE = 0xE92D41F0;       
       
        ADTG_WRITE = 0x24108;
        MEM_ADTG_WRITE = 0xE92D41F0;

        ENGIO_WRITE = 0xFF2ADE1C;
        MEM_ENGIO_WRITE = 0xE51FC15C;
       

Levas

Found the Engio write address for 6d in the stubs.s file for 6d:


/** Engio **/
NSTUB(0xFF2ADE1C, _EngDrvOut)                               // AJ_EngDrvOut_1xVar_to_ShadowStruct
NSTUB(0xFF2AE134, _engio_write)
NSTUB(0xFF2ADEB8,  shamem_read)                             // AJ_0x8FB0_engio_struct_n_R0_manipulation_to_get_ptr
// NSTUB(    ???,  EngDrvOut)                               /* present on 7D_MASTER.203 */


So ENGIO_WRITE address for 6d = 0xFF2AE134
How do I find the correct address for MEM_ENGIO_WRITE ?

Danne

Checking stubs in ROM1.BIN for 700D and 6D seems both shares the same adress:
E51FC15C;

This seems correct for the 6D as stated above(checked stubs):
ENGIO_WRITE address for 6d = 0xFF2AE134

Levas

 :o ok.
So let's compile...

Yes works! Thanks Danne and Alex  :D

mk11174

Quote from: Levas on June 21, 2018, 10:12:15 AM
mk11174 made a version of crop_rec.c with an 2K preset for the 6d.
:) Def didnt make it, just chopped it up so I can sort of understand what needed to go where, why most of the 5D3 stuff is missing, nothing was changing for me until I chopped it up and looked at what was left, then understood a tiny bit more what needed to go where to make the changes happen like they did in ADTG tool and now it works for 700D.

Was hoping it would help you get the 6D preset you wanted going, But Alex I guess is working on this for many of you, maybe best to wait for his update, you know it will be perfect as usual!

There were this lines that were hard coded though that should be changed for the 6D like I mentions in the PM, so prob try getting all those right first, maybe will work?
444        const int default_timerA[] = { 0x20F, 0x27F, 0x20f, 0x27F, 0x20F };
445        const int default_timerB[] = { 0x9DE, 0x7CF, 0x7E5, 0x3D7, 0x3F2 };
446        const int default_fps_1k[] = { 23976, 25000, 29973, 50000, 59940 };
551        int adtg_blanking_reg = (lv_dispsize == 1) ? 0x8061 : 0x805F;
639        adtg_new[0] = (struct adtg_new) {6, 0x8061, shutter_blanking};
640        adtg_new[1] = (struct adtg_new) {6, 0x805F, shutter_blanking};
1052       (old == 0x4540298) ||/* x5 zoom */ (old == 0x4a601d4 || old == 0x2d701d4);   /* 1080p or 720p */
500D/T1i  550D/T2i  600D/T3i  700D/T5i

Levas

Already changed everything on these lines and it's working now.
First I forgot the video check which is on Line 1052, like you mentioned.

Seems that the only thing that was wrong at the end was the ENGIO_WRITE address.
Took the address from the Stubs.s file for ENGIO_WRITE address and I got my crop_rec working  :D

Danne

Good job. Could you post your changes to bitbucket Levas? Curious how code differs. Or post the changed crop_rec.c version.

Levas

Something else:

Did some math recently and I think the 6d is screwed in terms of high resolution with normal frame rate crop_rec options...

Clockspeed and horizontal column factor determine the options.

5d3 has 24Mhz clockspeed and a column factor of 8
6d has 25.6Mhz clock speed and a column factor of 4
(most?) APS-C cams have 32Mhz clockspeed and a column factor of 4

While the 5d3 has a slow clock speed, it has DOUBLE the column factor.
The 6d has almost the same clock speed as the 5d3, but only a column factor of 4.
And while APS-C have, like the 6d, a column factor of 4, they have at least a much faster clock speed.

Why Canon why, you put the worst of both options in the 6d, slow clock speed and low column factor  :P

Levas

I did notice that the 'framing' preview from ML works in 14 bit mode, but when I switch to 14bitt lossless it most certainly disappears and doesn't come back. Only after setting it back to 14 bit and turn off and on the camera.

I see in the debug menu it has the following memory error:
No allocator for 675Kb at tweaks.c:3341, live_hiprio_task


Downloaded the newest experimental build Alex mentioned a few post earlier, made on 20 June 2018.
No problem anymore with 14 bit lossless and framing

mk11174

Quote from: Levas on June 21, 2018, 02:24:46 PM
Already changed everything on these lines and it's working now.

That's great! Glad to hear!

Note, anyone looking at this for 700D, FullRes is not Fullres, it was a work in progress, can't get stable Fullres going in Crop_Rec for some reason like can in ADTG tool, so it is basically set to the highest possible working resolution.
500D/T1i  550D/T2i  600D/T3i  700D/T5i

mk11174

Quote from: Levas on June 21, 2018, 01:38:25 PM
Found the Engio write address for 6d in the stubs.s file for 6d:


/** Engio **/
NSTUB(0xFF2ADE1C, _EngDrvOut)                               // AJ_EngDrvOut_1xVar_to_ShadowStruct
NSTUB(0xFF2AE134, _engio_write)
NSTUB(0xFF2ADEB8,  shamem_read)                             // AJ_0x8FB0_engio_struct_n_R0_manipulation_to_get_ptr
// NSTUB(    ???,  EngDrvOut)                               /* present on 7D_MASTER.203 */


Yeah, that was my bad, I checked the 6D stubs and put in the DrvOut address instead of the write, sorry, why always good to double check thing.
500D/T1i  550D/T2i  600D/T3i  700D/T5i

Levas

No problem, hate the debugging  :P, but learn a lot from it and it's satisfying when it finally works  8)

a1ex

Quote from: Levas on June 21, 2018, 02:38:46 PM
I see in the debug menu it has the following memory error:
No allocator for 675Kb at tweaks.c:3341, live_hiprio_task[/s]

I've got this one too, when using mlv_lite and adtg_gui (the latter ended up using about 2MB of memory - speed optimizations aren't exactly free).

Workaround: loaded mlv_rec and used that one for grayscale preview.

Levas

Quote from: a1ex on June 21, 2018, 03:05:16 PM
Workaround: loaded mlv_rec and used that one for grayscale preview.
Good one to know, cause I need the preview, live view is broken very quick ;D

Does anybody know what is normal behaviour for crop_rec.
The Crop_rec preset I made, which is made for 5x zoom mode and uses the 2K setting route in Crop_rec can be loaded in all video modes.

When I leave Crop_rec option on in ML menu, and turn off camera and turn it on again, it starts in Canon 1080p mode, but crop_rec still applies all the settings I wrote for 5x zoom, so messed up live view in 1080p as long as crop_rec is on.
Not a problem of course, just hit the zoom button until I'm in 5x zoom mode.
But was just wondering if this is normal behaviour.

a1ex

Not normal, but identifying video modes is not exactly easy. Canon firmware has a notification system (properties), but we get them too late, after the video mode is fully switched. I couldn't rely on properties to tell what video mode is going to be configured next (to know how to override it).

It took me quite a bit of fiddling to get something halfway usable, and there are still edge cases where it doesn't work well...

Levas

For checking if camera is in 1080P, 5x zoom or 720p mode, you could read out 800c register value, they're the same for each camera.
800c = 2 -> 1080p mode
800c = 0 -> 5 x zoom
800c = 4 ->720p mode

For an extra check to know if you're in PAL-25/50fps or NTSC-30/60fps or PAL/NTSC-24fps the only way is B timer, 6014.
But those B timer values are camera specific, so need to be write down for each camera.



Levas

Noticed the following difference in MLV_Lite between plain vanilla 14 bit raw recording and lossless 14 bit raw.
In experimental Crop_rec_4K build from 20th of June for 6d from download page.

14bit has other max vertical resolution then 14 bit lossless.
Made a Crop_rec preset for 6d, 2760x1200 resolution in 24fps.
-With 14 bit, max vertical resolution = 1200 Wonderfull  :D 8) just as I want it to be and pre tested it in adtg_gui  :)
-With 14 bit lossless, max vertical resolution =1206, nice those 6 pixels extra resolution, but it's out of live view range, it's in the white bar  :-\

This means 14 bit VS 14bit lossless uses different rules for resolution, it's probably in the 'modulo' stuff, rules like, can the pixel count be divided by 16, 8 or 2 that kind of stuff...

Can the resolution rules for 14 bit and 14 bit lossless be made the same, for consistency ?