1 /*
2  * Copyright 2023-2024 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_st7796s.h"
9 
10 #define ST7796S_ERROR_CHECK(x)      \
11     do                              \
12     {                               \
13         status_t ret = (x);         \
14         if (ret != kStatus_Success) \
15         {                           \
16             return ret;             \
17         }                           \
18     } while (false)
19 
20 #define ST7796S_CMD_SWRESET (0x01U)
21 #define ST7796S_CMD_SLPIN   (0x10U)
22 #define ST7796S_CMD_SLPOUT  (0x11U)
23 #define ST7796S_CMD_INVOFF  (0x20U)
24 #define ST7796S_CMD_INVON   (0x21U)
25 #define ST7796S_CMD_DISPOFF (0x28U)
26 #define ST7796S_CMD_DISPON  (0x29U)
27 #define ST7796S_CMD_CASET   (0x2AU)
28 #define ST7796S_CMD_RASET   (0x2BU)
29 #define ST7796S_CMD_RAMWR   (0x2CU)
30 #define ST7796S_CMD_TEOFF   (0x34U)
31 #define ST7796S_CMD_TEON    (0x35U)
32 #define ST7796S_CMD_MADCTL  (0x36U)
33 #define ST7796S_CMD_COLMOD  (0x3AU)
34 #define ST7796S_CMD_CSCON   (0xF0U)
35 #define ST7796S_CMD_INVTR   (0xB4U)
36 #define ST7796S_CMD_FRMCTR1 (0xB1U)
37 #define ST7796S_CMD_BPC     (0xB5U)
38 #define ST7796S_CMD_DFC     (0xB6U)
39 #define ST7796S_CMD_PWR1    (0xC0U)
40 #define ST7796S_CMD_PWR2    (0xC1U)
41 #define ST7796S_CMD_PWR3    (0xC2U)
42 #define ST7796S_CMD_VCMPCTL (0xC5U)
43 #define ST7796S_CMD_DOCA    (0xE8U)
44 #define ST7796S_CMD_PGC     (0xE0U)
45 #define ST7796S_CMD_NGC     (0xE1U)
46 
47 /*
48  * This is the panel-specific initialization parameters for driver section,
49  * including analog power/voltage control, gamma correction, scan timing, etc.
50  * Data format: | 1 Byte parameter length | 1 Byte command byte | N Bytes command parameter |
51  */
52 static const uint8_t s_st7796s_driver_preset_pars035[] = {
53     /* clang-format off */
54     0x01, ST7796S_CMD_CSCON,   0xC3,                            // Enable command part 1
55     0x01, ST7796S_CMD_CSCON,   0x96,                            // Enable command part 2
56     0x01, ST7796S_CMD_INVTR,   0x01,                            // Display inversion
57     0x02, ST7796S_CMD_FRMCTR1, 0x80, 0x10,                      // Frame rate control 1
58     0x04, ST7796S_CMD_BPC,     0x1F, 0x50, 0x00, 0x20,          // Blanking porch control
59     0x03, ST7796S_CMD_DFC,     0x8A, 0x07, 0x3B,                // Display function control
60     0x02, ST7796S_CMD_PWR1,    0x80, 0x64,                      // Power control 1
61     0x01, ST7796S_CMD_PWR2,    0x13,                            // Power control 2
62     0x01, ST7796S_CMD_PWR3,    0xA7,                            // Power control 3
63     0x01, ST7796S_CMD_VCMPCTL, 0x09,                            // VCOM control
64     0x08, ST7796S_CMD_DOCA,    0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33, // DOCA
65     0x0E, ST7796S_CMD_PGC,     0xF0, 0x06, 0x0B, 0x07, 0x06, 0x05, 0x2E, 0x33, 0x47, 0x3A, 0x17, 0x16, 0x2E, 0x31, // PGC
66     0x0E, ST7796S_CMD_NGC,     0xF0, 0x09, 0x0D, 0x09, 0x08, 0x23, 0x2E, 0x33, 0x46, 0x38, 0x13, 0x13, 0x2C, 0x32, // NGC
67     0x01, ST7796S_CMD_CSCON,   0x3C, // Disable command part 1
68     0x01, ST7796S_CMD_CSCON,   0x69, // Disable command part 2
69     /* clang-format on */
70 };
71 
ST7796S_WriteCommand(st7796s_handle_t * handle,uint8_t command,const uint8_t * params,uint8_t param_len)72 static status_t ST7796S_WriteCommand(st7796s_handle_t *handle,
73                                      uint8_t command,
74                                      const uint8_t *params,
75                                      uint8_t param_len)
76 {
77 #if MCUX_DBI_LEGACY
78     ST7796S_ERROR_CHECK(handle->xferOps->writeCommand(handle->xferOpsData, command));
79 
80     for (uint8_t i = 0; i < param_len; i++)
81     {
82         uint16_t param_data = params[i];
83 
84         ST7796S_ERROR_CHECK(handle->xferOps->writeData(handle->xferOpsData, &param_data, 2U));
85     }
86 
87     return kStatus_Success;
88 #else
89     return DBI_IFACE_WriteCmdData(handle->dbiIface, command, params, param_len);
90 #endif
91 }
92 
ST7796S_PresetDriver(st7796s_handle_t * handle,st7796s_driver_preset_t preset)93 static status_t ST7796S_PresetDriver(st7796s_handle_t *handle, st7796s_driver_preset_t preset)
94 {
95     const uint8_t *preset_ptr;
96     uint16_t preset_len;
97 
98     if (kST7796S_DriverPresetLCDPARS035 == preset)
99     {
100         preset_ptr = s_st7796s_driver_preset_pars035;
101         preset_len = ARRAY_SIZE(s_st7796s_driver_preset_pars035);
102     }
103     else
104     {
105         return kStatus_InvalidArgument;
106     }
107 
108     uint16_t i = 0;
109 
110     while (i < preset_len)
111     {
112         uint8_t param_len = preset_ptr[i];
113 
114         ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, preset_ptr[i + 1u], &preset_ptr[i + 2u], param_len));
115         i += ((uint16_t)param_len + 2u); /* Next = parameter length + command byte + length itself */
116     }
117 
118     return kStatus_Success;
119 }
120 
ST7796S_SoftwareReset(st7796s_handle_t * handle)121 static status_t ST7796S_SoftwareReset(st7796s_handle_t *handle)
122 {
123     ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_SWRESET, NULL, 0));
124     SDK_DelayAtLeastUs(5 * 1000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
125 
126     return kStatus_Success;
127 }
128 
ST7796S_SleepMode(st7796s_handle_t * handle,bool sleep)129 static status_t ST7796S_SleepMode(st7796s_handle_t *handle, bool sleep)
130 {
131     uint8_t slp_cmd;
132     if (sleep)
133     {
134         slp_cmd = ST7796S_CMD_SLPIN;
135     }
136     else
137     {
138         slp_cmd = ST7796S_CMD_SLPOUT;
139     }
140 
141     return ST7796S_WriteCommand(handle, slp_cmd, NULL, 0);
142 }
143 
144 #if MCUX_DBI_LEGACY
ST7796S_Init(st7796s_handle_t * handle,const st7796s_config_t * config,const dbi_xfer_ops_t * xferOps,void * xferOpsData)145 status_t ST7796S_Init(st7796s_handle_t *handle,
146                       const st7796s_config_t *config,
147                       const dbi_xfer_ops_t *xferOps,
148                       void *xferOpsData)
149 #else
150 status_t ST7796S_Init(st7796s_handle_t *handle, const st7796s_config_t *config, dbi_iface_t *dbiIface)
151 #endif
152 {
153 #if MCUX_DBI_LEGACY
154     handle->xferOps     = xferOps;
155     handle->xferOpsData = xferOpsData;
156 #else
157     handle->dbiIface = dbiIface;
158 #endif
159 
160     ST7796S_ERROR_CHECK(ST7796S_SoftwareReset(handle));
161     ST7796S_ERROR_CHECK(ST7796S_PresetDriver(handle, config->driverPreset));
162     ST7796S_ERROR_CHECK(ST7796S_SleepMode(handle, false));
163     ST7796S_ERROR_CHECK(ST7796S_Config(handle, config));
164 
165     return kStatus_Success;
166 }
167 
ST7796S_InvertDisplay(st7796s_handle_t * handle,bool invert)168 status_t ST7796S_InvertDisplay(st7796s_handle_t *handle, bool invert)
169 {
170     uint8_t inv_cmd;
171     if (invert)
172     {
173         inv_cmd = ST7796S_CMD_INVON;
174     }
175     else
176     {
177         inv_cmd = ST7796S_CMD_INVOFF;
178     }
179 
180     ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, inv_cmd, NULL, 0U));
181 
182     return kStatus_Success;
183 }
184 
ST7796S_EnableDisplay(st7796s_handle_t * handle,bool enable)185 status_t ST7796S_EnableDisplay(st7796s_handle_t *handle, bool enable)
186 {
187     uint8_t disp_cmd;
188     if (enable)
189     {
190         disp_cmd = ST7796S_CMD_DISPON;
191     }
192     else
193     {
194         disp_cmd = ST7796S_CMD_DISPOFF;
195     }
196 
197     ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, disp_cmd, NULL, 0U));
198 
199     return kStatus_Success;
200 }
201 
ST7796S_SetPixelFormat(st7796s_handle_t * handle,st7796s_pixel_format_t pixelFormat)202 status_t ST7796S_SetPixelFormat(st7796s_handle_t *handle, st7796s_pixel_format_t pixelFormat)
203 {
204     uint8_t pixel_fmt = (uint8_t)pixelFormat;
205     ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_COLMOD, &pixel_fmt, 0x01U));
206 
207     return kStatus_Success;
208 }
209 
ST7796S_SetTEConfig(st7796s_handle_t * handle,st7796s_te_config_t teConfig)210 status_t ST7796S_SetTEConfig(st7796s_handle_t *handle, st7796s_te_config_t teConfig)
211 {
212     if (teConfig == kST7796S_TEDisabled)
213     {
214         ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_TEOFF, NULL, 0U));
215     }
216     else
217     {
218         uint8_t te_cfg = 0x00U;
219         if (teConfig == kST7796S_TEHVSync)
220         {
221             te_cfg |= 0x01U; /* Set TEM bit */
222         }
223 
224         ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_TEON, &te_cfg, 1U));
225     }
226 
227     return kStatus_Success;
228 }
229 
ST7796S_SelectArea(st7796s_handle_t * handle,uint16_t startX,uint16_t startY,uint16_t endX,uint16_t endY)230 status_t ST7796S_SelectArea(st7796s_handle_t *handle, uint16_t startX, uint16_t startY, uint16_t endX, uint16_t endY)
231 {
232     uint8_t tx_buf[4];
233 
234     tx_buf[0] = (uint8_t)(startX >> 0x08U) & 0xFFU;
235     tx_buf[1] = (uint8_t)startX & 0xFFU;
236     tx_buf[2] = (uint8_t)(endX >> 0x08U) & 0xFFU;
237     tx_buf[3] = (uint8_t)endX & 0xFFU;
238 
239     ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_CASET, tx_buf, 4U));
240 
241     tx_buf[0] = (uint8_t)(startY >> 0x08U) & 0xFFU;
242     tx_buf[1] = (uint8_t)startY & 0xFFU;
243     tx_buf[2] = (uint8_t)(endY >> 0x08U) & 0xFFU;
244     tx_buf[3] = (uint8_t)endY & 0xFFU;
245 
246     ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_RASET, tx_buf, 4U));
247 
248     return kStatus_Success;
249 }
250 
ST7796S_Config(st7796s_handle_t * handle,const st7796s_config_t * config)251 status_t ST7796S_Config(st7796s_handle_t *handle, const st7796s_config_t *config)
252 {
253     ST7796S_ERROR_CHECK(ST7796S_InvertDisplay(handle, config->invertDisplay));
254     ST7796S_ERROR_CHECK(ST7796S_SetPixelFormat(handle, config->pixelFormat));
255     ST7796S_ERROR_CHECK(ST7796S_SetTEConfig(handle, config->teConfig));
256 
257     uint8_t tx_buf[1];
258 
259     tx_buf[0] = (uint8_t)config->orientationMode;
260     if (!config->bgrFilter)
261     {
262         tx_buf[0] &= (uint8_t)(~0x08U);
263     }
264 
265     if (config->flipDisplay)
266     {
267         if ((config->orientationMode == kST7796S_Orientation90) || (config->orientationMode == kST7796S_Orientation270))
268         {
269             tx_buf[0] ^= 0x80U;
270         }
271         else
272         {
273             tx_buf[0] ^= 0x40U;
274         }
275     }
276 
277     ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_MADCTL, tx_buf, 1U));
278 
279     handle->orientationMode = config->orientationMode;
280 
281     return kStatus_Success;
282 }
283 
ST7796S_WritePixels(st7796s_handle_t * handle,uint16_t * pixels,uint32_t len)284 status_t ST7796S_WritePixels(st7796s_handle_t *handle, uint16_t *pixels, uint32_t len)
285 {
286 #if MCUX_DBI_LEGACY
287     ST7796S_ERROR_CHECK(handle->xferOps->writeMemory(handle->xferOpsData, ST7796S_CMD_RAMWR, pixels, len * 2u));
288 #else
289     ST7796S_ERROR_CHECK(DBI_IFACE_WriteMemory(handle->dbiIface, (const uint8_t *)pixels, len * 2u));
290 #endif
291 
292     return kStatus_Success;
293 }
294 
295 #if MCUX_DBI_LEGACY
ST7796S_SetMemoryDoneCallback(st7796s_handle_t * handle,dbi_mem_done_callback_t callback,void * userData)296 void ST7796S_SetMemoryDoneCallback(st7796s_handle_t *handle, dbi_mem_done_callback_t callback, void *userData)
297 {
298     handle->xferOps->setMemoryDoneCallback(handle->xferOpsData, callback, userData);
299 }
300 #endif
301