1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <ddk/debug.h>
6
7#include "vim-display.h"
8#include "edid.h"
9
10zx_status_t get_vic(const display_mode_t* disp_timing, struct hdmi_param* p)
11{
12    // Monitor has its own preferred timings. Use that
13    p->timings.interlace_mode =     disp_timing->flags & MODE_FLAG_INTERLACED;
14    p->timings.pfreq =              (disp_timing->pixel_clock_10khz * 10); // KHz
15    //TODO: pixel repetition is 0 for most progressive. We don't support interlaced
16    p->timings.pixel_repeat =       0;
17    p->timings.hactive =            disp_timing->h_addressable;
18    p->timings.hblank =             disp_timing->h_blanking;
19    p->timings.hfront =             disp_timing->h_front_porch;
20    p->timings.hsync =              disp_timing->h_sync_pulse;
21    p->timings.htotal =             (p->timings.hactive) + (p->timings.hblank);
22    p->timings.hback =              (p->timings.hblank) - (p->timings.hfront + p->timings.hsync);
23    p->timings.hpol =               disp_timing->flags & MODE_FLAG_HSYNC_POSITIVE;
24
25    p->timings.vactive =            disp_timing->v_addressable;
26    p->timings.vblank0 =            disp_timing->v_blanking;
27    p->timings.vfront =             disp_timing->v_front_porch;
28    p->timings.vsync =              disp_timing->v_sync_pulse;
29    p->timings.vtotal =             (p->timings.vactive) + (p->timings.vblank0);
30    p->timings.vback =              (p->timings.vblank0) - (p->timings.vfront + p->timings.vsync);
31    p->timings.vpol =               disp_timing->flags & MODE_FLAG_VSYNC_POSITIVE;
32
33    //FIXE: VENC Repeat is undocumented. It seems to be only needed for the following
34    // resolutions: 1280x720p60, 1280x720p50, 720x480p60, 720x480i60, 720x576p50, 720x576i50
35    // For now, we will simply not support this feature.
36    p->timings.venc_pixel_repeat = 0;
37    // Let's make sure we support what we've got so far
38    if (p->timings.interlace_mode) {
39        return ZX_ERR_NOT_SUPPORTED;
40    }
41
42    if (p->timings.vactive == 2160) {
43        DISP_INFO("4K Monitor Detected.\n");
44
45        if (p->timings.pfreq == 533250) {
46            // FIXME: 4K with reduced blanking (533.25MHz) does not work
47            DISP_INFO("4K @ 30Hz\n");
48            p->timings.interlace_mode =     0;
49            p->timings.pfreq =              (297000); // KHz
50            p->timings.pixel_repeat =       0;
51            p->timings.hactive =            3840;
52            p->timings.hblank =             560;
53            p->timings.hfront =             176;
54            p->timings.hsync =              88;
55            p->timings.htotal =             (p->timings.hactive) + (p->timings.hblank);
56            p->timings.hback =              (p->timings.hblank) -
57                                               (p->timings.hfront + p->timings.hsync);
58            p->timings.hpol =               1;
59            p->timings.vactive =            2160;
60            p->timings.vblank0 =            90;
61            p->timings.vfront =             8;
62            p->timings.vsync =              10;
63            p->timings.vtotal =             (p->timings.vactive) + (p->timings.vblank0);
64            p->timings.vback =              (p->timings.vblank0) -
65                                               (p->timings.vfront + p->timings.vsync);
66            p->timings.vpol =               1;
67        }
68    }
69
70    if (p->timings.pfreq > 500000) {
71        p->is4K = true;
72    } else {
73        p->is4K = false;
74    }
75
76    if (p->timings.hactive * 3 == p->timings.vactive * 4) {
77        p->aspect_ratio = HDMI_ASPECT_RATIO_4x3;
78    } else if (p->timings.hactive * 9 == p->timings.vactive * 16) {
79        p->aspect_ratio = HDMI_ASPECT_RATIO_16x9;
80    } else {
81        p->aspect_ratio = HDMI_ASPECT_RATIO_NONE;
82    }
83
84    p->colorimetry = HDMI_COLORIMETRY_ITU601;
85
86    if (p->timings.pfreq > 500000) {
87        p->phy_mode = 1;
88    } else if (p->timings.pfreq > 200000) {
89        p->phy_mode = 2;
90    } else if (p->timings.pfreq > 100000) {
91        p->phy_mode = 3;
92    } else {
93        p->phy_mode = 4;
94    }
95
96    //TODO: We probably need a more sophisticated method for calculating
97    // clocks. This will do for now.
98    p->pll_p_24b.viu_channel =          1;
99    p->pll_p_24b.viu_type =             VIU_ENCP;
100    p->pll_p_24b.vid_pll_div =          VID_PLL_DIV_5;
101    p->pll_p_24b.vid_clk_div =          2;
102    p->pll_p_24b.hdmi_tx_pixel_div =    1;
103    p->pll_p_24b.encp_div =             1;
104    p->pll_p_24b.od1 =                  1;
105    p->pll_p_24b.od2 =                  1;
106    p->pll_p_24b.od3 =                  1;
107
108    p->pll_p_24b.hpll_clk_out = (p->timings.pfreq * 10);
109    while (p->pll_p_24b.hpll_clk_out < 2900000) {
110        if (p->pll_p_24b.od1 < 4) {
111            p->pll_p_24b.od1 *= 2;
112            p->pll_p_24b.hpll_clk_out *= 2;
113        } else if (p->pll_p_24b.od2 < 4) {
114            p->pll_p_24b.od2 *= 2;
115            p->pll_p_24b.hpll_clk_out *= 2;
116        } else if (p->pll_p_24b.od3 < 4) {
117            p->pll_p_24b.od3 *= 2;
118            p->pll_p_24b.hpll_clk_out *= 2;
119        } else {
120            return ZX_ERR_OUT_OF_RANGE;
121        }
122    }
123    if(p->pll_p_24b.hpll_clk_out > 6000000) {
124        DISP_ERROR("Something went wrong in clock calculation (pll_out = %d)\n",
125            p->pll_p_24b.hpll_clk_out);
126        return ZX_ERR_OUT_OF_RANGE;
127    }
128
129    return ZX_OK;
130}
131