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 "aml-mipi-phy.h"
6 #include <ddk/debug.h>
7 
8 namespace astro_display {
9 
10 #define READ32_MIPI_DSI_REG(a)              mipi_dsi_mmio_->Read32(a)
11 #define WRITE32_MIPI_DSI_REG(a, v)          mipi_dsi_mmio_->Write32(v, a)
12 
13 #define READ32_DSI_PHY_REG(a)               dsi_phy_mmio_->Read32(a)
14 #define WRITE32_DSI_PHY_REG(a, v)           dsi_phy_mmio_->Write32(v, a)
15 
16 template<typename T>
NsToLaneByte(T x,uint32_t lanebytetime)17 constexpr inline uint8_t NsToLaneByte(T x, uint32_t lanebytetime) {
18     return (static_cast<uint8_t>((x + lanebytetime - 1) / lanebytetime) & 0xFF);
19 }
20 
21 constexpr uint32_t kUnit = (1 * 1000 * 1000 * 100);
22 constexpr uint32_t kPhyDelay = 6;
23 
PhyCfgLoad(uint32_t bitrate)24 zx_status_t AmlMipiPhy::PhyCfgLoad(uint32_t bitrate) {
25     ZX_DEBUG_ASSERT(initialized_);
26 
27     // According to MIPI -PHY Spec, we need to define Unit Interval (UI).
28     // This UI is defined as the time it takes to send a bit (i.e. bitrate)
29     // The x100 is to ensure the ui is not rounded too much (i.e. 2.56 --> 256)
30     // However, since we have introduced x100, we need to make sure we include x100
31     // to all the PHY timings that are in ns units.
32     const uint32_t ui = kUnit / (bitrate / 1000);
33 
34     // Calculate values will be rounded by the lanebyteclk
35     const uint32_t lanebytetime = ui * 8;
36 
37     // lp_tesc:TX Excape Clock Division factor (from linebyteclk). Round up to units of ui
38     dsi_phy_cfg_.lp_tesc = NsToLaneByte(DPHY_TIME_LP_TESC, lanebytetime);
39 
40     // lp_lpx: Transmit length of any LP state period
41     dsi_phy_cfg_.lp_lpx = NsToLaneByte(DPHY_TIME_LP_LPX, lanebytetime);
42 
43     // lp_ta_sure
44     dsi_phy_cfg_.lp_ta_sure = NsToLaneByte(DPHY_TIME_LP_TA_SURE, lanebytetime);
45 
46     // lp_ta_go
47     dsi_phy_cfg_.lp_ta_go = NsToLaneByte(DPHY_TIME_LP_TA_GO, lanebytetime);
48 
49     // lp_ta_get
50     dsi_phy_cfg_.lp_ta_get = NsToLaneByte(DPHY_TIME_LP_TA_GET, lanebytetime);
51 
52     // hs_exit
53     dsi_phy_cfg_.hs_exit = NsToLaneByte(DPHY_TIME_HS_EXIT, lanebytetime);
54 
55     // clk-_prepare
56     dsi_phy_cfg_.clk_prepare = NsToLaneByte(DPHY_TIME_CLK_PREPARE, lanebytetime);
57 
58     // clk_zero
59     dsi_phy_cfg_.clk_zero = NsToLaneByte(DPHY_TIME_CLK_ZERO(ui), lanebytetime);
60 
61     // clk_pre
62     dsi_phy_cfg_.clk_pre = NsToLaneByte(DPHY_TIME_CLK_PRE(ui), lanebytetime);
63 
64     // init
65     dsi_phy_cfg_.init = NsToLaneByte(DPHY_TIME_INIT, lanebytetime);
66 
67     // wakeup
68     dsi_phy_cfg_.wakeup = NsToLaneByte(DPHY_TIME_WAKEUP, lanebytetime);
69 
70     // clk_trail
71     dsi_phy_cfg_.clk_trail = NsToLaneByte(DPHY_TIME_CLK_TRAIL, lanebytetime);
72 
73     // clk_post
74     dsi_phy_cfg_.clk_post = NsToLaneByte(DPHY_TIME_CLK_POST(ui), lanebytetime);
75 
76     // hs_trail
77     dsi_phy_cfg_.hs_trail = NsToLaneByte(DPHY_TIME_HS_TRAIL(ui), lanebytetime);
78 
79     // hs_prepare
80     dsi_phy_cfg_.hs_prepare = NsToLaneByte(DPHY_TIME_HS_PREPARE(ui), lanebytetime);
81 
82     // hs_zero
83     dsi_phy_cfg_.hs_zero = NsToLaneByte(DPHY_TIME_HS_ZERO(ui), lanebytetime);
84 
85     // Ensure both clk-trail and hs-trail do not exceed Teot (End of Transmission Time)
86     const uint32_t time_req_max = NsToLaneByte(DPHY_TIME_EOT(ui), lanebytetime);
87     if ((dsi_phy_cfg_.clk_trail > time_req_max) ||
88         (dsi_phy_cfg_.hs_trail > time_req_max)) {
89         DISP_ERROR("Invalid clk-trail and/or hs-trail exceed Teot!\n");
90         DISP_ERROR("clk-trail = 0x%02x, hs-trail =  0x%02x, Teot = 0x%02x\n",
91                    dsi_phy_cfg_.clk_trail, dsi_phy_cfg_.hs_trail, time_req_max );
92         return ZX_ERR_OUT_OF_RANGE;
93     }
94 
95     DISP_SPEW("lp_tesc     = 0x%02x\n"
96                 "lp_lpx      = 0x%02x\n"
97                 "lp_ta_sure  = 0x%02x\n"
98                 "lp_ta_go    = 0x%02x\n"
99                 "lp_ta_get   = 0x%02x\n"
100                 "hs_exit     = 0x%02x\n"
101                 "hs_trail    = 0x%02x\n"
102                 "hs_zero     = 0x%02x\n"
103                 "hs_prepare  = 0x%02x\n"
104                 "clk_trail   = 0x%02x\n"
105                 "clk_post    = 0x%02x\n"
106                 "clk_zero    = 0x%02x\n"
107                 "clk_prepare = 0x%02x\n"
108                 "clk_pre     = 0x%02x\n"
109                 "init        = 0x%02x\n"
110                 "wakeup      = 0x%02x\n\n",
111                 dsi_phy_cfg_.lp_tesc,
112                 dsi_phy_cfg_.lp_lpx,
113                 dsi_phy_cfg_.lp_ta_sure,
114                 dsi_phy_cfg_.lp_ta_go,
115                 dsi_phy_cfg_.lp_ta_get,
116                 dsi_phy_cfg_.hs_exit,
117                 dsi_phy_cfg_.hs_trail,
118                 dsi_phy_cfg_.hs_zero,
119                 dsi_phy_cfg_.hs_prepare,
120                 dsi_phy_cfg_.clk_trail,
121                 dsi_phy_cfg_.clk_post,
122                 dsi_phy_cfg_.clk_zero,
123                 dsi_phy_cfg_.clk_prepare,
124                 dsi_phy_cfg_.clk_pre,
125                 dsi_phy_cfg_.init,
126                 dsi_phy_cfg_.wakeup);
127     return ZX_OK;
128 }
129 
PhyInit()130 void AmlMipiPhy::PhyInit()
131 {
132     // Enable phy clock.
133     WRITE32_REG(DSI_PHY, MIPI_DSI_PHY_CTRL, PHY_CTRL_TXDDRCLK_EN |
134                 PHY_CTRL_DDRCLKPATH_EN | PHY_CTRL_CLK_DIV_COUNTER | PHY_CTRL_CLK_DIV_EN |
135                 PHY_CTRL_BYTECLK_EN);
136 
137     // Toggle PHY CTRL RST
138     SET_BIT32(DSI_PHY, MIPI_DSI_PHY_CTRL, 1, PHY_CTRL_RST_START, PHY_CTRL_RST_BITS);
139     SET_BIT32(DSI_PHY, MIPI_DSI_PHY_CTRL, 0, PHY_CTRL_RST_START, PHY_CTRL_RST_BITS);
140 
141     WRITE32_REG(DSI_PHY, MIPI_DSI_CLK_TIM,
142                 (dsi_phy_cfg_.clk_trail | (dsi_phy_cfg_.clk_post << 8) |
143                 (dsi_phy_cfg_.clk_zero << 16) |
144                 (dsi_phy_cfg_.clk_prepare << 24)));
145 
146     WRITE32_REG(DSI_PHY, MIPI_DSI_CLK_TIM1, dsi_phy_cfg_.clk_pre);
147 
148     WRITE32_REG(DSI_PHY, MIPI_DSI_HS_TIM,
149                 (dsi_phy_cfg_.hs_exit | (dsi_phy_cfg_.hs_trail << 8) |
150                 (dsi_phy_cfg_.hs_zero << 16) |
151                 (dsi_phy_cfg_.hs_prepare << 24)));
152 
153     WRITE32_REG(DSI_PHY, MIPI_DSI_LP_TIM,
154                 (dsi_phy_cfg_.lp_lpx | (dsi_phy_cfg_.lp_ta_sure << 8) |
155                 (dsi_phy_cfg_.lp_ta_go << 16) | (dsi_phy_cfg_.lp_ta_get << 24)));
156 
157     WRITE32_REG(DSI_PHY, MIPI_DSI_ANA_UP_TIM, ANA_UP_TIME);
158     WRITE32_REG(DSI_PHY, MIPI_DSI_INIT_TIM, dsi_phy_cfg_.init);
159     WRITE32_REG(DSI_PHY, MIPI_DSI_WAKEUP_TIM, dsi_phy_cfg_.wakeup);
160     WRITE32_REG(DSI_PHY, MIPI_DSI_LPOK_TIM,  LPOK_TIME);
161     WRITE32_REG(DSI_PHY, MIPI_DSI_ULPS_CHECK,  ULPS_CHECK_TIME);
162     WRITE32_REG(DSI_PHY, MIPI_DSI_LP_WCHDOG,  LP_WCHDOG_TIME);
163     WRITE32_REG(DSI_PHY, MIPI_DSI_TURN_WCHDOG,  TURN_WCHDOG_TIME);
164 
165     WRITE32_REG(DSI_PHY, MIPI_DSI_CHAN_CTRL, 0);
166 }
167 
168 
169 // This function checks two things in order to decide whether the PHY is
170 // ready or not. LOCK Bit and StopStateClk bit. According to spec, once these
171 // are set, PHY has completed initialization
WaitforPhyReady()172 zx_status_t AmlMipiPhy::WaitforPhyReady()
173 {
174     int timeout = DPHY_TIMEOUT;
175     while ((GET_BIT32(MIPI_DSI, DW_DSI_PHY_STATUS, PHY_STATUS_PHY_LOCK, 1) == 0) &&
176            timeout--) {
177         zx_nanosleep(zx_deadline_after(ZX_USEC(kPhyDelay)));
178     }
179     if (timeout <= 0) {
180         DISP_ERROR("Timeout! D-PHY did not lock\n");
181         return ZX_ERR_TIMED_OUT;
182     }
183 
184     timeout = DPHY_TIMEOUT;
185     while ((GET_BIT32(MIPI_DSI, DW_DSI_PHY_STATUS, PHY_STATUS_PHY_STOPSTATECLKLANE, 1) == 0) &&
186            timeout--) {
187         zx_nanosleep(zx_deadline_after(ZX_USEC(kPhyDelay)));
188     }
189     if (timeout <= 0) {
190         DISP_ERROR("Timeout! D-PHY StopStateClk not set\n");
191         return ZX_ERR_TIMED_OUT;
192     }
193     return ZX_OK;
194 }
195 
Shutdown()196 void AmlMipiPhy::Shutdown() {
197     ZX_DEBUG_ASSERT(initialized_);
198 
199     if (!phy_enabled_) {
200         return;
201     }
202 
203     // Power down DSI
204     WRITE32_REG(MIPI_DSI, DW_DSI_PWR_UP, PWR_UP_RST);
205     WRITE32_REG(DSI_PHY, MIPI_DSI_CHAN_CTRL, 0x1f);
206     SET_BIT32(DSI_PHY, MIPI_DSI_PHY_CTRL, 0, 7, 1);
207     phy_enabled_ = false;
208 }
209 
Startup()210 zx_status_t AmlMipiPhy::Startup() {
211     ZX_DEBUG_ASSERT(initialized_);
212 
213     if (phy_enabled_) {
214         return ZX_OK;
215     }
216 
217     // Power up DSI
218     WRITE32_REG(MIPI_DSI, DW_DSI_PWR_UP, PWR_UP_ON);
219 
220     // Setup Parameters of DPHY
221     // Below we are sending test code 0x44 with parameter 0x74. This means
222     // we are setting up the phy to operate in 1050-1099 Mbps mode
223     // TODO(payamm): Find out why 0x74 was selected
224     WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL1, 0x00010044);
225     WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x2);
226     WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x0);
227     WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL1, 0x00000074);
228     WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x2);
229     WRITE32_REG(MIPI_DSI, DW_DSI_PHY_TST_CTRL0, 0x0);
230 
231     // Power up D-PHY
232     WRITE32_REG(MIPI_DSI, DW_DSI_PHY_RSTZ, PHY_RSTZ_PWR_UP);
233 
234     // Setup PHY Timing parameters
235     PhyInit();
236 
237     // Wait for PHY to be read
238     zx_status_t status;
239     if ((status = WaitforPhyReady()) != ZX_OK) {
240         // no need to print additional info.
241         return status;
242     }
243 
244     // Trigger a sync active for esc_clk
245     SET_BIT32(DSI_PHY, MIPI_DSI_PHY_CTRL, 1, 1, 1);
246 
247     // Startup transfer, default lpclk
248     WRITE32_REG(MIPI_DSI, DW_DSI_LPCLK_CTRL, (0x1 << LPCLK_CTRL_AUTOCLKLANE_CTRL) |
249                 (0x1 << LPCLK_CTRL_TXREQUESTCLKHS));
250 
251     phy_enabled_ = true;
252     return ZX_OK;
253 }
254 
Init(zx_device_t * parent,uint32_t lane_num)255 zx_status_t AmlMipiPhy::Init(zx_device_t* parent, uint32_t lane_num) {
256     if (initialized_) {
257         return ZX_OK;
258     }
259 
260     num_of_lanes_ = lane_num;
261 
262     zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev_);
263     if (status != ZX_OK) {
264         DISP_ERROR("AmlMipiPhy: Could not get ZX_PROTOCOL_PDEV protocol\n");
265         return status;
266     }
267 
268     // Map Mipi Dsi and Dsi Phy registers
269     mmio_buffer_t mmio;
270     status = pdev_map_mmio_buffer2(&pdev_, MMIO_MPI_DSI, ZX_CACHE_POLICY_UNCACHED_DEVICE,
271                                   &mmio);
272     if (status != ZX_OK) {
273         DISP_ERROR("AmlMipiPhy: Could not map MIPI DSI mmio\n");
274         return status;
275     }
276     mipi_dsi_mmio_ = ddk::MmioBuffer(mmio);
277 
278     status = pdev_map_mmio_buffer2(&pdev_, MMIO_DSI_PHY, ZX_CACHE_POLICY_UNCACHED_DEVICE,
279                                   &mmio);
280     if (status != ZX_OK) {
281         DISP_ERROR("AmlMipiPhy: Could not map DSI PHY mmio\n");
282         return status;
283     }
284     dsi_phy_mmio_ = ddk::MmioBuffer(mmio);
285 
286     initialized_ = true;
287     return ZX_OK;
288 }
289 
Dump()290 void AmlMipiPhy::Dump() {
291     ZX_DEBUG_ASSERT(initialized_);
292     DISP_INFO("%s: DUMPING PHY REGS\n", __func__);
293     DISP_INFO("MIPI_DSI_PHY_CTRL = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_PHY_CTRL));
294     DISP_INFO("MIPI_DSI_CHAN_CTRL = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_CHAN_CTRL));
295     DISP_INFO("MIPI_DSI_CHAN_STS = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_CHAN_STS));
296     DISP_INFO("MIPI_DSI_CLK_TIM = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_CLK_TIM));
297     DISP_INFO("MIPI_DSI_HS_TIM = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_HS_TIM));
298     DISP_INFO("MIPI_DSI_LP_TIM = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_LP_TIM));
299     DISP_INFO("MIPI_DSI_ANA_UP_TIM = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_ANA_UP_TIM));
300     DISP_INFO("MIPI_DSI_INIT_TIM = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_INIT_TIM));
301     DISP_INFO("MIPI_DSI_WAKEUP_TIM = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_WAKEUP_TIM));
302     DISP_INFO("MIPI_DSI_LPOK_TIM = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_LPOK_TIM));
303     DISP_INFO("MIPI_DSI_LP_WCHDOG = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_LP_WCHDOG));
304     DISP_INFO("MIPI_DSI_ANA_CTRL = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_ANA_CTRL));
305     DISP_INFO("MIPI_DSI_CLK_TIM1 = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_CLK_TIM1));
306     DISP_INFO("MIPI_DSI_TURN_WCHDOG = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_TURN_WCHDOG));
307     DISP_INFO("MIPI_DSI_ULPS_CHECK = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_ULPS_CHECK));
308     DISP_INFO("MIPI_DSI_TEST_CTRL0 = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_TEST_CTRL0));
309     DISP_INFO("MIPI_DSI_TEST_CTRL1 = 0x%x\n", READ32_REG(DSI_PHY, MIPI_DSI_TEST_CTRL1));
310     DISP_INFO("\n");
311 
312     DISP_INFO("#############################\n");
313     DISP_INFO("Dumping dsi_phy_cfg structure:\n");
314     DISP_INFO("#############################\n");
315     DISP_INFO("lp_tesc = 0x%x (%u)\n", dsi_phy_cfg_.lp_tesc,
316               dsi_phy_cfg_.lp_tesc);
317     DISP_INFO("lp_lpx = 0x%x (%u)\n", dsi_phy_cfg_.lp_lpx,
318               dsi_phy_cfg_.lp_lpx);
319     DISP_INFO("lp_ta_sure = 0x%x (%u)\n", dsi_phy_cfg_.lp_ta_sure,
320               dsi_phy_cfg_.lp_ta_sure);
321     DISP_INFO("lp_ta_go = 0x%x (%u)\n", dsi_phy_cfg_.lp_ta_go,
322               dsi_phy_cfg_.lp_ta_go);
323     DISP_INFO("lp_ta_get = 0x%x (%u)\n", dsi_phy_cfg_.lp_ta_get,
324               dsi_phy_cfg_.lp_ta_get);
325     DISP_INFO("hs_exit = 0x%x (%u)\n", dsi_phy_cfg_.hs_exit,
326               dsi_phy_cfg_.hs_exit);
327     DISP_INFO("hs_trail = 0x%x (%u)\n", dsi_phy_cfg_.hs_trail,
328               dsi_phy_cfg_.hs_trail);
329     DISP_INFO("hs_zero = 0x%x (%u)\n", dsi_phy_cfg_.hs_zero,
330               dsi_phy_cfg_.hs_zero);
331     DISP_INFO("hs_prepare = 0x%x (%u)\n", dsi_phy_cfg_.hs_prepare,
332               dsi_phy_cfg_.hs_prepare);
333     DISP_INFO("clk_trail = 0x%x (%u)\n", dsi_phy_cfg_.clk_trail,
334               dsi_phy_cfg_.clk_trail);
335     DISP_INFO("clk_post = 0x%x (%u)\n", dsi_phy_cfg_.clk_post,
336               dsi_phy_cfg_.clk_post);
337     DISP_INFO("clk_zero = 0x%x (%u)\n", dsi_phy_cfg_.clk_zero,
338               dsi_phy_cfg_.clk_zero);
339     DISP_INFO("clk_prepare = 0x%x (%u)\n", dsi_phy_cfg_.clk_prepare,
340               dsi_phy_cfg_.clk_prepare);
341     DISP_INFO("clk_pre = 0x%x (%u)\n", dsi_phy_cfg_.clk_pre,
342               dsi_phy_cfg_.clk_pre);
343     DISP_INFO("init = 0x%x (%u)\n", dsi_phy_cfg_.init,
344               dsi_phy_cfg_.init);
345     DISP_INFO("wakeup = 0x%x (%u)\n", dsi_phy_cfg_.wakeup,
346               dsi_phy_cfg_.wakeup);
347 }
348 
349 } // namespace astro_display
350