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 <assert.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <ddk/device.h>
12 #include <ddk/driver.h>
13 #include <ddk/debug.h>
14 #include <ddk/binding.h>
15 #include <ddk/io-buffer.h>
16 #include <ddk/platform-defs.h>
17 #include <ddk/protocol/platform/device.h>
18 
19 #include <zircon/syscalls.h>
20 #include <zircon/assert.h>
21 #include <hw/reg.h>
22 #include "vim-display.h"
23 #include "hdmitx.h"
24 
25 #define WAIT_FOR_PLL_LOCKED(reg)                \
26     do {                            \
27         err = 0;                \
28         unsigned int st = 0; \
29         int cnt = 10000;          \
30         while (cnt--) {                                 \
31             usleep(5);              \
32             st = !!(READ32_HHI_REG(reg) & (1 << 31));  \
33             if (st) {                 \
34                 err = 0; \
35                 break;              \
36             } else { /* reset hpll */         \
37                 SET_BIT32(HHI, reg, 1, 1, 28); \
38                 SET_BIT32(HHI, reg, 0, 1, 28); \
39             }                   \
40         }                       \
41         DISP_ERROR("pll[0x%x] reset %d times\n", reg, 10000 - cnt);\
42         if (cnt <= 0) \
43             err = 1; \
44     } while (err)
45 
configure_hpll_clk_out(vim2_display_t * display,uint32_t hpll)46 void configure_hpll_clk_out(vim2_display_t* display, uint32_t hpll)
47 {
48     int err = 0;
49     uint32_t regVal;
50     float desired_pll = (float)hpll / (float) 24000;
51     uint8_t n;
52     uint16_t m;
53     n = (uint8_t)desired_pll;
54     m = static_cast<uint16_t>(((float)desired_pll - (float)n) * 1000);
55 
56     DISP_ERROR("Desired PLL = %f (m = %d, n = %d) (hpll = %d)\n", desired_pll, m, n, hpll);
57 
58     regVal = 0;
59     regVal |= PLL_CNTL_ENABLE;
60     regVal |= PLL_CNTL_N(1);
61     regVal &= ~(PLL_CNTL_LOCK);
62     WRITE32_REG(HHI, HHI_HDMI_PLL_CNTL, regVal);
63     SET_BIT32(HHI, HHI_HDMI_PLL_CNTL, n, PLL_CNTL_M_BITS, PLL_CNTL_M_START);
64 
65     WRITE32_REG(HHI, HHI_HDMI_PLL_CNTL1, 0x800cb000);
66     SET_BIT32(HHI, HHI_HDMI_PLL_CNTL1, m, PLL_CNTL1_DIV_FRAC_BITS, PLL_CNTL1_DIV_FRAC_START);
67 
68     WRITE32_REG(HHI, HHI_HDMI_PLL_CNTL2, 0x860f30c4);
69     WRITE32_REG(HHI, HHI_HDMI_PLL_CNTL3, 0x0c8e0000);
70     WRITE32_REG(HHI, HHI_HDMI_PLL_CNTL4, 0x001fa729);
71     WRITE32_REG(HHI, HHI_HDMI_PLL_CNTL5, 0x01a31500);
72     SET_BIT32(HHI, HHI_HDMI_PLL_CNTL, 0x1, 1, 28);
73     SET_BIT32(HHI, HHI_HDMI_PLL_CNTL, 0x0, 1, 28);
74     WAIT_FOR_PLL_LOCKED(HHI_HDMI_PLL_CNTL);
75     DISP_INFO("HPLL: 0x%x\n", READ32_HHI_REG(HHI_HDMI_PLL_CNTL));
76 }
77 
configure_od3_div(vim2_display_t * display,uint32_t div_sel)78 void configure_od3_div(vim2_display_t* display, uint32_t div_sel)
79 {
80     int shift_val = 0;
81     int shift_sel = 0;
82 
83     /* When div 6.25, need to reset vid_pll_div */
84     if (div_sel == VID_PLL_DIV_6p25) {
85         usleep(1);
86         SET_BIT32(PRESET, PRESET0_REGISTER, 1, 1, 7);
87     }
88     // Disable the output clock
89     SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 0, 1, 19);
90     SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 0, 1, 15);
91 
92     switch (div_sel) {
93     case VID_PLL_DIV_1:      shift_val = 0xFFFF; shift_sel = 0; break;
94     case VID_PLL_DIV_2:      shift_val = 0x0aaa; shift_sel = 0; break;
95     case VID_PLL_DIV_3:      shift_val = 0x0db6; shift_sel = 0; break;
96     case VID_PLL_DIV_3p5:    shift_val = 0x36cc; shift_sel = 1; break;
97     case VID_PLL_DIV_3p75:   shift_val = 0x6666; shift_sel = 2; break;
98     case VID_PLL_DIV_4:      shift_val = 0x0ccc; shift_sel = 0; break;
99     case VID_PLL_DIV_5:      shift_val = 0x739c; shift_sel = 2; break;
100     case VID_PLL_DIV_6:      shift_val = 0x0e38; shift_sel = 0; break;
101     case VID_PLL_DIV_6p25:   shift_val = 0x0000; shift_sel = 3; break;
102     case VID_PLL_DIV_7:      shift_val = 0x3c78; shift_sel = 1; break;
103     case VID_PLL_DIV_7p5:    shift_val = 0x78f0; shift_sel = 2; break;
104     case VID_PLL_DIV_12:     shift_val = 0x0fc0; shift_sel = 0; break;
105     case VID_PLL_DIV_14:     shift_val = 0x3f80; shift_sel = 1; break;
106     case VID_PLL_DIV_15:     shift_val = 0x7f80; shift_sel = 2; break;
107     case VID_PLL_DIV_2p5:    shift_val = 0x5294; shift_sel = 2; break;
108     default:
109         DISP_ERROR("Error: clocks_set_vid_clk_div:  Invalid parameter\n");
110         break;
111     }
112 
113     if (shift_val == 0xffff ) {      // if divide by 1
114         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 1, 1, 18);
115     } else {
116         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 0, 1, 18);
117         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 0, 2, 16);
118         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 0, 1, 15);
119         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 0, 14,  0);
120 
121         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, shift_sel, 2, 16);
122         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 1, 1, 15);
123         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, shift_val, 14,  0);
124         SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 0, 1, 15);
125     }
126     // Enable the final output clock
127     SET_BIT32(HHI, HHI_VID_PLL_CLK_DIV, 1, 1, 19);
128 
129 }
130 
configure_pll(vim2_display_t * display,const struct hdmi_param * p,const struct pll_param * pll)131 zx_status_t configure_pll(vim2_display_t* display, const struct hdmi_param* p, const struct pll_param* pll)
132 {
133     // Set VIU Mux Ctrl
134     SET_BIT32(VPU, VPU_VPU_VIU_VENC_MUX_CTRL, pll->viu_type, 2, (pll->viu_channel == 1) ? 0 : 2);
135     SET_BIT32(HHI, HHI_HDMI_CLK_CNTL, 0, 3, 9);
136     SET_BIT32(HHI, HHI_HDMI_CLK_CNTL, 0, 7, 0);
137     SET_BIT32(HHI, HHI_HDMI_CLK_CNTL, 1, 1, 8);
138     configure_hpll_clk_out(display, pll->hpll_clk_out);
139 
140     //set od1
141     SET_BIT32(HHI, HHI_HDMI_PLL_CNTL2, (pll->od1 >> 1), 2, 21);
142 
143     //set od2
144     SET_BIT32(HHI, HHI_HDMI_PLL_CNTL2, (pll->od2 >> 1), 2, 23);
145 
146     //set od1
147     SET_BIT32(HHI, HHI_HDMI_PLL_CNTL2, (pll->od3 >> 1), 2, 19);
148 
149     configure_od3_div(display, pll->vid_pll_div);
150 
151     SET_BIT32(HHI, HHI_VID_CLK_CNTL, 0, 3, 16);   // select vid_pll_clk
152     SET_BIT32(HHI, HHI_VID_CLK_DIV, (pll->vid_clk_div == 0) ? 0 : (pll->vid_clk_div - 1), 8, 0);
153     SET_BIT32(HHI, HHI_VID_CLK_CNTL, 7, 3, 0);
154 
155     SET_BIT32(HHI, HHI_HDMI_CLK_CNTL, (pll->hdmi_tx_pixel_div == 12) ?
156         4 : (pll->hdmi_tx_pixel_div >> 1), 4, 16);
157     SET_BIT32(HHI, HHI_VID_CLK_CNTL2, 1, 1, 5);   //enable gate
158 
159 
160     if (pll->encp_div != (uint32_t)-1) {
161         SET_BIT32(HHI, HHI_VID_CLK_DIV, (pll->encp_div == 12) ?
162             4 : (pll->encp_div >> 1), 4, 24);
163         SET_BIT32(HHI, HHI_VID_CLK_CNTL2, 1, 1, 2);   //enable gate
164         SET_BIT32(HHI, HHI_VID_CLK_CNTL, 1, 1, 19);
165     }
166     if (pll->enci_div != (uint32_t)-1) {
167         SET_BIT32(HHI, HHI_VID_CLK_DIV, (pll->encp_div == 12) ?
168             4 : (pll->encp_div >> 1), 4, 28);
169         SET_BIT32(HHI, HHI_VID_CLK_CNTL2, 1, 1, 0);   //enable gate
170         SET_BIT32(HHI, HHI_VID_CLK_CNTL, 1, 1, 19);
171     }
172 
173     DISP_INFO("done!\n");
174     return ZX_OK;
175 }
176