2 #include "mipi_dsih_dphy.h"
\r
3 #define PRECISION_FACTOR (1000)
\r
4 /* Reference clock frequency divided by Input Frequency Division Ratio LIMITS */
\r
5 #define DPHY_DIV_UPPER_LIMIT (40000)
\r
6 #define DPHY_DIV_LOWER_LIMIT (1000)
\r
8 #ifdef DWC_MIPI_DPHY_BIDIR_TSMC40LP
\r
9 #define MIN_OUTPUT_FREQ (80)
\r
10 #elif defined DPHY2Btql
\r
11 #define MIN_OUTPUT_FREQ (200)
\r
14 dsih_error_t mipi_dsih_dphy_open(dphy_t * phy)
\r
18 return ERR_DSI_PHY_INVALID;
\r
20 else if ((phy->core_read_function == 0) || (phy->core_write_function == 0))
\r
22 return ERR_DSI_INVALID_IO;
\r
24 else if (phy->status == INITIALIZED)
\r
26 return ERR_DSI_PHY_INVALID;
\r
28 phy->status = NOT_INITIALIZED;
\r
29 mipi_dsih_dphy_reset(phy, 0);
\r
30 mipi_dsih_dphy_stop_wait_time(phy, 0x1C);
\r
31 mipi_dsih_dphy_no_of_lanes(phy, 1);
\r
32 mipi_dsih_dphy_clock_en(phy, 1);
\r
33 mipi_dsih_dphy_shutdown(phy, 1);
\r
34 mipi_dsih_dphy_reset(phy, 1);
\r
35 phy->status = INITIALIZED;
\r
38 dsih_error_t mipi_dsih_dphy_configure(dphy_t * phy, uint8_t no_of_lanes, uint32_t output_freq)
\r
40 uint32_t loop_divider = 0; /* (M) */
\r
41 uint32_t input_divider = 1; /* (N) */
\r
42 uint8_t data[4]; /* maximum data for now are 4 bytes per test mode*/
\r
43 uint8_t no_of_bytes = 0;
\r
44 uint8_t i = 0; /* iterator */
\r
45 uint8_t range = 0; /* ranges iterator */
\r
47 #ifdef DWC_MIPI_DPHY_BIDIR_TSMC40LP
\r
50 uint32_t freq; /* upper margin of frequency range */
\r
51 uint8_t hs_freq; /* hsfreqrange */
\r
52 uint8_t vco_range; /* vcorange */
\r
56 {90, 0x00, 0x01}, {100, 0x10, 0x01}, {110, 0x20, 0x01},
\r
57 {125, 0x01, 0x01}, {140, 0x11, 0x01}, {150, 0x21, 0x01},
\r
58 {160, 0x02, 0x01}, {180, 0x12, 0x03}, {200, 0x22, 0x03},
\r
59 {210, 0x03, 0x03}, {240, 0x13, 0x03}, {250, 0x23, 0x03},
\r
60 {270, 0x04, 0x07}, {300, 0x14, 0x07}, {330, 0x24, 0x07},
\r
61 {360, 0x15, 0x07}, {400, 0x25, 0x07}, {450, 0x06, 0x07},
\r
62 {500, 0x16, 0x07}, {550, 0x07, 0x0f}, {600, 0x17, 0x0f},
\r
63 {650, 0x08, 0x0f}, {700, 0x18, 0x0f}, {750, 0x09, 0x0f},
\r
64 {800, 0x19, 0x0f}, {850, 0x0A, 0x0f}, {900, 0x1A, 0x0f},
\r
65 {950, 0x2A, 0x0f}, {1000, 0x3A, 0x0f}
\r
69 uint32_t loop_div; /* upper limit of loop divider range */
\r
70 uint8_t cp_current; /* icpctrl */
\r
71 uint8_t lpf_resistor; /* lpfctrl */
\r
75 {32, 0x06, 0x10}, {64, 0x06, 0x10}, {128, 0x0C, 0x08},
\r
76 {256, 0x04, 0x04}, {512, 0x00, 0x01}, {768, 0x01, 0x01},
\r
79 #elif defined DPHY2Btql
\r
82 uint32_t loop_div; /* upper limit of loop divider range */
\r
83 uint8_t cp_current; /* icpctrl */
\r
84 uint8_t lpf_resistor; /* lpfctrl */
\r
88 {32, 0x0B, 0x00}, {64, 0x0A, 0x00}, {128, 0x09, 0x01},
\r
89 {256, 0x08, 0x03}, {512, 0x08, 0x07}, {768, 0x08, 0x0F},
\r
95 return ERR_DSI_INVALID_INSTANCE;
\r
97 if (phy->status < INITIALIZED)
\r
99 return ERR_DSI_INVALID_INSTANCE;
\r
101 if (output_freq < MIN_OUTPUT_FREQ)
\r
103 return ERR_DSI_PHY_FREQ_OUT_OF_BOUND;
\r
105 /* find M and N dividers */
\r
106 for (input_divider = 1 + (phy->reference_freq / DPHY_DIV_UPPER_LIMIT); ((phy->reference_freq / input_divider) >= DPHY_DIV_LOWER_LIMIT) && (!flag); input_divider++)
\r
107 { /* here the >= DPHY_DIV_LOWER_LIMIT is a phy constraint, formula should be above 1 MHz */
\r
108 if (((output_freq * input_divider) % (phy->reference_freq )) == 0)
\r
109 { /* values found */
\r
110 loop_divider = ((output_freq * input_divider) / (phy->reference_freq ));
\r
111 if (loop_divider >= 12)
\r
117 if ((!flag) || ((phy->reference_freq / input_divider) < DPHY_DIV_LOWER_LIMIT))
\r
118 { /* no exact value found in previous for loop */
\r
119 /* this solution is not favourable as jitter would be maximum */
\r
120 loop_divider = output_freq / DPHY_DIV_LOWER_LIMIT;
\r
121 input_divider = phy->reference_freq / DPHY_DIV_LOWER_LIMIT;
\r
124 { /* variable was incremented before exiting the loop */
\r
127 for (i = 0; (i < (sizeof(loop_bandwidth)/sizeof(loop_bandwidth[0]))) && (loop_divider > loop_bandwidth[i].loop_div); i++)
\r
131 if (i >= (sizeof(loop_bandwidth)/sizeof(loop_bandwidth[0])))
\r
133 return ERR_DSI_PHY_FREQ_OUT_OF_BOUND;
\r
135 /* get the PHY in power down mode (shutdownz=0) and reset it (rstz=0) to
\r
136 avoid transient periods in PHY operation during re-configuration procedures. */
\r
137 mipi_dsih_dphy_reset(phy, 0);
\r
138 mipi_dsih_dphy_clock_en(phy, 0);
\r
139 mipi_dsih_dphy_shutdown(phy, 0);
\r
140 /* provide an initial active-high test clear pulse in TESTCLR */
\r
141 mipi_dsih_dphy_test_clear(phy, 1);
\r
142 mipi_dsih_dphy_test_clear(phy, 0);
\r
143 #ifdef DWC_MIPI_DPHY_BIDIR_TSMC40LP
\r
145 for (range = 0; (range < (sizeof(ranges)/sizeof(ranges[0]))) && ((output_freq / 1000) > ranges[range].freq); range++)
\r
149 if (range >= (sizeof(ranges)/sizeof(ranges[0])))
\r
151 return ERR_DSI_PHY_FREQ_OUT_OF_BOUND;
\r
153 /* set up board depending on environment if any */
\r
154 if (phy->bsp_pre_config != 0)
\r
156 phy->bsp_pre_config(phy, 0);
\r
159 /* Jessica add - begin*/
\r
160 data[0] = 0x41;//0x44;//0x44;//0x40; //0x40: ok for 200 clock lane lpx /*about 52ns*/
\r
161 mipi_dsih_dphy_write(phy, 0x60, data, 1);
\r
162 data[0] = 0x0;//0xA6;//0xC6;//0xC6;//0x86; //0x48: ok for 200 prepare time
\r
163 mipi_dsih_dphy_write(phy, 0x61, data, 1);
\r
165 data[0] = 0x0;//0x6a;//0x6a;//0x4a; //0x4a: ok for 200 zero time
\r
166 mipi_dsih_dphy_write(phy, 0x62, data, 1);
\r
168 data[0] = 0x41;//0x44;//0x40;//0x40; // 0x40: ok for 200 data lane lpx /*about 52ns*/
\r
169 mipi_dsih_dphy_write(phy, 0x70, data, 1);
\r
171 data[0] = 0x0;//0x84;//0x96;//0x96;//0x86; //0x48: ok for 200 prepare time
\r
172 mipi_dsih_dphy_write(phy, 0x71, data, 1);
\r
174 data[0] = 0x0;;//0x44;//0x44;//0x40; //0x4a: ok for 200 zero time
\r
175 mipi_dsih_dphy_write(phy, 0x72, data, 1);
\r
178 //mipi_dsih_dphy_write(phy, 0x73, data, 1);
\r
181 //mipi_dsih_dphy_write(phy, 0x74, data, 1);
\r
183 /* Jessica add - end*/
\r
185 /* setup digital part */
\r
186 /* hs frequency range [7]|[6:1]|[0]*/
\r
187 data[0] = (0 << 7) | (ranges[range].hs_freq << 1) | 0;
\r
188 //data[0] = (0 << 7) | (0x23 << 1) | 0;
\r
189 /*From ASIC, we need unmask this code to make the frequency correct*/
\r
190 mipi_dsih_dphy_write(phy, 0x44, data, 1); //Jessica remove for more accurate frequency
\r
192 /* vco range [7]|[6:3]|[2:1]|[0] */
\r
193 data[0] = (1 << 7) | (ranges[range].vco_range << 3) | (0 << 1) | 0;
\r
194 mipi_dsih_dphy_write(phy, 0x10, data, 1); //Jessica
\r
195 /* PLL reserved|Input divider control|Loop Divider Control|Post Divider Ratio [7:6]|[5]|[4]|[3:0] */
\r
196 data[0] = (0x00 << 6) | (0x01 << 5) | (0x01 << 4) | (0x03 << 0); /* post divider default = 0x03 - it is only used for clock out 2*/
\r
197 mipi_dsih_dphy_write(phy, 0x19, data, 1); //Jessica
\r
198 #elif defined DPHY2Btql
\r
199 /* vco range [7:5]|[4]|[3]|[2:1]|[0] */
\r
200 data[0] = ((((output_freq / 1000) > 500 )? 1: 0) << 4) | (1 << 3) | (0 << 1) | 0;
\r
201 mipi_dsih_dphy_write(phy, 0x10, data, 1);
\r
203 /* PLL Lock bypass|charge pump current [7:4]|[3:0] */
\r
204 data[0] = (0x00 << 4) | (loop_bandwidth[i].cp_current << 0);
\r
205 mipi_dsih_dphy_write(phy, 0x11, data, 1); //Jessica
\r
206 /* bypass CP default|bypass LPF default| LPF resistor [7]|[6]|[5:0] */
\r
207 data[0] = (0x01 << 7) | (0x01 << 6) |(loop_bandwidth[i].lpf_resistor << 0);
\r
208 mipi_dsih_dphy_write(phy, 0x12, data, 1);
\r
209 /* PLL input divider ratio [7:0] */
\r
210 data[0] = input_divider - 1;
\r
211 mipi_dsih_dphy_write(phy, 0x17, data, 1); //Jessica
\r
214 mipi_dsih_dphy_write(phy, 0xB0, data, 1);
\r
218 no_of_bytes = 2; /* pll loop divider (code 0x18) takes only 2 bytes (10 bits in data) */
\r
219 for (i = 0; i < no_of_bytes; i++)
\r
221 data[i] = ((uint8_t)((((loop_divider - 1) >> (5 * i)) & 0x1F) | (i << 7) ));
\r
222 /* 7 is dependent on no_of_bytes
\r
223 make sure 5 bits only of value are written at a time */
\r
225 /* PLL loop divider ratio - SET no|reserved|feedback divider [7]|[6:5]|[4:0] */
\r
226 mipi_dsih_dphy_write(phy, 0x18, data, no_of_bytes);
\r
227 mipi_dsih_dphy_no_of_lanes(phy, no_of_lanes);
\r
228 mipi_dsih_dphy_stop_wait_time(phy, 0x1C);
\r
229 mipi_dsih_dphy_clock_en(phy, 1);
\r
230 mipi_dsih_dphy_shutdown(phy, 1);
\r
231 mipi_dsih_dphy_reset(phy, 1);
\r
236 dsih_error_t mipi_dsih_dphy_close(dphy_t * phy)
\r
240 return ERR_DSI_INVALID_INSTANCE;
\r
242 else if ((phy->core_read_function == 0) || (phy->core_write_function == 0))
\r
244 return ERR_DSI_INVALID_IO;
\r
246 if (phy->status < NOT_INITIALIZED)
\r
248 return ERR_DSI_INVALID_INSTANCE;
\r
250 mipi_dsih_dphy_reset(phy, 0);
\r
251 mipi_dsih_dphy_reset(phy, 1);
\r
252 mipi_dsih_dphy_shutdown(phy, 0);
\r
253 phy->status = NOT_INITIALIZED;
\r
256 void mipi_dsih_dphy_clock_en(dphy_t * instance, int en)
\r
258 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_RSTZ, en, 2, 1);
\r
260 void mipi_dsih_dphy_reset(dphy_t * instance, int reset)
\r
262 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_RSTZ, reset, 1, 1);
\r
264 void mipi_dsih_dphy_shutdown(dphy_t * instance, int powerup)
\r
266 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_RSTZ, powerup, 0, 1);
\r
268 void mipi_dsih_dphy_stop_wait_time(dphy_t * instance, uint8_t no_of_byte_cycles)
\r
270 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CFG, no_of_byte_cycles, 2, 8);
\r
272 void mipi_dsih_dphy_no_of_lanes(dphy_t * instance, uint8_t no_of_lanes)
\r
274 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CFG, no_of_lanes - 1, 0, 2);
\r
276 uint8_t mipi_dsih_dphy_get_no_of_lanes(dphy_t * instance)
\r
278 return mipi_dsih_dphy_read_part(instance, R_DSI_HOST_PHY_IF_CFG, 0, 2);
\r
280 void mipi_dsih_dphy_enable_hs_clk(dphy_t * instance, int enable)
\r
282 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, enable, 0, 1);
\r
284 dsih_error_t mipi_dsih_dphy_escape_mode_trigger(dphy_t * instance, uint8_t trigger_request)
\r
288 for (i = 0; i < 4; i++)
\r
290 sum += ((trigger_request >> i) & 1);
\r
293 { /* clear old trigger */
\r
294 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 0x00, 5, 4);
\r
295 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, trigger_request, 5, 4);
\r
296 for (i = 0; i < DSIH_PHY_ACTIVE_WAIT; i++)
\r
298 if(mipi_dsih_dphy_status(instance, 0x0010))
\r
303 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 0x00, 5, 4);
\r
304 if (i >= DSIH_PHY_ACTIVE_WAIT)
\r
306 return ERR_DSI_TIMEOUT;
\r
310 return ERR_DSI_INVALID_COMMAND;
\r
312 void mipi_dsih_dphy_ulps_data_lanes(dphy_t * instance, int enable)
\r
317 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 1, 3, 1);
\r
321 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 1, 4, 1);
\r
322 for (timeout = 0; timeout < DSIH_PHY_ACTIVE_WAIT; timeout++)
\r
323 { /* verify that the DPHY has left ULPM */
\r
324 /* mask 1010100100000 */
\r
325 if (mipi_dsih_dphy_status(instance, 0x1520) == 0)
\r
326 { /* wait at least 1ms */
\r
327 for (timeout = 0; timeout < ONE_MS_ACTIVE_WAIT; timeout++)
\r
334 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 0, 3, 1);
\r
335 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 0, 4, 1);
\r
338 void mipi_dsih_dphy_ulps_clk_lane(dphy_t * instance, int enable)
\r
343 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 0, 0, 1);
\r
344 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 1, 1, 1);
\r
348 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 1, 2, 1);
\r
349 for (timeout = 0; timeout < DSIH_PHY_ACTIVE_WAIT; timeout++)
\r
350 { /* verify that the DPHY has left ULPM */
\r
351 /* mask 1010100100000 */
\r
352 if (mipi_dsih_dphy_status(instance, 0x0004) == 0)
\r
353 { /* wait at least 1ms */
\r
354 for (timeout = 0; timeout < ONE_MS_ACTIVE_WAIT; timeout++)
\r
361 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 0, 1, 1);
\r
362 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_IF_CTRL, 0, 2, 1);
\r
365 uint32_t mipi_dsih_dphy_status(dphy_t * instance, uint16_t mask)
\r
367 return mipi_dsih_dphy_read_word(instance, R_DSI_HOST_PHY_STATUS) & mask;
\r
369 void mipi_dsih_dphy_test_clock(dphy_t * instance, int value)
\r
371 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_TST_CRTL0, value, 1, 1);
\r
373 void mipi_dsih_dphy_test_clear(dphy_t * instance, int value)
\r
375 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_TST_CRTL0, value, 0, 1);
\r
377 void mipi_dsih_dphy_test_en(dphy_t * instance, uint8_t on_falling_edge)
\r
379 mipi_dsih_dphy_write_part(instance, R_DSI_HOST_PHY_TST_CRTL1, on_falling_edge, 16, 1);
\r
381 uint8_t mipi_dsih_dphy_test_data_out(dphy_t * instance)
\r
383 return mipi_dsih_dphy_read_part(instance, R_DSI_HOST_PHY_TST_CRTL1, 8, 8);
\r
385 void mipi_dsih_dphy_test_data_in(dphy_t * instance, uint8_t test_data)
\r
387 mipi_dsih_dphy_write_word(instance, R_DSI_HOST_PHY_TST_CRTL1, test_data);
\r
389 void mipi_dsih_dphy_write(dphy_t * instance, uint8_t address, uint8_t * data, uint8_t data_length)
\r
394 #if ((defined DWC_MIPI_DPHY_BIDIR_TSMC40LP) || (defined DPHY2Btql))
\r
395 /* set the TESTCLK input high in preparation to latch in the desired test mode */
\r
396 mipi_dsih_dphy_test_clock(instance, 1);
\r
397 /* set the desired test code in the input 8-bit bus TESTDIN[7:0] */
\r
398 mipi_dsih_dphy_test_data_in(instance, address);
\r
399 /* set TESTEN input high */
\r
400 mipi_dsih_dphy_test_en(instance, 1);
\r
401 /* drive the TESTCLK input low; the falling edge captures the chosen test code into the transceiver */
\r
402 mipi_dsih_dphy_test_clock(instance, 0);
\r
403 /* set TESTEN input low to disable further test mode code latching */
\r
404 mipi_dsih_dphy_test_en(instance, 0);
\r
405 /* start writing MSB first */
\r
406 for (i = data_length; i > 0; i--)
\r
407 { /* set TESTDIN[7:0] to the desired test data appropriate to the chosen test mode */
\r
408 mipi_dsih_dphy_test_data_in(instance, data[i - 1]);
\r
409 /* pulse TESTCLK high to capture this test data into the macrocell; repeat these two steps as necessary */
\r
410 mipi_dsih_dphy_test_clock(instance, 1);
\r
411 mipi_dsih_dphy_test_clock(instance, 0);
\r
419 /* abstracting BSP */
\r
420 void mipi_dsih_dphy_write_word(dphy_t * instance, uint32_t reg_address, uint32_t data)
\r
422 if (instance->core_write_function != 0)
\r
424 instance->core_write_function(instance->address, reg_address, data);
\r
427 void mipi_dsih_dphy_write_part(dphy_t * instance, uint32_t reg_address, uint32_t data, uint8_t shift, uint8_t width)
\r
429 uint32_t mask = 0;
\r
430 uint32_t temp = 0;
\r
431 if (instance->core_read_function != 0)
\r
433 mask = (1 << width) - 1;
\r
434 temp = mipi_dsih_dphy_read_word(instance, reg_address);
\r
435 temp &= ~(mask << shift);
\r
436 temp |= (data & mask) << shift;
\r
437 mipi_dsih_dphy_write_word(instance, reg_address, temp);
\r
440 uint32_t mipi_dsih_dphy_read_word(dphy_t * instance, uint32_t reg_address)
\r
442 if (instance->core_read_function == 0)
\r
444 return ERR_DSI_INVALID_IO;
\r
446 return instance->core_read_function(instance->address, reg_address);
\r
448 uint32_t mipi_dsih_dphy_read_part(dphy_t * instance, uint32_t reg_address, uint8_t shift, uint8_t width)
\r
450 return (mipi_dsih_dphy_read_word(instance, reg_address) >> shift) & ((1 << width) - 1);
\r