1 /*
2  * Copyright (c) 2023 HPMicro
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "hpm_mipi_dsi_drv.h"
9 
10 #define MIPI_WAIT_COND(cond, timeout_us)	\
11 ({ \
12     volatile uint32_t timeout_cycle = 1000UL * (timeout_us); \
13     for (;;) { \
14         if (cond) \
15             break; \
16         if (timeout_us && timeout_cycle == 0) { \
17             break; \
18         } \
19         timeout_cycle--; \
20     } \
21     (cond) ? true : false; \
22 })
23 
24 typedef struct mipi_dsi_packet {
25     uint8_t header[4]; /*!< the four bytes that make up the header (Data ID, Word Count or Packet Data, and ECC) */
26     uint16_t payload_length; /*!< number of bytes in the payload */
27     const uint8_t *payload; /*!< a pointer to a buffer containing the payload, if any */
28 } mipi_dsi_packet_t;
29 
30 /**
31  * mipi_dsi_packet_format_is_short - check if a packet is of the short format
32  * @param type: MIPI DSI data type of the packet
33  *
34  * @return: true if the packet for the given data type is a short packet, false
35  * otherwise.
36  */
mipi_dsi_packet_format_is_short(uint8_t type)37 static bool mipi_dsi_packet_format_is_short(uint8_t type)
38 {
39     switch (type) {
40     case MIPI_DSI_SHUTDOWN_PERIPHERAL:
41     case MIPI_DSI_TURN_ON_PERIPHERAL:
42     case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
43     case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
44     case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
45     case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
46     case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
47     case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
48     case MIPI_DSI_DCS_SHORT_WRITE:
49     case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
50     case MIPI_DSI_DCS_READ:
51     case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
52         return true;
53     }
54 
55     return false;
56 }
57 
58 /**
59  * mipi_dsi_packet_format_is_long - check if a packet is of the long format
60  * @param type: MIPI DSI data type of the packet
61  *
62  * @return: true if the packet for the given data type is a long packet, false
63  * otherwise.
64  */
mipi_dsi_packet_format_is_long(uint8_t type)65 static bool mipi_dsi_packet_format_is_long(uint8_t type)
66 {
67     switch (type) {
68     case MIPI_DSI_GENERIC_LONG_WRITE:
69     case MIPI_DSI_DCS_LONG_WRITE:
70         return true;
71     }
72 
73     return false;
74 }
75 
76 /**
77  * mipi_dsi_create_packet - create a packet from a message according to the
78  *	DSI protocol
79  * @param packet: pointer to a DSI packet structure
80  * @param msg: message to translate into a packet
81  *
82  * @return: true on success or false on failure.
83  */
mipi_dsi_create_packet(mipi_dsi_packet_t * packet,const mipi_dsi_msg_t * msg)84 static bool mipi_dsi_create_packet(mipi_dsi_packet_t *packet, const mipi_dsi_msg_t *msg)
85 {
86     if (!packet || !msg)
87         return false;
88 
89     /* do some minimum sanity checking */
90     if (!mipi_dsi_packet_format_is_short(msg->type) &&
91         !mipi_dsi_packet_format_is_long(msg->type))
92         return false;
93 
94     if (msg->channel > 3)
95         return false;
96 
97     memset(packet, 0, sizeof(*packet));
98     packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
99     if (mipi_dsi_packet_format_is_long(msg->type)) {
100         packet->header[1] = (msg->tx_len >> 0) & 0xff;
101         packet->header[2] = (msg->tx_len >> 8) & 0xff;
102 
103         packet->payload_length = msg->tx_len;
104         packet->payload = (const uint8_t *)msg->tx_buf;
105     } else {
106         const uint8_t *tx = (const uint8_t *)msg->tx_buf;
107 
108         packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
109         packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
110     }
111 
112     return true;
113 }
114 
mipi_dsi_config_format(MIPI_DSI_Type * ptr,mipi_dsi_pixel_format_t format)115 static void mipi_dsi_config_format(MIPI_DSI_Type *ptr, mipi_dsi_pixel_format_t format)
116 {
117     uint32_t val = 0;
118 
119     switch ((uint8_t)format) {
120     case MIPI_DSI_FMT_RGB888:
121         val = MIPI_DSI_DPI_COLOR_CODING_DPI_COLOR_CODING_SET(0x05);
122         break;
123     case MIPI_DSI_FMT_RGB666:
124         val = MIPI_DSI_DPI_COLOR_CODING_DPI_COLOR_CODING_SET(0x04) |
125                 MIPI_DSI_DPI_COLOR_CODING_LOOSELY18_EN_MASK;
126         break;
127     case MIPI_DSI_FMT_RGB666_PACKED:
128         val = MIPI_DSI_DPI_COLOR_CODING_DPI_COLOR_CODING_SET(0x04);
129         break;
130     case MIPI_DSI_FMT_RGB565:
131         val = MIPI_DSI_DPI_COLOR_CODING_DPI_COLOR_CODING_SET(0x00);
132         break;
133     }
134 
135     ptr->DPI_COLOR_CODING = val;
136 }
137 
138 /* Get lane byte clock cycles. */
mipi_dsi_get_hcomponent_lbcc(uint32_t lane_mbps,uint32_t pixel_clock_khz,uint32_t hcomponent)139 static int mipi_dsi_get_hcomponent_lbcc(uint32_t lane_mbps, uint32_t pixel_clock_khz, uint32_t hcomponent)
140 {
141 	uint32_t lbcc = hcomponent * lane_mbps * 1000 / 8;
142 
143 	if (!pixel_clock_khz)
144 		return 0;
145 
146 	return HPM_DIV_ROUND_CLOSEST(lbcc, pixel_clock_khz);
147 }
148 
mipi_dsi_video_para_config(MIPI_DSI_Type * ptr,mipi_dsi_config_t * cfg)149 static void mipi_dsi_video_para_config(MIPI_DSI_Type *ptr, mipi_dsi_config_t *cfg)
150 {
151     mipi_video_para_t *video_para = &cfg->video_para;
152     int htotal, lbcc;
153 
154     /* VID_HXXXX_TIME uint is lbcc(lane byte clock = lane_mbps / 8) */
155     htotal = video_para->hactive + video_para->hsync_len +
156                 video_para->hback_porch + video_para->hfront_porch;
157     lbcc = mipi_dsi_get_hcomponent_lbcc(cfg->lane_mbps, video_para->pixel_clock_khz, htotal);
158     ptr->VID_HLINE_TIME = lbcc;
159     lbcc = mipi_dsi_get_hcomponent_lbcc(cfg->lane_mbps, video_para->pixel_clock_khz, video_para->hsync_len);
160     ptr->VID_HSA_TIME = lbcc;
161     lbcc = mipi_dsi_get_hcomponent_lbcc(cfg->lane_mbps, video_para->pixel_clock_khz, video_para->hback_porch);
162     ptr->VID_HBP_TIME = lbcc;
163 
164     ptr->VID_VACTIVE_LINES = video_para->vactive;
165     ptr->VID_VSA_LINES = video_para->vsync_len;
166     ptr->VID_VBP_LINES = video_para->vback_porch;
167     ptr->VID_VFP_LINES = video_para->vfront_porch;
168 }
169 
mipi_dsi_genif_wait_w_pld_fifo_not_full(MIPI_DSI_Type * ptr)170 static bool mipi_dsi_genif_wait_w_pld_fifo_not_full(MIPI_DSI_Type *ptr)
171 {
172     uint32_t mask = MIPI_DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL_MASK;
173     return MIPI_WAIT_COND(!(ptr->CMD_PKT_STATUS & mask), 10000);
174 }
175 
mipi_dsi_genif_wait_cmd_fifo_not_full(MIPI_DSI_Type * ptr)176 static bool mipi_dsi_genif_wait_cmd_fifo_not_full(MIPI_DSI_Type *ptr)
177 {
178     uint32_t mask = MIPI_DSI_CMD_PKT_STATUS_GEN_CMD_FULL_MASK;
179     return MIPI_WAIT_COND(!(ptr->CMD_PKT_STATUS & mask), 10000);
180 }
181 
mipi_dsi_genif_wait_write_fifo_empty(MIPI_DSI_Type * ptr)182 static bool mipi_dsi_genif_wait_write_fifo_empty(MIPI_DSI_Type *ptr)
183 {
184     uint32_t mask = MIPI_DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY_MASK |
185                     MIPI_DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY_MASK;
186 
187     return MIPI_WAIT_COND((ptr->CMD_PKT_STATUS & mask) == mask, 10000);
188 }
189 
dw_mipi_dsi_read_from_fifo(MIPI_DSI_Type * ptr,const struct mipi_dsi_msg * msg)190 static bool dw_mipi_dsi_read_from_fifo(MIPI_DSI_Type *ptr,
191 				      const struct mipi_dsi_msg *msg)
192 {
193     uint8_t *payload = (uint8_t *)msg->rx_buf;
194     uint16_t length;
195     uint32_t val;
196     uint32_t mask;
197     bool ret = true;
198 
199     mask = MIPI_DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY_MASK;
200     ret = MIPI_WAIT_COND(!(ptr->CMD_PKT_STATUS & mask), 10000);
201     if (ret == false) {
202         return ret;
203     }
204 
205     /* Receive payload */
206     for (length = msg->rx_len; length; length -= 4) {
207         mask = MIPI_DSI_CMD_PKT_STATUS_GEN_PLD_R_EMPTY_MASK;
208         ret = MIPI_WAIT_COND(!(ptr->CMD_PKT_STATUS & mask), 10000);
209         if (ret == false) {
210             return ret;
211         }
212 
213         val = ptr->GEN_PLD_DATA;
214 
215         switch (length) {
216         case 3:
217             payload[2] = (val >> 16) & 0xff;
218         /* Fall through */
219         case 2:
220             payload[1] = (val >> 8) & 0xff;
221         /* Fall through */
222         case 1:
223             payload[0] = val & 0xff;
224             return ret;
225         }
226 
227         payload[0] = (val >>  0) & 0xff;
228         payload[1] = (val >>  8) & 0xff;
229         payload[2] = (val >> 16) & 0xff;
230         payload[3] = (val >> 24) & 0xff;
231         payload += 4;
232     }
233 
234     return ret;
235 }
236 
get_le32(const uint8_t * p)237 static uint32_t get_le32(const uint8_t *p)
238 {
239     return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
240 }
241 
mipi_dsi_get_defconfig_on_video(mipi_dsi_config_t * cfg)242 void mipi_dsi_get_defconfig_on_video(mipi_dsi_config_t *cfg)
243 {
244     mipi_video_para_t video_para = {
245         .pixel_clock_khz = 59400,
246         .hactive = 800,
247         .hsync_len = 8,
248         .hback_porch = 48,
249         .hfront_porch = 52,
250         .vsync_len = 6,
251         .vactive = 1280,
252         .vback_porch = 16,
253         .vfront_porch = 15
254     };
255 
256     cfg->lanes = 4;
257     cfg->channel = 0;
258     cfg->lane_mbps = 500;
259     cfg->disable_eotp = false;
260     cfg->pixel_format = MIPI_DSI_FMT_RGB888;
261     cfg->video_mode = MIPI_DSI_VIDEO_MODE_BURST;
262     cfg->video_para = video_para;
263 }
264 
mipi_dsi_init(MIPI_DSI_Type * ptr,mipi_dsi_config_t * cfg)265 void mipi_dsi_init(MIPI_DSI_Type *ptr, mipi_dsi_config_t *cfg)
266 {
267     uint32_t val;
268 
269     /* PWR need reset when config register */
270     ptr->PWR_UP &= ~MIPI_DSI_PWR_UP_SHUTDOWNZ_MASK;
271 
272     /* escclk config about 20MHz and esc_clk_div > 1*/
273     uint32_t esc_clk_div = HPM_DIV_ROUND_UP(cfg->lane_mbps / 8, 20);
274     esc_clk_div = esc_clk_div <= 1 ? 2 : esc_clk_div;
275 
276     ptr->CLKMGR_CFG = MIPI_DSI_CLKMGR_CFG_TO_CLK_DIVISION_SET(10) |
277         MIPI_DSI_CLKMGR_CFG_TX_ESC_CLK_DIVISION_SET(esc_clk_div);
278 
279     mipi_dsi_config_format(ptr, cfg->pixel_format);
280     ptr->DPI_VCID = MIPI_DSI_DPI_VCID_DPI_VCID_SET(cfg->channel);
281     ptr->DPI_LP_CMD_TIM = MIPI_DSI_DPI_LP_CMD_TIM_OUTVACT_LPCMD_TIME_SET(4) |
282                             MIPI_DSI_DPI_LP_CMD_TIM_OUTVACT_LPCMD_TIME_SET(4);
283 
284     val = MIPI_DSI_PCKHDL_CFG_BTA_EN_MASK |
285             MIPI_DSI_PCKHDL_CFG_EOTP_TX_EN_MASK |
286             MIPI_DSI_PCKHDL_CFG_ECC_RX_EN_MASK |
287             MIPI_DSI_PCKHDL_CFG_CRC_RX_EN_MASK;
288     if (cfg->disable_eotp)
289         val &= ~MIPI_DSI_PCKHDL_CFG_EOTP_TX_EN_MASK;
290     ptr->PCKHDL_CFG = val;
291 
292     val = MIPI_DSI_VID_MODE_CFG_LP_HFP_EN_MASK |
293             MIPI_DSI_VID_MODE_CFG_LP_HBP_EN_MASK |
294             MIPI_DSI_VID_MODE_CFG_LP_VACT_EN_MASK |
295             MIPI_DSI_VID_MODE_CFG_LP_VFP_EN_MASK |
296             MIPI_DSI_VID_MODE_CFG_LP_VBP_EN_MASK |
297             MIPI_DSI_VID_MODE_CFG_LP_VSA_EN_MASK |
298             MIPI_DSI_VID_MODE_CFG_VID_MODE_TYPE_SET(cfg->video_mode);
299     ptr->VID_MODE_CFG = val;
300 
301     ptr->VID_PKT_SIZE = cfg->video_para.hactive;
302 
303     ptr->TO_CNT_CFG = MIPI_DSI_TO_CNT_CFG_HSTX_TO_CNT_SET(1000) |
304                         MIPI_DSI_TO_CNT_CFG_LPRX_TO_CNT_SET(1000);
305 
306     ptr->BTA_TO_CNT = MIPI_DSI_BTA_TO_CNT_BTA_TO_CNT_SET(0xd00);
307 
308     mipi_dsi_video_para_config(ptr, cfg);
309 
310     ptr->PHY_TMR_CFG = MIPI_DSI_PHY_TMR_CFG_PHY_HS2LP_TIME_SET(0x40) |
311                         MIPI_DSI_PHY_TMR_CFG_PHY_LP2HS_TIME_SET(0x40);
312     ptr->PHY_TMR_RD = 10000;
313     ptr->PHY_TMR_LPCLK_CFG = MIPI_DSI_PHY_TMR_LPCLK_CFG_PHY_CLKHS2LP_TIME_SET(0x40) |
314                                 MIPI_DSI_PHY_TMR_LPCLK_CFG_PHY_CLKLP2HS_TIME_SET(0x40);
315     ptr->PHY_IF_CFG = MIPI_DSI_PHY_IF_CFG_PHY_STOP_WAIT_TIME_SET(0x20) |
316                         MIPI_DSI_PHY_IF_CFG_N_LANES_SET(cfg->lanes - 1);
317     ptr->PWR_UP |= MIPI_DSI_PWR_UP_SHUTDOWNZ_MASK;
318 }
319 
mipi_dsi_phy_poweron(MIPI_DSI_Type * ptr)320 void mipi_dsi_phy_poweron(MIPI_DSI_Type *ptr)
321 {
322     ptr->PHY_RSTZ |= MIPI_DSI_PHY_RSTZ_PHY_SHUTDOWNZ_MASK;
323     ptr->PHY_RSTZ |= MIPI_DSI_PHY_RSTZ_PHY_RSTZ_MASK;
324 }
325 
mipi_dsi_phy_powerdown(MIPI_DSI_Type * ptr)326 void mipi_dsi_phy_powerdown(MIPI_DSI_Type *ptr)
327 {
328     ptr->PHY_RSTZ &= ~(MIPI_DSI_PHY_RSTZ_PHY_SHUTDOWNZ_MASK |
329                         MIPI_DSI_PHY_RSTZ_PHY_RSTZ_MASK);
330 }
331 
mipi_dsi_video_mode_hs_transfer_enable(MIPI_DSI_Type * ptr)332 void mipi_dsi_video_mode_hs_transfer_enable(MIPI_DSI_Type *ptr)
333 {
334     ptr->PWR_UP &= ~MIPI_DSI_PWR_UP_SHUTDOWNZ_MASK;
335     ptr->LPCLK_CTRL |= MIPI_DSI_LPCLK_CTRL_PHY_TXREQUESTCLKHS_MASK;
336     ptr->MODE_CFG = MIPI_DSI_MODE_CFG_CMD_VIDEO_MODE_SET(0);
337     ptr->PWR_UP |= MIPI_DSI_PWR_UP_SHUTDOWNZ_MASK;
338 }
339 
mipi_dsi_video_mode_hs_transfer_disable(MIPI_DSI_Type * ptr)340 void mipi_dsi_video_mode_hs_transfer_disable(MIPI_DSI_Type *ptr)
341 {
342     ptr->PWR_UP &= ~MIPI_DSI_PWR_UP_SHUTDOWNZ_MASK;
343     ptr->LPCLK_CTRL &= ~MIPI_DSI_LPCLK_CTRL_PHY_TXREQUESTCLKHS_MASK;
344 }
345 
mipi_dsi_lp_cmd_transfer(MIPI_DSI_Type * ptr,const mipi_dsi_msg_t * msg)346 int mipi_dsi_lp_cmd_transfer(MIPI_DSI_Type *ptr, const mipi_dsi_msg_t *msg)
347 {
348     struct mipi_dsi_packet packet;
349     int ret = -1;
350     int val;
351 
352     /* do some minimum sanity checking */
353     if (!mipi_dsi_packet_format_is_short(msg->type) &&
354         !mipi_dsi_packet_format_is_long(msg->type))
355         return ret;
356 
357     ptr->VID_MODE_CFG |= MIPI_DSI_VID_MODE_CFG_LP_CMD_EN_MASK;
358     ptr->LPCLK_CTRL &= ~MIPI_DSI_LPCLK_CTRL_PHY_TXREQUESTCLKHS_MASK;
359 
360     /* create a packet to the DSI protocol */
361     if (mipi_dsi_create_packet(&packet, msg) == false) {
362         return ret;
363     }
364 
365     ptr->CMD_MODE_CFG = 1u<<24 | 1u<<19 | 1u<<18 | 1u<<17 |
366                         1u<<16 | 1u<<14 | 1u<<13 | 1u<<12 |
367                         1u<<11 | 1u<<10 | 1u<<9 | 1u<<8;
368 
369     /* config to cmd mode */
370     ptr->MODE_CFG = MIPI_DSI_MODE_CFG_CMD_VIDEO_MODE_SET(1);
371 
372     /* Send payload */
373     while (packet.payload_length > 0) {
374         /*
375          * Alternatively, you can always keep the FIFO
376          * nearly full by monitoring the FIFO state until
377          * it is not full, and then writea single word of data.
378          * This solution is more resource consuming
379          * but it simultaneously avoids FIFO starvation,
380          * making it possible to use FIFO sizes smaller than
381          * the amount of data of the longest packet to be written.
382          */
383         if (mipi_dsi_genif_wait_w_pld_fifo_not_full(ptr) == false)
384             return ret;
385 
386         if (packet.payload_length < 4) {
387             /* send residu payload */
388             val = 0;
389             memcpy(&val, packet.payload, packet.payload_length);
390             packet.payload_length = 0;
391         } else {
392             val = get_le32(packet.payload);
393             packet.payload += 4;
394             packet.payload_length -= 4;
395         }
396         ptr->GEN_PLD_DATA = val;
397     }
398 
399     if (mipi_dsi_genif_wait_cmd_fifo_not_full(ptr) == false)
400         return ret;
401 
402     /* Send packet header */
403     val = get_le32(packet.header);
404     ptr->GEN_HDR = val;
405 
406     if (mipi_dsi_genif_wait_write_fifo_empty(ptr) == false)
407         return ret;
408 
409     if (msg->rx_len) {
410         if (dw_mipi_dsi_read_from_fifo(ptr, msg) == false)
411             return ret;
412     }
413 
414     return msg->rx_len ? msg->rx_len : msg->tx_len;
415 }
416 
mipi_dsi_set_maximum_return_packet_size(MIPI_DSI_Type * ptr,uint8_t channel,uint16_t value)417 int mipi_dsi_set_maximum_return_packet_size(MIPI_DSI_Type *ptr, uint8_t channel, uint16_t value)
418 {
419     uint8_t tx[2] = {value & 0xff, value >> 8};
420     struct mipi_dsi_msg msg = {
421         .channel = channel,
422         .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
423         .tx_len = sizeof(tx),
424         .tx_buf = tx,
425     };
426 
427     int ret = mipi_dsi_lp_cmd_transfer(ptr, &msg);
428 
429     return (ret < 0) ? false : true;
430 }
431 
mipi_dsi_generic_write(MIPI_DSI_Type * ptr,uint8_t channel,const void * payload,uint16_t size)432 int mipi_dsi_generic_write(MIPI_DSI_Type *ptr, uint8_t channel, const void *payload,
433 			       uint16_t size)
434 {
435     struct mipi_dsi_msg msg = {
436         .channel = channel,
437         .tx_buf = payload,
438         .tx_len = size
439     };
440 
441     switch (size) {
442     case 0:
443         msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
444         break;
445     case 1:
446         msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
447         break;
448     case 2:
449         msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
450         break;
451     default:
452         msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
453         break;
454     }
455 
456     return mipi_dsi_lp_cmd_transfer(ptr, &msg);
457 }
458 
459 
mipi_dsi_generic_read(MIPI_DSI_Type * ptr,uint8_t channel,const void * params,uint16_t num_params,void * data,uint16_t size)460 int mipi_dsi_generic_read(MIPI_DSI_Type *ptr, uint8_t channel, const void *params,
461 			      uint16_t num_params, void *data, uint16_t size)
462 {
463     struct mipi_dsi_msg msg = {
464         .channel = channel,
465         .tx_len = num_params,
466         .tx_buf = params,
467         .rx_len = size,
468         .rx_buf = data
469     };
470 
471     switch (num_params) {
472     case 0:
473         msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
474         break;
475     case 1:
476         msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
477         break;
478     case 2:
479         msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
480         break;
481     default:
482         return -1;
483     }
484 
485     return mipi_dsi_lp_cmd_transfer(ptr, &msg);
486 }
487 
488 
mipi_dsi_dcs_write_buffer(MIPI_DSI_Type * ptr,uint8_t channel,const void * data,uint16_t len)489 int mipi_dsi_dcs_write_buffer(MIPI_DSI_Type *ptr, uint8_t channel,
490 				  const void *data, uint16_t len)
491 {
492     struct mipi_dsi_msg msg = {
493         .channel = channel,
494         .tx_buf = data,
495         .tx_len = len
496     };
497 
498     switch (len) {
499     case 0:
500         return -1;
501     case 1:
502         msg.type = MIPI_DSI_DCS_SHORT_WRITE;
503         break;
504     case 2:
505         msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
506         break;
507     default:
508         msg.type = MIPI_DSI_DCS_LONG_WRITE;
509         break;
510     }
511 
512     return mipi_dsi_lp_cmd_transfer(ptr, &msg);
513 }
514 
mipi_dsi_dcs_write(MIPI_DSI_Type * ptr,uint8_t channel,uint8_t cmd,const void * data,uint16_t len)515 int mipi_dsi_dcs_write(MIPI_DSI_Type *ptr, uint8_t channel, uint8_t cmd,
516 			   const void *data, uint16_t len)
517 {
518     int err;
519     uint16_t size;
520     uint8_t tx[128];
521 
522     if (len < sizeof(tx)) {
523         size = 1 + len;
524         tx[0] = cmd;
525         if (len > 0)
526             memcpy(&tx[1], data, len);
527     } else {
528         return -1;
529     }
530 
531     err = mipi_dsi_dcs_write_buffer(ptr, channel, tx, size);
532 
533     return err;
534 }
535 
mipi_dsi_dcs_read(MIPI_DSI_Type * ptr,uint8_t channel,uint8_t cmd,void * data,uint16_t len)536 int mipi_dsi_dcs_read(MIPI_DSI_Type *ptr, uint8_t channel, uint8_t cmd, void *data, uint16_t len)
537 {
538 	struct mipi_dsi_msg msg = {
539 		.channel = channel,
540 		.type = MIPI_DSI_DCS_READ,
541 		.tx_buf = &cmd,
542 		.tx_len = 1,
543 		.rx_buf = data,
544 		.rx_len = len
545 	};
546 
547 	return mipi_dsi_lp_cmd_transfer(ptr, &msg);
548 }