1 /*
2 * Copyright (c) 2016, Freescale Semiconductor, Inc.
3 * Copyright 2016-2017, 2020-2021 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_ssd1963.h"
10
11 /*******************************************************************************
12 * Definitations
13 ******************************************************************************/
14 #define SSD1963_ORIENTATION_MODE_MASK \
15 (SSD1963_ADDR_MODE_PAGE_ADDR_ORDER | SSD1963_ADDR_MODE_PAG_COL_ADDR_ORDER | SSD1963_ADDR_MODE_COL_ADDR_ORDER)
16
17 #define SSD1963_FLIP_MODE_MASK (SSD1963_ADDR_MODE_FLIP_VERT | SSD1963_ADDR_MODE_FLIP_HORZ)
18
19 /* The PLL VCO clock must be in the range of (250MHz, 800MHz). */
20 #define SSD1963_VCO_MIN_HZ 250000000U
21 #define SSD1963_VCO_MAX_HZ 800000000U
22 #define SSD1963_PLL_MULTI_MIN 0x0U
23 #define SSD1963_PLL_MULTI_MAX 0xFFU
24 #define SSD1963_PLL_DIV_MIN 0x0U
25 #define SSD1963_PLL_DIV_MAX 0x1FU
26
27 /* The PLL output frequency will be configured to about 100MHz. */
28 #define SSD1963_PLL_FREQ_HZ 100000000U
29
30 /* The max value of LCDC_FPR to generate the lshift clock (pixel clock). */
31 #define SSD1963_LCDC_FPR_MAX 0xFFFFFU
32
33 #if (SSD1963_DATA_WITDH != 16) && (SSD1963_DATA_WITDH != 8)
34 #error Only support 8-bit or 16-bit data bus
35 #endif
36
37 #define SSD1963_DATA_WITDH_BYTE (((uint8_t)SSD1963_DATA_WITDH) / 8U)
38
39 #define SSD1963_RET(x) \
40 do \
41 { \
42 status = (x); \
43 if (kStatus_Success != status) \
44 { \
45 return status; \
46 } \
47 } while (false)
48
49 /*******************************************************************************
50 * Prototypes
51 ******************************************************************************/
52
53 /*!
54 * @brief Use loop to delay.
55 *
56 * @param loops Number of the loops.
57 */
58 static void SSD1963_Delay(uint32_t loops);
59
60 /*!
61 * @brief Get the multiplier and divider setting for PLL.
62 *
63 * This function gets the multiplier and divider to generate PLL frequency at
64 * about 100MHz. The actually PLL frequency is returned.
65 *
66 * @param multi The multiplier value.
67 * @param div The divider value.
68 * @param srcClock_Hz The external reference clock(XTAL or CLK) frequency in Hz.
69 * @return Generated PLL frequency with the @p multi and @p div. If could not get
70 * the desired PLL frequency, this function returns 0.
71 */
72 static uint32_t SSD1963_GetPllDivider(uint8_t *multi, uint8_t *div, uint32_t srcClock_Hz);
73
74 /*******************************************************************************
75 * Variables
76 ******************************************************************************/
77
78 /*******************************************************************************
79 * Code
80 ******************************************************************************/
81
SSD1963_Delay(uint32_t loops)82 static void SSD1963_Delay(uint32_t loops)
83 {
84 while (0U != (loops--))
85 {
86 __NOP();
87 }
88 }
89
SSD1963_GetPllDivider(uint8_t * multi,uint8_t * div,uint32_t srcClock_Hz)90 static uint32_t SSD1963_GetPllDivider(uint8_t *multi, uint8_t *div, uint32_t srcClock_Hz)
91 {
92 uint32_t multiCur, divCur, pllFreqCur, vcoCur, diffCur;
93 uint32_t multiCandidate = 0U;
94 uint32_t divCandidate = 0U;
95 uint32_t pllFreqCandidate = 0U;
96 uint32_t diff = 0xFFFFFFFFU;
97
98 for (multiCur = SSD1963_PLL_MULTI_MIN; multiCur <= SSD1963_PLL_MULTI_MAX; multiCur++)
99 {
100 vcoCur = srcClock_Hz * (multiCur + 1U);
101
102 /* VCO must be larger than SSD1963_VCO_MIN_HZ. */
103 if (vcoCur <= SSD1963_VCO_MIN_HZ)
104 {
105 continue;
106 }
107
108 /* VCO must be smaller than SSD1963_VCO_MAX_HZ. */
109 if (vcoCur >= SSD1963_VCO_MAX_HZ)
110 {
111 break;
112 }
113
114 divCur = ((vcoCur + (SSD1963_PLL_FREQ_HZ / 2U)) / SSD1963_PLL_FREQ_HZ) - 1U;
115
116 /*
117 * VCO frequency must be in the range of (250MHz, 800MHz). The desired
118 * PLL output frequency is 100MHz, then the divCur here must be in the
119 * range of (1, 8). In this case, it is not necessary to check whether
120 * divCur is in the range of (0, 31). But for safty when the desired
121 * PLL frequency is changed, here check the upper range.
122 */
123 #if ((((SSD1963_VCO_MAX_HZ + (SSD1963_PLL_FREQ_HZ / 2U)) / SSD1963_PLL_FREQ_HZ) - 1U) > SSD1963_PLL_DIV_MAX)
124 if (divCur > SSD1963_PLL_DIV_MAX)
125 {
126 divCur = SSD1963_PLL_DIV_MAX;
127 }
128 #endif
129
130 pllFreqCur = vcoCur / (divCur + 1U);
131
132 if (SSD1963_PLL_FREQ_HZ > pllFreqCur)
133 {
134 diffCur = SSD1963_PLL_FREQ_HZ - pllFreqCur;
135 }
136 else
137 {
138 diffCur = pllFreqCur - SSD1963_PLL_FREQ_HZ;
139 }
140
141 /* Find better multi and divider. */
142 if (diff > diffCur)
143 {
144 diff = diffCur;
145 multiCandidate = multiCur;
146 divCandidate = divCur;
147 pllFreqCandidate = pllFreqCur;
148 }
149 }
150
151 *multi = (uint8_t)multiCandidate;
152 *div = (uint8_t)divCandidate;
153
154 return pllFreqCandidate;
155 }
156
SSD1963_Init(ssd1963_handle_t * handle,const ssd1963_config_t * config,const dbi_xfer_ops_t * xferOps,void * xferOpsData,uint32_t srcClock_Hz)157 status_t SSD1963_Init(ssd1963_handle_t *handle,
158 const ssd1963_config_t *config,
159 const dbi_xfer_ops_t *xferOps,
160 void *xferOpsData,
161 uint32_t srcClock_Hz)
162 {
163 assert(handle);
164 assert(config);
165
166 uint8_t multi, div;
167 uint32_t pllFreq_Hz;
168 uint32_t fpr; /* Pixel clock = PLL clock * ((fpr + 1) / 2^20) */
169 float fprFloat;
170 #if (16 == SSD1963_DATA_WITDH)
171 uint16_t commandParam[8];
172 #else
173 uint8_t commandParam[8];
174 #endif
175 uint16_t vt, vps, ht, hps;
176 status_t status;
177
178 pllFreq_Hz = SSD1963_GetPllDivider(&multi, &div, srcClock_Hz);
179
180 /* Could not set the PLL to desired frequency. */
181 if (0U == pllFreq_Hz)
182 {
183 return kStatus_InvalidArgument;
184 }
185
186 fprFloat = ((float)config->pclkFreq_Hz / (float)pllFreq_Hz) * (float)1048576.0f; /* 1048576 = 1<<20 */
187 fpr = (uint32_t)fprFloat;
188
189 if ((fpr < 1U) || (fpr > (SSD1963_LCDC_FPR_MAX + 1U)))
190 {
191 return kStatus_InvalidArgument;
192 }
193
194 fpr--;
195
196 /* Initialize the handle. */
197 (void)memset(handle, 0, sizeof(ssd1963_handle_t));
198
199 handle->panelWidth = config->panelWidth;
200 handle->panelHeight = config->panelHeight;
201 handle->xferOps = xferOps;
202 handle->xferOpsData = xferOpsData;
203
204 /* Soft reset. */
205 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SOFT_RESET));
206 SSD1963_Delay(50000);
207
208 /* Setup the PLL. */
209 /* Set the multiplier and divider. */
210 commandParam[0] = multi;
211 commandParam[1] = (uint8_t)(div | (1U << 5U));
212 commandParam[2] = 1U << 2U;
213 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SET_PLL_MN));
214 SSD1963_RET(handle->xferOps->writeData(xferOpsData, commandParam, 3U * SSD1963_DATA_WITDH_BYTE));
215
216 /* Enable PLL. */
217 commandParam[0] = 0x01U;
218 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SET_PLL));
219 SSD1963_RET(handle->xferOps->writeData(xferOpsData, commandParam, 1U * SSD1963_DATA_WITDH_BYTE));
220
221 /* Delay at least 100us, to wait for the PLL stable. */
222 SSD1963_Delay(500);
223
224 /* Use the PLL. */
225 commandParam[0] = 0x03U;
226 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SET_PLL));
227 SSD1963_RET(handle->xferOps->writeData(xferOpsData, commandParam, 1U * SSD1963_DATA_WITDH_BYTE));
228
229 /* Configure the pixel clock. */
230 commandParam[0] = (uint8_t)((fpr & 0xFF0000U) >> 16U);
231 commandParam[1] = (uint8_t)((fpr & 0xFF00U) >> 8U);
232 commandParam[2] = (uint8_t)((fpr & 0xFFU));
233 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SET_LSHIFT_FREQ));
234 SSD1963_RET(handle->xferOps->writeData(xferOpsData, commandParam, 3U * SSD1963_DATA_WITDH_BYTE));
235
236 /* Configure LCD panel. */
237 commandParam[0] =
238 (uint8_t)((uint8_t)config->panelDataWidth | (uint8_t)config->polarityFlags); /* Not enable FRC, dithering. */
239 commandParam[1] = 0x20U; /* TFT mode. */
240 commandParam[2] = (uint8_t)((config->panelWidth - 1U) >> 8);
241 commandParam[3] = (uint8_t)((config->panelWidth - 1U) & 0xFFU);
242 commandParam[4] = (uint8_t)((config->panelHeight - 1U) >> 8);
243 commandParam[5] = (uint8_t)((config->panelHeight - 1U) & 0xFFU);
244 commandParam[6] = 0;
245 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SET_LCD_MODE));
246 SSD1963_RET(handle->xferOps->writeData(xferOpsData, commandParam, 7U * SSD1963_DATA_WITDH_BYTE));
247
248 /* Horizontal period setting. */
249 ht = config->panelWidth + config->hsw + config->hfp + config->hbp;
250 hps = config->hsw + config->hbp;
251 commandParam[0] = (uint8_t)((ht - 1U) >> 8U);
252 commandParam[1] = (uint8_t)((ht - 1U) & 0xFFU);
253 commandParam[2] = (uint8_t)(hps >> 8U);
254 commandParam[3] = (uint8_t)(hps & 0xFFU);
255 commandParam[4] = (uint8_t)(config->hsw - 1U);
256 commandParam[5] = 0U;
257 commandParam[6] = 0U;
258 commandParam[7] = 0U;
259 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SET_HORI_PERIOD));
260 SSD1963_RET(handle->xferOps->writeData(xferOpsData, commandParam, 8U * SSD1963_DATA_WITDH_BYTE));
261
262 /* Vertical period setting. */
263 vt = config->panelHeight + config->vsw + config->vfp + config->vbp;
264 vps = config->vsw + config->vbp;
265 commandParam[0] = (uint8_t)((vt - 1U) >> 8U);
266 commandParam[1] = (uint8_t)((vt - 1U) & 0xFFU);
267 commandParam[2] = (uint8_t)(vps >> 8U);
268 commandParam[3] = (uint8_t)(vps & 0xFFU);
269 commandParam[4] = (uint8_t)(config->vsw - 1U);
270 commandParam[5] = 0U;
271 commandParam[6] = 0U;
272 SSD1963_RET(handle->xferOps->writeCommand(xferOpsData, SSD1963_SET_VERT_PERIOD));
273 SSD1963_RET(handle->xferOps->writeData(xferOpsData, commandParam, 7U * SSD1963_DATA_WITDH_BYTE));
274
275 /* Pixel format. */
276 return SSD1963_SetPixelFormat(handle, config->pixelInterface);
277 }
278
SSD1963_SetMemoryDoneCallback(ssd1963_handle_t * handle,dbi_mem_done_callback_t callback,void * userData)279 void SSD1963_SetMemoryDoneCallback(ssd1963_handle_t *handle, dbi_mem_done_callback_t callback, void *userData)
280 {
281 assert(handle);
282
283 handle->xferOps->setMemoryDoneCallback(handle->xferOpsData, callback, userData);
284 }
285
SSD1963_Deinit(ssd1963_handle_t * handle)286 void SSD1963_Deinit(ssd1963_handle_t *handle)
287 {
288 assert(handle);
289
290 (void)memset(handle, 0, sizeof(ssd1963_handle_t));
291 }
292
SSD1963_StartDisplay(ssd1963_handle_t * handle)293 status_t SSD1963_StartDisplay(ssd1963_handle_t *handle)
294 {
295 return handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_DISPLAY_ON);
296 }
297
SSD1963_StopDisplay(ssd1963_handle_t * handle)298 status_t SSD1963_StopDisplay(ssd1963_handle_t *handle)
299 {
300 return handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_DISPLAY_OFF);
301 }
302
SSD1963_SetFlipMode(ssd1963_handle_t * handle,ssd1963_flip_mode_t mode)303 status_t SSD1963_SetFlipMode(ssd1963_handle_t *handle, ssd1963_flip_mode_t mode)
304 {
305 status_t status;
306
307 #if (16 == SSD1963_DATA_WITDH)
308 uint16_t newAddrMode;
309
310 newAddrMode = (uint16_t)(handle->addrMode & ~SSD1963_FLIP_MODE_MASK) | (uint16_t)mode;
311 #else
312 uint8_t newAddrMode;
313
314 newAddrMode = (uint8_t)(handle->addrMode & ~SSD1963_FLIP_MODE_MASK) | (uint8_t)mode;
315 #endif
316
317 SSD1963_RET(handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_ADDRESS_MODE));
318 SSD1963_RET(handle->xferOps->writeData(handle->xferOpsData, &newAddrMode, 1U * SSD1963_DATA_WITDH_BYTE));
319
320 handle->addrMode = (uint8_t)newAddrMode;
321
322 return kStatus_Success;
323 }
324
SSD1963_SetOrientationMode(ssd1963_handle_t * handle,ssd1963_orientation_mode_t mode)325 status_t SSD1963_SetOrientationMode(ssd1963_handle_t *handle, ssd1963_orientation_mode_t mode)
326 {
327 status_t status;
328 #if (16 == SSD1963_DATA_WITDH)
329 uint16_t newAddrMode;
330
331 newAddrMode = (uint16_t)(handle->addrMode & ~SSD1963_ORIENTATION_MODE_MASK) | (uint16_t)mode;
332 #else
333 uint8_t newAddrMode;
334
335 newAddrMode = (uint8_t)(handle->addrMode & ~SSD1963_ORIENTATION_MODE_MASK) | (uint8_t)mode;
336 #endif
337
338 SSD1963_RET(handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_ADDRESS_MODE));
339 SSD1963_RET(handle->xferOps->writeData(handle->xferOpsData, &newAddrMode, 1U * SSD1963_DATA_WITDH_BYTE));
340
341 handle->addrMode = (uint8_t)newAddrMode;
342
343 return kStatus_Success;
344 }
345
SSD1963_SelectArea(ssd1963_handle_t * handle,uint16_t startX,uint16_t startY,uint16_t endX,uint16_t endY)346 status_t SSD1963_SelectArea(ssd1963_handle_t *handle, uint16_t startX, uint16_t startY, uint16_t endX, uint16_t endY)
347 {
348 uint16_t sc; /* Start of column number. */
349 uint16_t ec; /* End of column number. */
350 uint16_t sp; /* Start of page number. */
351 uint16_t ep; /* End of page number. */
352 ssd1963_orientation_mode_t mode;
353 #if (16 == SSD1963_DATA_WITDH)
354 uint16_t commandParam[4]; /* Command parameters for set_page_address and set_column_address. */
355 #else
356 uint8_t commandParam[4]; /* Command parameters for set_page_address and set_column_address. */
357 #endif
358 status_t status;
359
360 mode = (ssd1963_orientation_mode_t)(uint8_t)(handle->addrMode & SSD1963_ORIENTATION_MODE_MASK);
361
362 switch (mode)
363 {
364 default:
365 /* For MISRA-C 2012 rule 16.4. */
366 case kSSD1963_Orientation0:
367 sp = startY;
368 ep = endY;
369 sc = startX;
370 ec = endX;
371 break;
372
373 case kSSD1963_Orientation90:
374 sp = handle->panelHeight - 1U - endX;
375 ep = handle->panelHeight - 1U - startX;
376 sc = startY;
377 ec = endY;
378 break;
379
380 case kSSD1963_Orientation180:
381 sp = handle->panelHeight - 1U - endY;
382 ep = handle->panelHeight - 1U - startY;
383 sc = handle->panelWidth - 1U - endX;
384 ec = handle->panelWidth - 1U - startX;
385 break;
386
387 case kSSD1963_Orientation270:
388 sp = startX;
389 ep = endX;
390 sc = handle->panelWidth - 1U - endY;
391 ec = handle->panelWidth - 1U - startY;
392 break;
393 }
394
395 /* Send the set_page_address command. */
396 commandParam[0] = (uint8_t)((sp & 0xFF00U) >> 8U);
397 commandParam[1] = (uint8_t)(sp & 0xFFU);
398 commandParam[2] = (uint8_t)((ep & 0xFF00U) >> 8U);
399 commandParam[3] = (uint8_t)(ep & 0xFFU);
400
401 SSD1963_RET(handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_PAGE_ADDRESS));
402 SSD1963_RET(handle->xferOps->writeData(handle->xferOpsData, commandParam, 4U * SSD1963_DATA_WITDH_BYTE));
403
404 /* Send the set_column_address command. */
405 commandParam[0] = (uint8_t)((sc & 0xFF00U) >> 8U);
406 commandParam[1] = (uint8_t)(sc & 0xFFU);
407 commandParam[2] = (uint8_t)((ec & 0xFF00U) >> 8U);
408 commandParam[3] = (uint8_t)(ec & 0xFFU);
409
410 SSD1963_RET(handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_COLUMN_ADDRESS));
411 return handle->xferOps->writeData(handle->xferOpsData, commandParam, 4U * SSD1963_DATA_WITDH_BYTE);
412 }
413
414 #if (16 == SSD1963_DATA_WITDH)
SSD1963_WritePixels(ssd1963_handle_t * handle,const uint16_t * pixels,uint32_t length)415 status_t SSD1963_WritePixels(ssd1963_handle_t *handle, const uint16_t *pixels, uint32_t length)
416 {
417 return handle->xferOps->writeMemory(handle->xferOpsData, SSD1963_WRITE_MEMORY_START, (const uint8_t *)pixels,
418 length * 2U);
419 }
420
SSD1963_ReadPixels(ssd1963_handle_t * handle,uint16_t * pixels,uint32_t length)421 status_t SSD1963_ReadPixels(ssd1963_handle_t *handle, uint16_t *pixels, uint32_t length)
422 {
423 return handle->xferOps->readMemory(handle->xferOpsData, SSD1963_READ_MEMORY_START, (uint8_t *)pixels, length * 2U);
424 }
425 #endif
426
SSD1963_SetBackLight(ssd1963_handle_t * handle,uint8_t value)427 status_t SSD1963_SetBackLight(ssd1963_handle_t *handle, uint8_t value)
428 {
429 status_t status;
430 #if (16 == SSD1963_DATA_WITDH)
431 uint16_t commandParam[] = {0x06U, value, 0x01U, 0xFFU, 0x00U, 0x01U};
432 #else
433 uint8_t commandParam[] = {0x06U, value, 0x01U, 0xFFU, 0x00U, 0x01U};
434 #endif
435
436 SSD1963_RET(handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_PWM_CONF));
437 return handle->xferOps->writeData(handle->xferOpsData, commandParam, sizeof(commandParam));
438 }
439
SSD1963_EnableTearEffect(ssd1963_handle_t * handle,bool enable)440 status_t SSD1963_EnableTearEffect(ssd1963_handle_t *handle, bool enable)
441 {
442 uint16_t regVal = 0;
443 status_t status;
444
445 if (enable)
446 {
447 status = handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_TEAR_ON);
448 status = handle->xferOps->writeData(handle->xferOpsData, ®Val, SSD1963_DATA_WITDH_BYTE);
449 }
450 else
451 {
452 status = handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_TEAR_OFF);
453 }
454
455 return status;
456 }
457
SSD1963_SetPixelFormat(ssd1963_handle_t * handle,ssd1963_pixel_interface_t pixelFormat)458 status_t SSD1963_SetPixelFormat(ssd1963_handle_t *handle, ssd1963_pixel_interface_t pixelFormat)
459 {
460 status_t status;
461 #if (16 == SSD1963_DATA_WITDH)
462 uint16_t commandParam[1];
463 #else
464 uint8_t commandParam[1];
465 #endif
466
467 /* Data interface. */
468 #if (8 == SSD1963_DATA_WITDH)
469 commandParam[0] = 0;
470 #else
471 commandParam[0] = 3;
472 #endif
473 SSD1963_RET(handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_PIXEL_DATA_INTERFACE));
474 SSD1963_RET(handle->xferOps->writeData(handle->xferOpsData, commandParam, 1U * SSD1963_DATA_WITDH_BYTE));
475
476 /* Address mode. */
477 handle->addrMode &= (uint8_t)(~SSD1963_ADDR_MODE_BGR);
478 #if (8 == SSD1963_DATA_WITDH)
479 if (kSSD1963_RGB888 == pixelFormat)
480 {
481 handle->addrMode |= SSD1963_ADDR_MODE_BGR;
482 }
483 #else
484 if (kSSD1963_BGR565 == pixelFormat)
485 {
486 handle->addrMode |= SSD1963_ADDR_MODE_BGR;
487 }
488 #endif
489
490 commandParam[0] = handle->addrMode;
491 SSD1963_RET(handle->xferOps->writeCommand(handle->xferOpsData, SSD1963_SET_ADDRESS_MODE));
492 return handle->xferOps->writeData(handle->xferOpsData, commandParam, 1U * SSD1963_DATA_WITDH_BYTE);
493 }
494
SSD1963_ReadMemory(ssd1963_handle_t * handle,uint8_t * data,uint32_t length)495 status_t SSD1963_ReadMemory(ssd1963_handle_t *handle, uint8_t *data, uint32_t length)
496 {
497 return handle->xferOps->readMemory(handle->xferOpsData, SSD1963_READ_MEMORY_START, data, length);
498 }
499
SSD1963_WriteMemory(ssd1963_handle_t * handle,const uint8_t * data,uint32_t length)500 status_t SSD1963_WriteMemory(ssd1963_handle_t *handle, const uint8_t *data, uint32_t length)
501 {
502 return handle->xferOps->writeMemory(handle->xferOpsData, SSD1963_WRITE_MEMORY_START, data, length);
503 }
504