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 "dw-mipi-dsi.h"
6 #include <ddk/debug.h>
7 #include <ddktl/device.h>
8 
9 namespace astro_display {
10 
11 #define READ32_MIPI_DSI_REG(a)              mipi_dsi_mmio_->Read32(a)
12 #define WRITE32_MIPI_DSI_REG(a, v)          mipi_dsi_mmio_->Write32(v, a)
13 
14 
IsPldREmpty()15 inline bool DwMipiDsi::IsPldREmpty() {
16     return (GET_BIT32(MIPI_DSI, DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_R_EMPTY, 1) == 1);
17 }
18 
IsPldRFull()19 inline bool DwMipiDsi::IsPldRFull() {
20     return (GET_BIT32(MIPI_DSI, DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_R_FULL, 1) == 1);
21 }
22 
IsPldWEmpty()23 inline bool DwMipiDsi::IsPldWEmpty() {
24     return (GET_BIT32(MIPI_DSI, DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_W_EMPTY, 1) == 1);
25 }
26 
IsPldWFull()27 inline bool DwMipiDsi::IsPldWFull() {
28     return (GET_BIT32(MIPI_DSI, DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_W_FULL, 1) == 1);
29 }
30 
IsCmdEmpty()31 inline bool DwMipiDsi::IsCmdEmpty() {
32     return (GET_BIT32(MIPI_DSI, DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_CMD_EMPTY, 1) == 1);
33 }
34 
IsCmdFull()35 inline bool DwMipiDsi::IsCmdFull() {
36     return (GET_BIT32(MIPI_DSI, DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_CMD_FULL, 1) == 1);
37 }
38 
WaitforFifo(uint32_t reg,uint32_t bit,uint32_t val)39 zx_status_t DwMipiDsi::WaitforFifo(uint32_t reg, uint32_t bit, uint32_t val) {
40     int retry = MIPI_DSI_RETRY_MAX;
41     while (GET_BIT32(MIPI_DSI, reg, bit, 1) != val && retry--) {
42         zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
43     }
44     if (retry <= 0) {
45         return ZX_ERR_TIMED_OUT;
46     }
47     return ZX_OK;
48 }
49 
WaitforPldWNotFull()50 zx_status_t DwMipiDsi::WaitforPldWNotFull() {
51     return WaitforFifo(DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_W_FULL, 0);
52 }
53 
WaitforPldWEmpty()54 zx_status_t DwMipiDsi::WaitforPldWEmpty() {
55     return WaitforFifo(DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_W_EMPTY, 1);
56 }
57 
WaitforPldRFull()58 zx_status_t DwMipiDsi::WaitforPldRFull() {
59     return WaitforFifo(DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_R_FULL, 1);
60 }
61 
WaitforPldRNotEmpty()62 zx_status_t DwMipiDsi::WaitforPldRNotEmpty() {
63     return WaitforFifo(DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_PLD_R_EMPTY, 0);
64 }
65 
WaitforCmdNotFull()66 zx_status_t DwMipiDsi::WaitforCmdNotFull() {
67     return WaitforFifo(DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_CMD_FULL, 0);
68 }
69 
WaitforCmdEmpty()70 zx_status_t DwMipiDsi::WaitforCmdEmpty() {
71     return WaitforFifo(DW_DSI_CMD_PKT_STATUS, CMD_PKT_STATUS_CMD_EMPTY, 1);
72 }
73 
DumpCmd(const MipiDsiCmd & cmd)74 void DwMipiDsi::DumpCmd(const MipiDsiCmd& cmd) {
75     zxlogf(ERROR, "\n\t\t MIPI DSI Command:\n");
76     zxlogf(ERROR, "\t\t\t\t VIC = 0x%x (%d)\n", cmd.virt_chn_id, cmd.virt_chn_id);
77     zxlogf(ERROR, "\t\t\t\t Data Type = 0x%x (%d)\n", cmd.dsi_data_type, cmd.dsi_data_type);
78     zxlogf(ERROR, "\t\t\t\t ACK = 0x%x (%d)\n", cmd.flags, cmd.flags);
79     zxlogf(ERROR, "\t\t\t\t Payload size = 0x%lx (%ld)\n", cmd.pld_size, cmd.pld_size);
80     zxlogf(ERROR, "\t\t\t\t Payload Data: [");
81 
82     for (size_t i = 0; i < cmd.pld_size; i++) {
83         zxlogf(ERROR, "0x%x, ", cmd.pld_data[i]);
84     }
85     zxlogf(ERROR, "]\n\n");
86 }
87 
GenericPayloadRead(uint32_t * data)88 zx_status_t DwMipiDsi::GenericPayloadRead(uint32_t* data) {
89     // make sure there is something valid to read from payload fifo
90     if (WaitforPldRNotEmpty() != ZX_OK) {
91         DISP_ERROR("Timeout! PLD R FIFO remained empty\n");
92         return ZX_ERR_TIMED_OUT;
93     }
94     *data = READ32_REG(MIPI_DSI, DW_DSI_GEN_PLD_DATA);
95     return ZX_OK;
96 }
97 
GenericHdrWrite(uint32_t data)98 zx_status_t DwMipiDsi::GenericHdrWrite(uint32_t data) {
99     // make sure cmd fifo is not full before writing into it
100     if (WaitforCmdNotFull() != ZX_OK) {
101         DISP_ERROR("Timeout! CMD FIFO remained full\n");
102         return ZX_ERR_TIMED_OUT;
103     }
104     WRITE32_REG(MIPI_DSI, DW_DSI_GEN_HDR, data);
105     return ZX_OK;
106 }
107 
GenericPayloadWrite(uint32_t data)108 zx_status_t DwMipiDsi::GenericPayloadWrite(uint32_t data) {
109     // Make sure PLD_W is not full before writing into it
110     if (WaitforPldWNotFull() != ZX_OK) {
111         DISP_ERROR("Timeout! PLD W FIFO remained full!\n");
112         return ZX_ERR_TIMED_OUT;
113     }
114     WRITE32_REG(MIPI_DSI, DW_DSI_GEN_PLD_DATA, data);
115     return ZX_OK;
116 }
117 
EnableBta()118 void DwMipiDsi::EnableBta() {
119     // enable ack req after each packet transmission
120     SET_BIT32(MIPI_DSI, DW_DSI_CMD_MODE_CFG,
121         MIPI_DSI_ACK, CMD_MODE_CFG_ACK_RQST_EN, 1);
122     // enable But Turn-Around request
123     SET_BIT32(MIPI_DSI, DW_DSI_PCKHDL_CFG,
124         MIPI_DSI_ACK, PCKHDL_CFG_BTA_EN, 1);
125 }
126 
DisableBta()127 void DwMipiDsi::DisableBta() {
128     // disable ack req after each packet transmission
129     SET_BIT32(MIPI_DSI, DW_DSI_CMD_MODE_CFG,
130         MIPI_DSI_NO_ACK, CMD_MODE_CFG_ACK_RQST_EN, 1);
131     // disable But Turn-Around request
132     SET_BIT32(MIPI_DSI, DW_DSI_PCKHDL_CFG,
133         MIPI_DSI_NO_ACK, PCKHDL_CFG_BTA_EN, 1);
134 }
135 
WaitforBtaAck()136 zx_status_t DwMipiDsi::WaitforBtaAck() {
137     // BTA ACK is complete once Host PHY goes from RX to TX
138     int retry;
139     uint32_t phy_dir;
140 
141     // (1) TX --> RX
142     retry = MIPI_DSI_RETRY_MAX;
143     while (((phy_dir =
144              GET_BIT32(MIPI_DSI, DW_DSI_PHY_STATUS, PHY_STATUS_PHY_DIRECTION, 1)) == PHY_TX) &&
145              retry--) {
146         zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
147     }
148     if (retry <= 0) {
149         DISP_ERROR("Timeout! Phy Direction remained as TX\n");
150         return ZX_ERR_TIMED_OUT;
151     }
152 
153     // (2) RX --> TX
154     retry = MIPI_DSI_RETRY_MAX;
155     while (((phy_dir =
156              GET_BIT32(MIPI_DSI, DW_DSI_PHY_STATUS, PHY_STATUS_PHY_DIRECTION, 1)) == PHY_RX) &&
157              retry--) {
158         zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
159     }
160     if (retry <= 0) {
161         DISP_ERROR("Timeout! Phy Direction remained as RX\n");
162         return ZX_ERR_TIMED_OUT;
163     }
164 
165     return ZX_OK;
166 }
167 
168 // MIPI DSI Functions as implemented by DWC IP
GenWriteShort(const MipiDsiCmd & cmd)169 zx_status_t DwMipiDsi::GenWriteShort(const MipiDsiCmd& cmd) {
170     // Sanity check payload data and size
171     if ((cmd.pld_size > 2) ||
172         (cmd.pld_size > 0 && cmd.pld_data == NULL) ||
173         (cmd.dsi_data_type & MIPI_DSI_DT_GEN_SHORT_WRITE_0) != MIPI_DSI_DT_GEN_SHORT_WRITE_0) {
174         DISP_ERROR("Invalid Gen short cmd sent\n");
175         return ZX_ERR_INVALID_ARGS;
176     }
177 
178     uint32_t regVal = 0;
179     regVal |= GEN_HDR_DT(cmd.dsi_data_type);
180     regVal |= GEN_HDR_VC(cmd.virt_chn_id);
181     if (cmd.pld_size >= 1) {
182         regVal |= GEN_HDR_WC_LSB(cmd.pld_data[0]);
183     }
184     if (cmd.pld_size == 2) {
185         regVal |= GEN_HDR_WC_MSB(cmd.pld_data[1]);
186     }
187 
188     return GenericHdrWrite(regVal);
189 }
190 
DcsWriteShort(const MipiDsiCmd & cmd)191 zx_status_t DwMipiDsi::DcsWriteShort(const MipiDsiCmd& cmd) {
192     // Sanity check payload data and size
193     if ((cmd.pld_size > 1) ||
194         (cmd.pld_data == NULL) ||
195         (cmd.dsi_data_type & MIPI_DSI_DT_DCS_SHORT_WRITE_0) != MIPI_DSI_DT_DCS_SHORT_WRITE_0) {
196         DISP_ERROR("Invalid DCS short command\n");
197         return ZX_ERR_INVALID_ARGS;
198     }
199 
200     uint32_t regVal = 0;
201     regVal |= GEN_HDR_DT(cmd.dsi_data_type);
202     regVal |= GEN_HDR_VC(cmd.virt_chn_id);
203     regVal |= GEN_HDR_WC_LSB(cmd.pld_data[0]);
204     if (cmd.pld_size == 1) {
205         regVal |= GEN_HDR_WC_MSB(cmd.pld_data[1]);
206     }
207 
208     return GenericHdrWrite(regVal);
209 }
210 
211 // This function writes a generic long command. We can only write a maximum of FIFO_DEPTH
212 // to the payload fifo. This value is implementation specific.
GenWriteLong(const MipiDsiCmd & cmd)213 zx_status_t DwMipiDsi::GenWriteLong(const MipiDsiCmd& cmd) {
214     zx_status_t status = ZX_OK;
215     uint32_t pld_data_idx = 0; // payload data index
216     uint32_t regVal = 0;
217     ZX_DEBUG_ASSERT(cmd.pld_size < DWC_DEFAULT_MAX_PLD_FIFO_DEPTH);
218     size_t ts = cmd.pld_size; // initial transfer size
219 
220     if (ts > 0 && cmd.pld_data == NULL) {
221         DISP_ERROR("Invalid generic long write command\n");
222         return ZX_ERR_INVALID_ARGS;
223     }
224 
225     // first write complete words
226     while (ts >= 4) {
227         regVal =    cmd.pld_data[pld_data_idx + 0] << 0  |
228                     cmd.pld_data[pld_data_idx + 1] << 8  |
229                     cmd.pld_data[pld_data_idx + 2] << 16 |
230                     cmd.pld_data[pld_data_idx + 3] << 24;
231         pld_data_idx += 4;
232         if ((status = GenericPayloadWrite(regVal)) != ZX_OK) {
233             DISP_ERROR("Generic Payload write failed! %d\n", status);
234             return status;
235         }
236         ts -= 4;
237     }
238 
239     // Write remaining bytes
240     if (ts > 0) {
241         regVal = cmd.pld_data[pld_data_idx++] << 0;
242         if (ts > 1) {
243             regVal |= cmd.pld_data[pld_data_idx++] << 8;
244         }
245         if (ts > 2) {
246             regVal |= cmd.pld_data[pld_data_idx++] << 16;
247         }
248         if ((status = GenericPayloadWrite(regVal)) != ZX_OK) {
249             DISP_ERROR("Generic Payload write failed! %d\n", status);
250             return status;
251         }
252     }
253 
254     // At this point, we have written all of our mipi payload to FIFO. Let's transmit it
255     regVal = 0;
256     regVal |= GEN_HDR_DT(cmd.dsi_data_type);
257     regVal |= GEN_HDR_VC(cmd.virt_chn_id);
258     regVal |= GEN_HDR_WC_LSB(static_cast<uint32_t>(cmd.pld_size) & 0xFF);
259     regVal |= GEN_HDR_WC_MSB((cmd.pld_size & 0xFF00) >> 16);
260 
261     return GenericHdrWrite(regVal);
262 }
263 
GenRead(const MipiDsiCmd & cmd)264 zx_status_t DwMipiDsi::GenRead(const MipiDsiCmd& cmd) {
265     uint32_t regVal = 0;
266     zx_status_t status = ZX_OK;
267 
268     // valid cmd packet
269     if ((cmd.rsp_data == NULL) || (cmd.pld_size > 2) ||
270         (cmd.pld_size > 0 && cmd.pld_data == NULL)) {
271         DISP_ERROR("Invalid generic read command\n");
272         return ZX_ERR_INVALID_ARGS;
273     }
274 
275     // Check whether max return packet size should be set
276     if (cmd.flags & MIPI_DSI_CMD_FLAGS_SET_MAX) {
277         // We will set the max return size as rlen
278         regVal |= GEN_HDR_VC(cmd.virt_chn_id);
279         regVal |= MIPI_DSI_DT_SET_MAX_RET_PKT;
280         regVal |= GEN_HDR_WC_LSB(static_cast<uint32_t>(cmd.rsp_size) & 0xFF);
281         regVal |= GEN_HDR_WC_MSB((static_cast<uint32_t>(cmd.rsp_size) >> 8) & 0xFF);
282 
283         if ((status = GenericHdrWrite(regVal)) != ZX_OK) {
284             // no need to print extra info
285             return status;
286         }
287     }
288 
289     regVal = 0;
290     regVal |= GEN_HDR_DT(cmd.dsi_data_type);
291     regVal |= GEN_HDR_VC(cmd.virt_chn_id);
292     if (cmd.pld_size >= 1) {
293         regVal |= GEN_HDR_WC_LSB(cmd.pld_data[0]);
294     }
295     if (cmd.pld_size == 2) {
296         regVal |= GEN_HDR_WC_MSB(cmd.pld_data[1]);
297     }
298 
299     // Packet is ready. Let's enable BTA first
300     EnableBta();
301 
302     if ((status = GenericHdrWrite(regVal)) != ZX_OK) {
303         // no need to print extra error msg
304         return status;
305     }
306 
307     if ((status = WaitforBtaAck()) != ZX_OK) {
308         // bta never returned. no need to print extra error msg
309         return status;
310     }
311 
312     // Got ACK. Let's start reading
313     // We should only read rlen worth of DATA. Let's hope the device is not sending
314     // more than it should.
315     size_t ts = cmd.rsp_size;
316     uint32_t rsp_data_idx = 0;
317     uint32_t data;
318     while (ts >= 4) {
319         if ((status = GenericPayloadRead(&data)) != ZX_OK) {
320             DISP_ERROR("Something went wrong when reading data. Aborting\n");
321             return status;
322         }
323         cmd.rsp_data[rsp_data_idx++] = static_cast<uint8_t>((data >> 0) & 0xFF);
324         cmd.rsp_data[rsp_data_idx++] = static_cast<uint8_t>((data >> 8) & 0xFF);
325         cmd.rsp_data[rsp_data_idx++] = static_cast<uint8_t>((data >> 16) & 0xFF);
326         cmd.rsp_data[rsp_data_idx++] = static_cast<uint8_t>((data >> 24) & 0xFF);
327         ts -= 4;
328     }
329 
330     // Read out remaining bytes
331     if (ts > 0) {
332         if ((status = GenericPayloadRead(&data)) != ZX_OK) {
333             DISP_ERROR("Something went wrong when reading data. Aborting\n");
334             return status;
335         }
336         cmd.rsp_data[rsp_data_idx++] = (data >> 0) & 0xFF;
337         if (ts > 1) {
338             cmd.rsp_data[rsp_data_idx++] = (data >> 8) & 0xFF;
339         }
340         if (ts > 2) {
341             cmd.rsp_data[rsp_data_idx++] = (data >> 16) & 0xFF;
342         }
343     }
344 
345     // we are done. Display BTA
346     DisableBta();
347     return status;
348 }
349 
SendCmd(const MipiDsiCmd & cmd)350 zx_status_t DwMipiDsi::SendCmd(const MipiDsiCmd& cmd) {
351 
352     zx_status_t status = ZX_OK;
353 
354     switch (cmd.dsi_data_type) {
355     case MIPI_DSI_DT_GEN_SHORT_WRITE_0:
356     case MIPI_DSI_DT_GEN_SHORT_WRITE_1:
357     case MIPI_DSI_DT_GEN_SHORT_WRITE_2:
358         status = GenWriteShort(cmd);
359         break;
360     case MIPI_DSI_DT_GEN_LONG_WRITE:
361     case MIPI_DSI_DT_DCS_LONG_WRITE:
362         status = GenWriteLong(cmd);
363         break;
364     case MIPI_DSI_DT_GEN_SHORT_READ_0:
365     case MIPI_DSI_DT_GEN_SHORT_READ_1:
366     case MIPI_DSI_DT_GEN_SHORT_READ_2:
367         status = GenRead(cmd);
368         break;
369     case MIPI_DSI_DT_DCS_SHORT_WRITE_0:
370     case MIPI_DSI_DT_DCS_SHORT_WRITE_1:
371         status = DcsWriteShort(cmd);
372         break;
373     case MIPI_DSI_DT_DCS_READ_0:
374     default:
375         DISP_ERROR("Unsupported/Invalid DSI Command type %d\n", cmd.dsi_data_type);
376         status = ZX_ERR_INVALID_ARGS;
377     }
378 
379     if (status != ZX_OK) {
380         DISP_ERROR("Something went wrong is sending command\n");
381         DumpCmd(cmd);
382     }
383 
384     return status;
385 }
386 
Cmd(const uint8_t * tbuf,size_t tlen,uint8_t * rbuf,size_t rlen,bool is_dcs)387 zx_status_t DwMipiDsi::Cmd(const uint8_t* tbuf, size_t tlen, uint8_t* rbuf, size_t rlen,
388                            bool is_dcs) {
389     ZX_DEBUG_ASSERT(initialized_);
390     // Create a command packet
391     MipiDsiCmd cmd;
392     cmd.virt_chn_id = MIPI_DSI_VIRTUAL_CHAN_ID; // TODO(payamm): change name
393     cmd.pld_data = tbuf; // tbuf is allowed to be null
394     cmd.pld_size = tlen;
395     cmd.rsp_data = rbuf; // rbuf is allowed to be null if rlen is 0
396     cmd.rsp_size = rlen;
397     cmd.flags = 0;
398     cmd.dsi_data_type = MIPI_DSI_DT_UNKNOWN;
399 
400     switch (tlen) {
401         case 0:
402             if (rbuf && rlen > 0) {
403                 cmd.dsi_data_type = is_dcs ? MIPI_DSI_DT_DCS_READ_0 :
404                                             MIPI_DSI_DT_GEN_SHORT_READ_0;
405                 cmd.flags |= MIPI_DSI_CMD_FLAGS_ACK | MIPI_DSI_CMD_FLAGS_SET_MAX;
406             } else {
407                 cmd.dsi_data_type = is_dcs ? MIPI_DSI_DT_DCS_SHORT_WRITE_0 :
408                                             MIPI_DSI_DT_GEN_SHORT_WRITE_0;
409             }
410             break;
411         case 1:
412             if (rbuf && rlen > 0) {
413                 if (is_dcs) {
414                     DISP_ERROR("Invalid DCS Read request\n");
415                     return ZX_ERR_INVALID_ARGS;
416                 }
417                 cmd.dsi_data_type = MIPI_DSI_DT_GEN_SHORT_READ_1;
418                 cmd.flags |= MIPI_DSI_CMD_FLAGS_ACK | MIPI_DSI_CMD_FLAGS_SET_MAX;
419             } else {
420                 cmd.dsi_data_type = is_dcs ? MIPI_DSI_DT_DCS_SHORT_WRITE_1 :
421                                             MIPI_DSI_DT_GEN_SHORT_WRITE_1;
422             }
423             break;
424         case 2:
425             if (is_dcs) {
426                 DISP_ERROR("Invalid DCS request\n");
427                 return ZX_ERR_INVALID_ARGS;
428             }
429             if (rbuf && rlen > 0) {
430                 cmd.flags |= MIPI_DSI_CMD_FLAGS_ACK | MIPI_DSI_CMD_FLAGS_SET_MAX;
431             } else {
432                 cmd.dsi_data_type = MIPI_DSI_DT_GEN_SHORT_WRITE_2;
433             }
434             break;
435         default:
436             if (rbuf || rlen > 0) {
437                 DISP_ERROR("Invalid DSI GEN READ Command!\n");
438                 return ZX_ERR_INVALID_ARGS;
439             } else {
440                 cmd.dsi_data_type = is_dcs ? MIPI_DSI_DT_DCS_LONG_WRITE :
441                                             MIPI_DSI_DT_GEN_LONG_WRITE;
442             }
443             break;
444     }
445 
446     // packet command has been created.
447     return SendCmd(cmd);
448 }
449 
Init(zx_device_t * parent)450 zx_status_t DwMipiDsi::Init(zx_device_t* parent) {
451     if (initialized_) {
452         return ZX_OK;
453     }
454     zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev_);
455     if (status != ZX_OK) {
456         DISP_ERROR("DwMipiDsi: Could not get ZX_PROTOCOL_PLATFORKM_DEV protocol\n");
457         return status;
458     }
459 
460     // Map Mipi Dsi registers
461     mmio_buffer_t mmio;
462     status = pdev_map_mmio_buffer2(&pdev_, MMIO_MPI_DSI, ZX_CACHE_POLICY_UNCACHED_DEVICE,
463                                   &mmio);
464     if (status != ZX_OK) {
465         DISP_ERROR("DwMipiDsi: Could not map MIPI DSI mmio\n");
466         return status;
467     }
468     mipi_dsi_mmio_ = ddk::MmioBuffer(mmio);
469 
470     initialized_ = true;
471     return status;
472 }
473 
474 } // namespace astro_display
475