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