1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * i.MX8 NWL MIPI DSI host driver |
4 | * |
5 | * Copyright (C) 2017 NXP |
6 | * Copyright (C) 2020 Purism SPC |
7 | */ |
8 | |
9 | #include <linux/bitfield.h> |
10 | #include <linux/bits.h> |
11 | #include <linux/clk.h> |
12 | #include <linux/irq.h> |
13 | #include <linux/math64.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/media-bus-format.h> |
16 | #include <linux/module.h> |
17 | #include <linux/mux/consumer.h> |
18 | #include <linux/of.h> |
19 | #include <linux/phy/phy.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/regmap.h> |
22 | #include <linux/reset.h> |
23 | #include <linux/sys_soc.h> |
24 | #include <linux/time64.h> |
25 | |
26 | #include <drm/drm_atomic_state_helper.h> |
27 | #include <drm/drm_bridge.h> |
28 | #include <drm/drm_mipi_dsi.h> |
29 | #include <drm/drm_of.h> |
30 | #include <drm/drm_print.h> |
31 | |
32 | #include <video/mipi_display.h> |
33 | |
34 | #include "nwl-dsi.h" |
35 | |
36 | #define DRV_NAME "nwl-dsi" |
37 | |
38 | /* i.MX8 NWL quirks */ |
39 | /* i.MX8MQ errata E11418 */ |
40 | #define E11418_HS_MODE_QUIRK BIT(0) |
41 | |
42 | #define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500) |
43 | |
44 | enum transfer_direction { |
45 | DSI_PACKET_SEND, |
46 | DSI_PACKET_RECEIVE, |
47 | }; |
48 | |
49 | #define NWL_DSI_ENDPOINT_LCDIF 0 |
50 | #define NWL_DSI_ENDPOINT_DCSS 1 |
51 | |
52 | struct nwl_dsi_transfer { |
53 | const struct mipi_dsi_msg *msg; |
54 | struct mipi_dsi_packet packet; |
55 | struct completion completed; |
56 | |
57 | int status; /* status of transmission */ |
58 | enum transfer_direction direction; |
59 | bool need_bta; |
60 | u8 cmd; |
61 | u16 rx_word_count; |
62 | size_t tx_len; /* in bytes */ |
63 | size_t rx_len; /* in bytes */ |
64 | }; |
65 | |
66 | struct nwl_dsi { |
67 | struct drm_bridge bridge; |
68 | struct mipi_dsi_host dsi_host; |
69 | struct device *dev; |
70 | struct phy *phy; |
71 | union phy_configure_opts phy_cfg; |
72 | unsigned int quirks; |
73 | |
74 | struct regmap *regmap; |
75 | int irq; |
76 | /* |
77 | * The DSI host controller needs this reset sequence according to NWL: |
78 | * 1. Deassert pclk reset to get access to DSI regs |
79 | * 2. Configure DSI Host and DPHY and enable DPHY |
80 | * 3. Deassert ESC and BYTE resets to allow host TX operations) |
81 | * 4. Send DSI cmds to configure peripheral (handled by panel drv) |
82 | * 5. Deassert DPI reset so DPI receives pixels and starts sending |
83 | * DSI data |
84 | * |
85 | * TODO: Since panel_bridges do their DSI setup in enable we |
86 | * currently have 4. and 5. swapped. |
87 | */ |
88 | struct reset_control *rst_byte; |
89 | struct reset_control *rst_esc; |
90 | struct reset_control *rst_dpi; |
91 | struct reset_control *rst_pclk; |
92 | struct mux_control *mux; |
93 | |
94 | /* DSI clocks */ |
95 | struct clk *phy_ref_clk; |
96 | struct clk *rx_esc_clk; |
97 | struct clk *tx_esc_clk; |
98 | struct clk *core_clk; |
99 | /* |
100 | * hardware bug: the i.MX8MQ needs this clock on during reset |
101 | * even when not using LCDIF. |
102 | */ |
103 | struct clk *lcdif_clk; |
104 | |
105 | /* dsi lanes */ |
106 | u32 lanes; |
107 | enum mipi_dsi_pixel_format format; |
108 | struct drm_display_mode mode; |
109 | unsigned long dsi_mode_flags; |
110 | int error; |
111 | |
112 | struct nwl_dsi_transfer *xfer; |
113 | }; |
114 | |
115 | static const struct regmap_config nwl_dsi_regmap_config = { |
116 | .reg_bits = 16, |
117 | .val_bits = 32, |
118 | .reg_stride = 4, |
119 | .max_register = NWL_DSI_IRQ_MASK2, |
120 | .name = DRV_NAME, |
121 | }; |
122 | |
123 | static inline struct nwl_dsi *bridge_to_dsi(struct drm_bridge *bridge) |
124 | { |
125 | return container_of(bridge, struct nwl_dsi, bridge); |
126 | } |
127 | |
128 | static int nwl_dsi_clear_error(struct nwl_dsi *dsi) |
129 | { |
130 | int ret = dsi->error; |
131 | |
132 | dsi->error = 0; |
133 | return ret; |
134 | } |
135 | |
136 | static void nwl_dsi_write(struct nwl_dsi *dsi, unsigned int reg, u32 val) |
137 | { |
138 | int ret; |
139 | |
140 | if (dsi->error) |
141 | return; |
142 | |
143 | ret = regmap_write(map: dsi->regmap, reg, val); |
144 | if (ret < 0) { |
145 | DRM_DEV_ERROR(dsi->dev, |
146 | "Failed to write NWL DSI reg 0x%x: %d\n" , reg, |
147 | ret); |
148 | dsi->error = ret; |
149 | } |
150 | } |
151 | |
152 | static u32 nwl_dsi_read(struct nwl_dsi *dsi, u32 reg) |
153 | { |
154 | unsigned int val; |
155 | int ret; |
156 | |
157 | if (dsi->error) |
158 | return 0; |
159 | |
160 | ret = regmap_read(map: dsi->regmap, reg, val: &val); |
161 | if (ret < 0) { |
162 | DRM_DEV_ERROR(dsi->dev, "Failed to read NWL DSI reg 0x%x: %d\n" , |
163 | reg, ret); |
164 | dsi->error = ret; |
165 | } |
166 | return val; |
167 | } |
168 | |
169 | static int nwl_dsi_get_dpi_pixel_format(enum mipi_dsi_pixel_format format) |
170 | { |
171 | switch (format) { |
172 | case MIPI_DSI_FMT_RGB565: |
173 | return NWL_DSI_PIXEL_FORMAT_16; |
174 | case MIPI_DSI_FMT_RGB666: |
175 | return NWL_DSI_PIXEL_FORMAT_18L; |
176 | case MIPI_DSI_FMT_RGB666_PACKED: |
177 | return NWL_DSI_PIXEL_FORMAT_18; |
178 | case MIPI_DSI_FMT_RGB888: |
179 | return NWL_DSI_PIXEL_FORMAT_24; |
180 | default: |
181 | return -EINVAL; |
182 | } |
183 | } |
184 | |
185 | /* |
186 | * ps2bc - Picoseconds to byte clock cycles |
187 | */ |
188 | static u32 ps2bc(struct nwl_dsi *dsi, unsigned long long ps) |
189 | { |
190 | u32 bpp = mipi_dsi_pixel_format_to_bpp(fmt: dsi->format); |
191 | |
192 | return DIV64_U64_ROUND_UP(ps * dsi->mode.clock * bpp, |
193 | dsi->lanes * 8ULL * NSEC_PER_SEC); |
194 | } |
195 | |
196 | /* |
197 | * ui2bc - UI time periods to byte clock cycles |
198 | */ |
199 | static u32 ui2bc(unsigned int ui) |
200 | { |
201 | return DIV_ROUND_UP(ui, BITS_PER_BYTE); |
202 | } |
203 | |
204 | /* |
205 | * us2bc - micro seconds to lp clock cycles |
206 | */ |
207 | static u32 us2lp(u32 lp_clk_rate, unsigned long us) |
208 | { |
209 | return DIV_ROUND_UP(us * lp_clk_rate, USEC_PER_SEC); |
210 | } |
211 | |
212 | static int nwl_dsi_config_host(struct nwl_dsi *dsi) |
213 | { |
214 | u32 cycles; |
215 | struct phy_configure_opts_mipi_dphy *cfg = &dsi->phy_cfg.mipi_dphy; |
216 | |
217 | if (dsi->lanes < 1 || dsi->lanes > 4) |
218 | return -EINVAL; |
219 | |
220 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "DSI Lanes %d\n" , dsi->lanes); |
221 | nwl_dsi_write(dsi, NWL_DSI_CFG_NUM_LANES, val: dsi->lanes - 1); |
222 | |
223 | if (dsi->dsi_mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { |
224 | nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, val: 0x01); |
225 | nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, val: 0x01); |
226 | } else { |
227 | nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, val: 0x00); |
228 | nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, val: 0x00); |
229 | } |
230 | |
231 | /* values in byte clock cycles */ |
232 | cycles = ui2bc(ui: cfg->clk_pre); |
233 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_pre: 0x%x\n" , cycles); |
234 | nwl_dsi_write(dsi, NWL_DSI_CFG_T_PRE, val: cycles); |
235 | cycles = ps2bc(dsi, ps: cfg->lpx + cfg->clk_prepare + cfg->clk_zero); |
236 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap (pre): 0x%x\n" , cycles); |
237 | cycles += ui2bc(ui: cfg->clk_pre); |
238 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_post: 0x%x\n" , cycles); |
239 | nwl_dsi_write(dsi, NWL_DSI_CFG_T_POST, val: cycles); |
240 | cycles = ps2bc(dsi, ps: cfg->hs_exit); |
241 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n" , cycles); |
242 | nwl_dsi_write(dsi, NWL_DSI_CFG_TX_GAP, val: cycles); |
243 | |
244 | nwl_dsi_write(dsi, NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP, val: 0x01); |
245 | nwl_dsi_write(dsi, NWL_DSI_CFG_HTX_TO_COUNT, val: 0x00); |
246 | nwl_dsi_write(dsi, NWL_DSI_CFG_LRX_H_TO_COUNT, val: 0x00); |
247 | nwl_dsi_write(dsi, NWL_DSI_CFG_BTA_H_TO_COUNT, val: 0x00); |
248 | /* In LP clock cycles */ |
249 | cycles = us2lp(lp_clk_rate: cfg->lp_clk_rate, us: cfg->wakeup); |
250 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_twakeup: 0x%x\n" , cycles); |
251 | nwl_dsi_write(dsi, NWL_DSI_CFG_TWAKEUP, val: cycles); |
252 | |
253 | return nwl_dsi_clear_error(dsi); |
254 | } |
255 | |
256 | static int nwl_dsi_config_dpi(struct nwl_dsi *dsi) |
257 | { |
258 | u32 mode; |
259 | int color_format; |
260 | bool burst_mode; |
261 | int hfront_porch, hback_porch, vfront_porch, vback_porch; |
262 | int hsync_len, vsync_len; |
263 | |
264 | hfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay; |
265 | hsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start; |
266 | hback_porch = dsi->mode.htotal - dsi->mode.hsync_end; |
267 | |
268 | vfront_porch = dsi->mode.vsync_start - dsi->mode.vdisplay; |
269 | vsync_len = dsi->mode.vsync_end - dsi->mode.vsync_start; |
270 | vback_porch = dsi->mode.vtotal - dsi->mode.vsync_end; |
271 | |
272 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "hfront_porch = %d\n" , hfront_porch); |
273 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "hback_porch = %d\n" , hback_porch); |
274 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "hsync_len = %d\n" , hsync_len); |
275 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "hdisplay = %d\n" , dsi->mode.hdisplay); |
276 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "vfront_porch = %d\n" , vfront_porch); |
277 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "vback_porch = %d\n" , vback_porch); |
278 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "vsync_len = %d\n" , vsync_len); |
279 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "vactive = %d\n" , dsi->mode.vdisplay); |
280 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "clock = %d kHz\n" , dsi->mode.clock); |
281 | |
282 | color_format = nwl_dsi_get_dpi_pixel_format(format: dsi->format); |
283 | if (color_format < 0) { |
284 | DRM_DEV_ERROR(dsi->dev, "Invalid color format 0x%x\n" , |
285 | dsi->format); |
286 | return color_format; |
287 | } |
288 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "pixel fmt = %d\n" , dsi->format); |
289 | |
290 | nwl_dsi_write(dsi, NWL_DSI_INTERFACE_COLOR_CODING, NWL_DSI_DPI_24_BIT); |
291 | nwl_dsi_write(dsi, NWL_DSI_PIXEL_FORMAT, val: color_format); |
292 | /* |
293 | * Adjusting input polarity based on the video mode results in |
294 | * a black screen so always pick active low: |
295 | */ |
296 | nwl_dsi_write(dsi, NWL_DSI_VSYNC_POLARITY, |
297 | NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW); |
298 | nwl_dsi_write(dsi, NWL_DSI_HSYNC_POLARITY, |
299 | NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW); |
300 | |
301 | burst_mode = (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_BURST) && |
302 | !(dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE); |
303 | |
304 | if (burst_mode) { |
305 | nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, NWL_DSI_VM_BURST_MODE); |
306 | nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL, val: 256); |
307 | } else { |
308 | mode = ((dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ? |
309 | NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES : |
310 | NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS); |
311 | nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, val: mode); |
312 | nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL, |
313 | val: dsi->mode.hdisplay); |
314 | } |
315 | |
316 | nwl_dsi_write(dsi, NWL_DSI_HFP, val: hfront_porch); |
317 | nwl_dsi_write(dsi, NWL_DSI_HBP, val: hback_porch); |
318 | nwl_dsi_write(dsi, NWL_DSI_HSA, val: hsync_len); |
319 | |
320 | nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, val: 0x0); |
321 | nwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, val: 0x1); |
322 | nwl_dsi_write(dsi, NWL_DSI_USE_NULL_PKT_BLLP, val: 0x0); |
323 | nwl_dsi_write(dsi, NWL_DSI_VC, val: 0x0); |
324 | |
325 | nwl_dsi_write(dsi, NWL_DSI_PIXEL_PAYLOAD_SIZE, val: dsi->mode.hdisplay); |
326 | nwl_dsi_write(dsi, NWL_DSI_VACTIVE, val: dsi->mode.vdisplay - 1); |
327 | nwl_dsi_write(dsi, NWL_DSI_VBP, val: vback_porch); |
328 | nwl_dsi_write(dsi, NWL_DSI_VFP, val: vfront_porch); |
329 | |
330 | return nwl_dsi_clear_error(dsi); |
331 | } |
332 | |
333 | static int nwl_dsi_init_interrupts(struct nwl_dsi *dsi) |
334 | { |
335 | u32 irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK | |
336 | NWL_DSI_RX_PKT_HDR_RCVD_MASK | |
337 | NWL_DSI_TX_FIFO_OVFLW_MASK | |
338 | NWL_DSI_HS_TX_TIMEOUT_MASK); |
339 | |
340 | nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, val: irq_enable); |
341 | nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, val: 0x7); |
342 | |
343 | return nwl_dsi_clear_error(dsi); |
344 | } |
345 | |
346 | static int nwl_dsi_host_attach(struct mipi_dsi_host *dsi_host, |
347 | struct mipi_dsi_device *device) |
348 | { |
349 | struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host); |
350 | struct device *dev = dsi->dev; |
351 | |
352 | DRM_DEV_INFO(dev, "lanes=%u, format=0x%x flags=0x%lx\n" , device->lanes, |
353 | device->format, device->mode_flags); |
354 | |
355 | if (device->lanes < 1 || device->lanes > 4) |
356 | return -EINVAL; |
357 | |
358 | dsi->lanes = device->lanes; |
359 | dsi->format = device->format; |
360 | dsi->dsi_mode_flags = device->mode_flags; |
361 | |
362 | return 0; |
363 | } |
364 | |
365 | static bool nwl_dsi_read_packet(struct nwl_dsi *dsi, u32 status) |
366 | { |
367 | struct device *dev = dsi->dev; |
368 | struct nwl_dsi_transfer *xfer = dsi->xfer; |
369 | int err; |
370 | u8 *payload = xfer->msg->rx_buf; |
371 | u32 val; |
372 | u16 word_count; |
373 | u8 channel; |
374 | u8 data_type; |
375 | |
376 | xfer->status = 0; |
377 | |
378 | if (xfer->rx_word_count == 0) { |
379 | if (!(status & NWL_DSI_RX_PKT_HDR_RCVD)) |
380 | return false; |
381 | /* Get the RX header and parse it */ |
382 | val = nwl_dsi_read(dsi, NWL_DSI_RX_PKT_HEADER); |
383 | err = nwl_dsi_clear_error(dsi); |
384 | if (err) |
385 | xfer->status = err; |
386 | word_count = NWL_DSI_WC(val); |
387 | channel = NWL_DSI_RX_VC(val); |
388 | data_type = NWL_DSI_RX_DT(val); |
389 | |
390 | if (channel != xfer->msg->channel) { |
391 | DRM_DEV_ERROR(dev, |
392 | "[%02X] Channel mismatch (%u != %u)\n" , |
393 | xfer->cmd, channel, xfer->msg->channel); |
394 | xfer->status = -EINVAL; |
395 | return true; |
396 | } |
397 | |
398 | switch (data_type) { |
399 | case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: |
400 | case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: |
401 | if (xfer->msg->rx_len > 1) { |
402 | /* read second byte */ |
403 | payload[1] = word_count >> 8; |
404 | ++xfer->rx_len; |
405 | } |
406 | fallthrough; |
407 | case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: |
408 | case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: |
409 | if (xfer->msg->rx_len > 0) { |
410 | /* read first byte */ |
411 | payload[0] = word_count & 0xff; |
412 | ++xfer->rx_len; |
413 | } |
414 | xfer->status = xfer->rx_len; |
415 | return true; |
416 | case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: |
417 | word_count &= 0xff; |
418 | DRM_DEV_ERROR(dev, "[%02X] DSI error report: 0x%02x\n" , |
419 | xfer->cmd, word_count); |
420 | xfer->status = -EPROTO; |
421 | return true; |
422 | } |
423 | |
424 | if (word_count > xfer->msg->rx_len) { |
425 | DRM_DEV_ERROR(dev, |
426 | "[%02X] Receive buffer too small: %zu (< %u)\n" , |
427 | xfer->cmd, xfer->msg->rx_len, word_count); |
428 | xfer->status = -EINVAL; |
429 | return true; |
430 | } |
431 | |
432 | xfer->rx_word_count = word_count; |
433 | } else { |
434 | /* Set word_count from previous header read */ |
435 | word_count = xfer->rx_word_count; |
436 | } |
437 | |
438 | /* If RX payload is not yet received, wait for it */ |
439 | if (!(status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)) |
440 | return false; |
441 | |
442 | /* Read the RX payload */ |
443 | while (word_count >= 4) { |
444 | val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD); |
445 | payload[0] = (val >> 0) & 0xff; |
446 | payload[1] = (val >> 8) & 0xff; |
447 | payload[2] = (val >> 16) & 0xff; |
448 | payload[3] = (val >> 24) & 0xff; |
449 | payload += 4; |
450 | xfer->rx_len += 4; |
451 | word_count -= 4; |
452 | } |
453 | |
454 | if (word_count > 0) { |
455 | val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD); |
456 | switch (word_count) { |
457 | case 3: |
458 | payload[2] = (val >> 16) & 0xff; |
459 | ++xfer->rx_len; |
460 | fallthrough; |
461 | case 2: |
462 | payload[1] = (val >> 8) & 0xff; |
463 | ++xfer->rx_len; |
464 | fallthrough; |
465 | case 1: |
466 | payload[0] = (val >> 0) & 0xff; |
467 | ++xfer->rx_len; |
468 | break; |
469 | } |
470 | } |
471 | |
472 | xfer->status = xfer->rx_len; |
473 | err = nwl_dsi_clear_error(dsi); |
474 | if (err) |
475 | xfer->status = err; |
476 | |
477 | return true; |
478 | } |
479 | |
480 | static void nwl_dsi_finish_transmission(struct nwl_dsi *dsi, u32 status) |
481 | { |
482 | struct nwl_dsi_transfer *xfer = dsi->xfer; |
483 | bool end_packet = false; |
484 | |
485 | if (!xfer) |
486 | return; |
487 | |
488 | if (xfer->direction == DSI_PACKET_SEND && |
489 | status & NWL_DSI_TX_PKT_DONE) { |
490 | xfer->status = xfer->tx_len; |
491 | end_packet = true; |
492 | } else if (status & NWL_DSI_DPHY_DIRECTION && |
493 | ((status & (NWL_DSI_RX_PKT_HDR_RCVD | |
494 | NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)))) { |
495 | end_packet = nwl_dsi_read_packet(dsi, status); |
496 | } |
497 | |
498 | if (end_packet) |
499 | complete(&xfer->completed); |
500 | } |
501 | |
502 | static void nwl_dsi_begin_transmission(struct nwl_dsi *dsi) |
503 | { |
504 | struct nwl_dsi_transfer *xfer = dsi->xfer; |
505 | struct mipi_dsi_packet *pkt = &xfer->packet; |
506 | const u8 *payload; |
507 | size_t length; |
508 | u16 word_count; |
509 | u8 hs_mode; |
510 | u32 val; |
511 | u32 hs_workaround = 0; |
512 | |
513 | /* Send the payload, if any */ |
514 | length = pkt->payload_length; |
515 | payload = pkt->payload; |
516 | |
517 | while (length >= 4) { |
518 | val = *(u32 *)payload; |
519 | hs_workaround |= !(val & 0xFFFF00); |
520 | nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val); |
521 | payload += 4; |
522 | length -= 4; |
523 | } |
524 | /* Send the rest of the payload */ |
525 | val = 0; |
526 | switch (length) { |
527 | case 3: |
528 | val |= payload[2] << 16; |
529 | fallthrough; |
530 | case 2: |
531 | val |= payload[1] << 8; |
532 | hs_workaround |= !(val & 0xFFFF00); |
533 | fallthrough; |
534 | case 1: |
535 | val |= payload[0]; |
536 | nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val); |
537 | break; |
538 | } |
539 | xfer->tx_len = pkt->payload_length; |
540 | |
541 | /* |
542 | * Send the header |
543 | * header[0] = Virtual Channel + Data Type |
544 | * header[1] = Word Count LSB (LP) or first param (SP) |
545 | * header[2] = Word Count MSB (LP) or second param (SP) |
546 | */ |
547 | word_count = pkt->header[1] | (pkt->header[2] << 8); |
548 | if (hs_workaround && (dsi->quirks & E11418_HS_MODE_QUIRK)) { |
549 | DRM_DEV_DEBUG_DRIVER(dsi->dev, |
550 | "Using hs mode workaround for cmd 0x%x\n" , |
551 | xfer->cmd); |
552 | hs_mode = 1; |
553 | } else { |
554 | hs_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : 1; |
555 | } |
556 | val = NWL_DSI_WC(word_count) | NWL_DSI_TX_VC(xfer->msg->channel) | |
557 | NWL_DSI_TX_DT(xfer->msg->type) | NWL_DSI_HS_SEL(hs_mode) | |
558 | NWL_DSI_BTA_TX(xfer->need_bta); |
559 | nwl_dsi_write(dsi, NWL_DSI_PKT_CONTROL, val); |
560 | |
561 | /* Send packet command */ |
562 | nwl_dsi_write(dsi, NWL_DSI_SEND_PACKET, val: 0x1); |
563 | } |
564 | |
565 | static ssize_t nwl_dsi_host_transfer(struct mipi_dsi_host *dsi_host, |
566 | const struct mipi_dsi_msg *msg) |
567 | { |
568 | struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host); |
569 | struct nwl_dsi_transfer xfer; |
570 | ssize_t ret = 0; |
571 | |
572 | /* Create packet to be sent */ |
573 | dsi->xfer = &xfer; |
574 | ret = mipi_dsi_create_packet(packet: &xfer.packet, msg); |
575 | if (ret < 0) { |
576 | dsi->xfer = NULL; |
577 | return ret; |
578 | } |
579 | |
580 | if ((msg->type & MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM || |
581 | msg->type & MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM || |
582 | msg->type & MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM || |
583 | msg->type & MIPI_DSI_DCS_READ) && |
584 | msg->rx_len > 0 && msg->rx_buf) |
585 | xfer.direction = DSI_PACKET_RECEIVE; |
586 | else |
587 | xfer.direction = DSI_PACKET_SEND; |
588 | |
589 | xfer.need_bta = (xfer.direction == DSI_PACKET_RECEIVE); |
590 | xfer.need_bta |= (msg->flags & MIPI_DSI_MSG_REQ_ACK) ? 1 : 0; |
591 | xfer.msg = msg; |
592 | xfer.status = -ETIMEDOUT; |
593 | xfer.rx_word_count = 0; |
594 | xfer.rx_len = 0; |
595 | xfer.cmd = 0x00; |
596 | if (msg->tx_len > 0) |
597 | xfer.cmd = ((u8 *)(msg->tx_buf))[0]; |
598 | init_completion(x: &xfer.completed); |
599 | |
600 | ret = clk_prepare_enable(clk: dsi->rx_esc_clk); |
601 | if (ret < 0) { |
602 | DRM_DEV_ERROR(dsi->dev, "Failed to enable rx_esc clk: %zd\n" , |
603 | ret); |
604 | return ret; |
605 | } |
606 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled rx_esc clk @%lu Hz\n" , |
607 | clk_get_rate(dsi->rx_esc_clk)); |
608 | |
609 | /* Initiate the DSI packet transmision */ |
610 | nwl_dsi_begin_transmission(dsi); |
611 | |
612 | if (!wait_for_completion_timeout(x: &xfer.completed, |
613 | NWL_DSI_MIPI_FIFO_TIMEOUT)) { |
614 | DRM_DEV_ERROR(dsi_host->dev, "[%02X] DSI transfer timed out\n" , |
615 | xfer.cmd); |
616 | ret = -ETIMEDOUT; |
617 | } else { |
618 | ret = xfer.status; |
619 | } |
620 | |
621 | clk_disable_unprepare(clk: dsi->rx_esc_clk); |
622 | |
623 | return ret; |
624 | } |
625 | |
626 | static const struct mipi_dsi_host_ops nwl_dsi_host_ops = { |
627 | .attach = nwl_dsi_host_attach, |
628 | .transfer = nwl_dsi_host_transfer, |
629 | }; |
630 | |
631 | static irqreturn_t nwl_dsi_irq_handler(int irq, void *data) |
632 | { |
633 | u32 irq_status; |
634 | struct nwl_dsi *dsi = data; |
635 | |
636 | irq_status = nwl_dsi_read(dsi, NWL_DSI_IRQ_STATUS); |
637 | |
638 | if (irq_status & NWL_DSI_TX_FIFO_OVFLW) |
639 | DRM_DEV_ERROR_RATELIMITED(dsi->dev, "tx fifo overflow\n" ); |
640 | |
641 | if (irq_status & NWL_DSI_HS_TX_TIMEOUT) |
642 | DRM_DEV_ERROR_RATELIMITED(dsi->dev, "HS tx timeout\n" ); |
643 | |
644 | if (irq_status & NWL_DSI_TX_PKT_DONE || |
645 | irq_status & NWL_DSI_RX_PKT_HDR_RCVD || |
646 | irq_status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD) |
647 | nwl_dsi_finish_transmission(dsi, status: irq_status); |
648 | |
649 | return IRQ_HANDLED; |
650 | } |
651 | |
652 | static int nwl_dsi_mode_set(struct nwl_dsi *dsi) |
653 | { |
654 | struct device *dev = dsi->dev; |
655 | union phy_configure_opts *phy_cfg = &dsi->phy_cfg; |
656 | int ret; |
657 | |
658 | if (!dsi->lanes) { |
659 | DRM_DEV_ERROR(dev, "Need DSI lanes: %d\n" , dsi->lanes); |
660 | return -EINVAL; |
661 | } |
662 | |
663 | ret = phy_init(phy: dsi->phy); |
664 | if (ret < 0) { |
665 | DRM_DEV_ERROR(dev, "Failed to init DSI phy: %d\n" , ret); |
666 | return ret; |
667 | } |
668 | |
669 | ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY); |
670 | if (ret < 0) { |
671 | DRM_DEV_ERROR(dev, "Failed to set DSI phy mode: %d\n" , ret); |
672 | goto uninit_phy; |
673 | } |
674 | |
675 | ret = phy_configure(phy: dsi->phy, opts: phy_cfg); |
676 | if (ret < 0) { |
677 | DRM_DEV_ERROR(dev, "Failed to configure DSI phy: %d\n" , ret); |
678 | goto uninit_phy; |
679 | } |
680 | |
681 | ret = clk_prepare_enable(clk: dsi->tx_esc_clk); |
682 | if (ret < 0) { |
683 | DRM_DEV_ERROR(dsi->dev, "Failed to enable tx_esc clk: %d\n" , |
684 | ret); |
685 | goto uninit_phy; |
686 | } |
687 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled tx_esc clk @%lu Hz\n" , |
688 | clk_get_rate(dsi->tx_esc_clk)); |
689 | |
690 | ret = nwl_dsi_config_host(dsi); |
691 | if (ret < 0) { |
692 | DRM_DEV_ERROR(dev, "Failed to set up DSI: %d" , ret); |
693 | goto disable_clock; |
694 | } |
695 | |
696 | ret = nwl_dsi_config_dpi(dsi); |
697 | if (ret < 0) { |
698 | DRM_DEV_ERROR(dev, "Failed to set up DPI: %d" , ret); |
699 | goto disable_clock; |
700 | } |
701 | |
702 | ret = phy_power_on(phy: dsi->phy); |
703 | if (ret < 0) { |
704 | DRM_DEV_ERROR(dev, "Failed to power on DPHY (%d)\n" , ret); |
705 | goto disable_clock; |
706 | } |
707 | |
708 | ret = nwl_dsi_init_interrupts(dsi); |
709 | if (ret < 0) |
710 | goto power_off_phy; |
711 | |
712 | return ret; |
713 | |
714 | power_off_phy: |
715 | phy_power_off(phy: dsi->phy); |
716 | disable_clock: |
717 | clk_disable_unprepare(clk: dsi->tx_esc_clk); |
718 | uninit_phy: |
719 | phy_exit(phy: dsi->phy); |
720 | |
721 | return ret; |
722 | } |
723 | |
724 | static int nwl_dsi_disable(struct nwl_dsi *dsi) |
725 | { |
726 | struct device *dev = dsi->dev; |
727 | |
728 | DRM_DEV_DEBUG_DRIVER(dev, "Disabling clocks and phy\n" ); |
729 | |
730 | phy_power_off(phy: dsi->phy); |
731 | phy_exit(phy: dsi->phy); |
732 | |
733 | /* Disabling the clock before the phy breaks enabling dsi again */ |
734 | clk_disable_unprepare(clk: dsi->tx_esc_clk); |
735 | |
736 | return 0; |
737 | } |
738 | |
739 | static void |
740 | nwl_dsi_bridge_atomic_disable(struct drm_bridge *bridge, |
741 | struct drm_bridge_state *old_bridge_state) |
742 | { |
743 | struct nwl_dsi *dsi = bridge_to_dsi(bridge); |
744 | int ret; |
745 | |
746 | nwl_dsi_disable(dsi); |
747 | |
748 | ret = reset_control_assert(rstc: dsi->rst_dpi); |
749 | if (ret < 0) { |
750 | DRM_DEV_ERROR(dsi->dev, "Failed to assert DPI: %d\n" , ret); |
751 | return; |
752 | } |
753 | ret = reset_control_assert(rstc: dsi->rst_byte); |
754 | if (ret < 0) { |
755 | DRM_DEV_ERROR(dsi->dev, "Failed to assert ESC: %d\n" , ret); |
756 | return; |
757 | } |
758 | ret = reset_control_assert(rstc: dsi->rst_esc); |
759 | if (ret < 0) { |
760 | DRM_DEV_ERROR(dsi->dev, "Failed to assert BYTE: %d\n" , ret); |
761 | return; |
762 | } |
763 | ret = reset_control_assert(rstc: dsi->rst_pclk); |
764 | if (ret < 0) { |
765 | DRM_DEV_ERROR(dsi->dev, "Failed to assert PCLK: %d\n" , ret); |
766 | return; |
767 | } |
768 | |
769 | clk_disable_unprepare(clk: dsi->core_clk); |
770 | clk_disable_unprepare(clk: dsi->lcdif_clk); |
771 | |
772 | pm_runtime_put(dev: dsi->dev); |
773 | } |
774 | |
775 | static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi, |
776 | const struct drm_display_mode *mode, |
777 | union phy_configure_opts *phy_opts) |
778 | { |
779 | unsigned long rate; |
780 | int ret; |
781 | |
782 | if (dsi->lanes < 1 || dsi->lanes > 4) |
783 | return -EINVAL; |
784 | |
785 | /* |
786 | * So far the DPHY spec minimal timings work for both mixel |
787 | * dphy and nwl dsi host |
788 | */ |
789 | ret = phy_mipi_dphy_get_default_config(pixel_clock: mode->clock * 1000, |
790 | bpp: mipi_dsi_pixel_format_to_bpp(fmt: dsi->format), lanes: dsi->lanes, |
791 | cfg: &phy_opts->mipi_dphy); |
792 | if (ret < 0) |
793 | return ret; |
794 | |
795 | rate = clk_get_rate(clk: dsi->tx_esc_clk); |
796 | DRM_DEV_DEBUG_DRIVER(dsi->dev, "LP clk is @%lu Hz\n" , rate); |
797 | phy_opts->mipi_dphy.lp_clk_rate = rate; |
798 | |
799 | return 0; |
800 | } |
801 | |
802 | static enum drm_mode_status |
803 | nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge, |
804 | const struct drm_display_info *info, |
805 | const struct drm_display_mode *mode) |
806 | { |
807 | struct nwl_dsi *dsi = bridge_to_dsi(bridge); |
808 | int bpp = mipi_dsi_pixel_format_to_bpp(fmt: dsi->format); |
809 | |
810 | if (mode->clock * bpp > 15000000 * dsi->lanes) |
811 | return MODE_CLOCK_HIGH; |
812 | |
813 | if (mode->clock * bpp < 80000 * dsi->lanes) |
814 | return MODE_CLOCK_LOW; |
815 | |
816 | return MODE_OK; |
817 | } |
818 | |
819 | static int nwl_dsi_bridge_atomic_check(struct drm_bridge *bridge, |
820 | struct drm_bridge_state *bridge_state, |
821 | struct drm_crtc_state *crtc_state, |
822 | struct drm_connector_state *conn_state) |
823 | { |
824 | struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; |
825 | |
826 | /* At least LCDIF + NWL needs active high sync */ |
827 | adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); |
828 | adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC); |
829 | |
830 | /* |
831 | * Do a full modeset if crtc_state->active is changed to be true. |
832 | * This ensures our ->mode_set() is called to get the DSI controller |
833 | * and the PHY ready to send DCS commands, when only the connector's |
834 | * DPMS is brought out of "Off" status. |
835 | */ |
836 | if (crtc_state->active_changed && crtc_state->active) |
837 | crtc_state->mode_changed = true; |
838 | |
839 | return 0; |
840 | } |
841 | |
842 | static void |
843 | nwl_dsi_bridge_mode_set(struct drm_bridge *bridge, |
844 | const struct drm_display_mode *mode, |
845 | const struct drm_display_mode *adjusted_mode) |
846 | { |
847 | struct nwl_dsi *dsi = bridge_to_dsi(bridge); |
848 | struct device *dev = dsi->dev; |
849 | union phy_configure_opts new_cfg; |
850 | unsigned long phy_ref_rate; |
851 | int ret; |
852 | |
853 | ret = nwl_dsi_get_dphy_params(dsi, mode: adjusted_mode, phy_opts: &new_cfg); |
854 | if (ret < 0) |
855 | return; |
856 | |
857 | phy_ref_rate = clk_get_rate(clk: dsi->phy_ref_clk); |
858 | DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n" , phy_ref_rate); |
859 | /* Save the new desired phy config */ |
860 | memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg)); |
861 | |
862 | drm_mode_copy(dst: &dsi->mode, src: adjusted_mode); |
863 | drm_mode_debug_printmodeline(mode: adjusted_mode); |
864 | |
865 | if (pm_runtime_resume_and_get(dev) < 0) |
866 | return; |
867 | |
868 | if (clk_prepare_enable(clk: dsi->lcdif_clk) < 0) |
869 | goto runtime_put; |
870 | if (clk_prepare_enable(clk: dsi->core_clk) < 0) |
871 | goto runtime_put; |
872 | |
873 | /* Step 1 from DSI reset-out instructions */ |
874 | ret = reset_control_deassert(rstc: dsi->rst_pclk); |
875 | if (ret < 0) { |
876 | DRM_DEV_ERROR(dev, "Failed to deassert PCLK: %d\n" , ret); |
877 | goto runtime_put; |
878 | } |
879 | |
880 | /* Step 2 from DSI reset-out instructions */ |
881 | nwl_dsi_mode_set(dsi); |
882 | |
883 | /* Step 3 from DSI reset-out instructions */ |
884 | ret = reset_control_deassert(rstc: dsi->rst_esc); |
885 | if (ret < 0) { |
886 | DRM_DEV_ERROR(dev, "Failed to deassert ESC: %d\n" , ret); |
887 | goto runtime_put; |
888 | } |
889 | ret = reset_control_deassert(rstc: dsi->rst_byte); |
890 | if (ret < 0) { |
891 | DRM_DEV_ERROR(dev, "Failed to deassert BYTE: %d\n" , ret); |
892 | goto runtime_put; |
893 | } |
894 | |
895 | return; |
896 | |
897 | runtime_put: |
898 | pm_runtime_put_sync(dev); |
899 | } |
900 | |
901 | static void |
902 | nwl_dsi_bridge_atomic_enable(struct drm_bridge *bridge, |
903 | struct drm_bridge_state *old_bridge_state) |
904 | { |
905 | struct nwl_dsi *dsi = bridge_to_dsi(bridge); |
906 | int ret; |
907 | |
908 | /* Step 5 from DSI reset-out instructions */ |
909 | ret = reset_control_deassert(rstc: dsi->rst_dpi); |
910 | if (ret < 0) |
911 | DRM_DEV_ERROR(dsi->dev, "Failed to deassert DPI: %d\n" , ret); |
912 | } |
913 | |
914 | static int nwl_dsi_bridge_attach(struct drm_bridge *bridge, |
915 | enum drm_bridge_attach_flags flags) |
916 | { |
917 | struct nwl_dsi *dsi = bridge_to_dsi(bridge); |
918 | struct drm_bridge *panel_bridge; |
919 | |
920 | panel_bridge = devm_drm_of_get_bridge(dev: dsi->dev, node: dsi->dev->of_node, port: 1, endpoint: 0); |
921 | if (IS_ERR(ptr: panel_bridge)) |
922 | return PTR_ERR(ptr: panel_bridge); |
923 | |
924 | return drm_bridge_attach(encoder: bridge->encoder, bridge: panel_bridge, previous: bridge, flags); |
925 | } |
926 | |
927 | static u32 *nwl_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, |
928 | struct drm_bridge_state *bridge_state, |
929 | struct drm_crtc_state *crtc_state, |
930 | struct drm_connector_state *conn_state, |
931 | u32 output_fmt, |
932 | unsigned int *num_input_fmts) |
933 | { |
934 | u32 *input_fmts, input_fmt; |
935 | |
936 | *num_input_fmts = 0; |
937 | |
938 | switch (output_fmt) { |
939 | /* If MEDIA_BUS_FMT_FIXED is tested, return default bus format */ |
940 | case MEDIA_BUS_FMT_FIXED: |
941 | input_fmt = MEDIA_BUS_FMT_RGB888_1X24; |
942 | break; |
943 | case MEDIA_BUS_FMT_RGB888_1X24: |
944 | case MEDIA_BUS_FMT_RGB666_1X18: |
945 | case MEDIA_BUS_FMT_RGB565_1X16: |
946 | input_fmt = output_fmt; |
947 | break; |
948 | default: |
949 | return NULL; |
950 | } |
951 | |
952 | input_fmts = kcalloc(n: 1, size: sizeof(*input_fmts), GFP_KERNEL); |
953 | if (!input_fmts) |
954 | return NULL; |
955 | input_fmts[0] = input_fmt; |
956 | *num_input_fmts = 1; |
957 | |
958 | return input_fmts; |
959 | } |
960 | |
961 | static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = { |
962 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
963 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
964 | .atomic_reset = drm_atomic_helper_bridge_reset, |
965 | .atomic_check = nwl_dsi_bridge_atomic_check, |
966 | .atomic_enable = nwl_dsi_bridge_atomic_enable, |
967 | .atomic_disable = nwl_dsi_bridge_atomic_disable, |
968 | .atomic_get_input_bus_fmts = nwl_bridge_atomic_get_input_bus_fmts, |
969 | .mode_set = nwl_dsi_bridge_mode_set, |
970 | .mode_valid = nwl_dsi_bridge_mode_valid, |
971 | .attach = nwl_dsi_bridge_attach, |
972 | }; |
973 | |
974 | static int nwl_dsi_parse_dt(struct nwl_dsi *dsi) |
975 | { |
976 | struct platform_device *pdev = to_platform_device(dsi->dev); |
977 | struct clk *clk; |
978 | void __iomem *base; |
979 | int ret; |
980 | |
981 | dsi->phy = devm_phy_get(dev: dsi->dev, string: "dphy" ); |
982 | if (IS_ERR(ptr: dsi->phy)) { |
983 | ret = PTR_ERR(ptr: dsi->phy); |
984 | if (ret != -EPROBE_DEFER) |
985 | DRM_DEV_ERROR(dsi->dev, "Could not get PHY: %d\n" , ret); |
986 | return ret; |
987 | } |
988 | |
989 | clk = devm_clk_get(dev: dsi->dev, id: "lcdif" ); |
990 | if (IS_ERR(ptr: clk)) { |
991 | ret = PTR_ERR(ptr: clk); |
992 | DRM_DEV_ERROR(dsi->dev, "Failed to get lcdif clock: %d\n" , |
993 | ret); |
994 | return ret; |
995 | } |
996 | dsi->lcdif_clk = clk; |
997 | |
998 | clk = devm_clk_get(dev: dsi->dev, id: "core" ); |
999 | if (IS_ERR(ptr: clk)) { |
1000 | ret = PTR_ERR(ptr: clk); |
1001 | DRM_DEV_ERROR(dsi->dev, "Failed to get core clock: %d\n" , |
1002 | ret); |
1003 | return ret; |
1004 | } |
1005 | dsi->core_clk = clk; |
1006 | |
1007 | clk = devm_clk_get(dev: dsi->dev, id: "phy_ref" ); |
1008 | if (IS_ERR(ptr: clk)) { |
1009 | ret = PTR_ERR(ptr: clk); |
1010 | DRM_DEV_ERROR(dsi->dev, "Failed to get phy_ref clock: %d\n" , |
1011 | ret); |
1012 | return ret; |
1013 | } |
1014 | dsi->phy_ref_clk = clk; |
1015 | |
1016 | clk = devm_clk_get(dev: dsi->dev, id: "rx_esc" ); |
1017 | if (IS_ERR(ptr: clk)) { |
1018 | ret = PTR_ERR(ptr: clk); |
1019 | DRM_DEV_ERROR(dsi->dev, "Failed to get rx_esc clock: %d\n" , |
1020 | ret); |
1021 | return ret; |
1022 | } |
1023 | dsi->rx_esc_clk = clk; |
1024 | |
1025 | clk = devm_clk_get(dev: dsi->dev, id: "tx_esc" ); |
1026 | if (IS_ERR(ptr: clk)) { |
1027 | ret = PTR_ERR(ptr: clk); |
1028 | DRM_DEV_ERROR(dsi->dev, "Failed to get tx_esc clock: %d\n" , |
1029 | ret); |
1030 | return ret; |
1031 | } |
1032 | dsi->tx_esc_clk = clk; |
1033 | |
1034 | dsi->mux = devm_mux_control_get(dev: dsi->dev, NULL); |
1035 | if (IS_ERR(ptr: dsi->mux)) { |
1036 | ret = PTR_ERR(ptr: dsi->mux); |
1037 | if (ret != -EPROBE_DEFER) |
1038 | DRM_DEV_ERROR(dsi->dev, "Failed to get mux: %d\n" , ret); |
1039 | return ret; |
1040 | } |
1041 | |
1042 | base = devm_platform_ioremap_resource(pdev, index: 0); |
1043 | if (IS_ERR(ptr: base)) |
1044 | return PTR_ERR(ptr: base); |
1045 | |
1046 | dsi->regmap = |
1047 | devm_regmap_init_mmio(dsi->dev, base, &nwl_dsi_regmap_config); |
1048 | if (IS_ERR(ptr: dsi->regmap)) { |
1049 | ret = PTR_ERR(ptr: dsi->regmap); |
1050 | DRM_DEV_ERROR(dsi->dev, "Failed to create NWL DSI regmap: %d\n" , |
1051 | ret); |
1052 | return ret; |
1053 | } |
1054 | |
1055 | dsi->irq = platform_get_irq(pdev, 0); |
1056 | if (dsi->irq < 0) { |
1057 | DRM_DEV_ERROR(dsi->dev, "Failed to get device IRQ: %d\n" , |
1058 | dsi->irq); |
1059 | return dsi->irq; |
1060 | } |
1061 | |
1062 | dsi->rst_pclk = devm_reset_control_get_exclusive(dev: dsi->dev, id: "pclk" ); |
1063 | if (IS_ERR(ptr: dsi->rst_pclk)) { |
1064 | DRM_DEV_ERROR(dsi->dev, "Failed to get pclk reset: %ld\n" , |
1065 | PTR_ERR(dsi->rst_pclk)); |
1066 | return PTR_ERR(ptr: dsi->rst_pclk); |
1067 | } |
1068 | dsi->rst_byte = devm_reset_control_get_exclusive(dev: dsi->dev, id: "byte" ); |
1069 | if (IS_ERR(ptr: dsi->rst_byte)) { |
1070 | DRM_DEV_ERROR(dsi->dev, "Failed to get byte reset: %ld\n" , |
1071 | PTR_ERR(dsi->rst_byte)); |
1072 | return PTR_ERR(ptr: dsi->rst_byte); |
1073 | } |
1074 | dsi->rst_esc = devm_reset_control_get_exclusive(dev: dsi->dev, id: "esc" ); |
1075 | if (IS_ERR(ptr: dsi->rst_esc)) { |
1076 | DRM_DEV_ERROR(dsi->dev, "Failed to get esc reset: %ld\n" , |
1077 | PTR_ERR(dsi->rst_esc)); |
1078 | return PTR_ERR(ptr: dsi->rst_esc); |
1079 | } |
1080 | dsi->rst_dpi = devm_reset_control_get_exclusive(dev: dsi->dev, id: "dpi" ); |
1081 | if (IS_ERR(ptr: dsi->rst_dpi)) { |
1082 | DRM_DEV_ERROR(dsi->dev, "Failed to get dpi reset: %ld\n" , |
1083 | PTR_ERR(dsi->rst_dpi)); |
1084 | return PTR_ERR(ptr: dsi->rst_dpi); |
1085 | } |
1086 | return 0; |
1087 | } |
1088 | |
1089 | static int nwl_dsi_select_input(struct nwl_dsi *dsi) |
1090 | { |
1091 | struct device_node *remote; |
1092 | u32 use_dcss = 1; |
1093 | int ret; |
1094 | |
1095 | remote = of_graph_get_remote_node(node: dsi->dev->of_node, port: 0, |
1096 | NWL_DSI_ENDPOINT_LCDIF); |
1097 | if (remote) { |
1098 | use_dcss = 0; |
1099 | } else { |
1100 | remote = of_graph_get_remote_node(node: dsi->dev->of_node, port: 0, |
1101 | NWL_DSI_ENDPOINT_DCSS); |
1102 | if (!remote) { |
1103 | DRM_DEV_ERROR(dsi->dev, |
1104 | "No valid input endpoint found\n" ); |
1105 | return -EINVAL; |
1106 | } |
1107 | } |
1108 | |
1109 | DRM_DEV_INFO(dsi->dev, "Using %s as input source\n" , |
1110 | (use_dcss) ? "DCSS" : "LCDIF" ); |
1111 | ret = mux_control_try_select(mux: dsi->mux, state: use_dcss); |
1112 | if (ret < 0) |
1113 | DRM_DEV_ERROR(dsi->dev, "Failed to select input: %d\n" , ret); |
1114 | |
1115 | of_node_put(node: remote); |
1116 | return ret; |
1117 | } |
1118 | |
1119 | static int nwl_dsi_deselect_input(struct nwl_dsi *dsi) |
1120 | { |
1121 | int ret; |
1122 | |
1123 | ret = mux_control_deselect(mux: dsi->mux); |
1124 | if (ret < 0) |
1125 | DRM_DEV_ERROR(dsi->dev, "Failed to deselect input: %d\n" , ret); |
1126 | |
1127 | return ret; |
1128 | } |
1129 | |
1130 | static const struct drm_bridge_timings nwl_dsi_timings = { |
1131 | .input_bus_flags = DRM_BUS_FLAG_DE_LOW, |
1132 | }; |
1133 | |
1134 | static const struct of_device_id nwl_dsi_dt_ids[] = { |
1135 | { .compatible = "fsl,imx8mq-nwl-dsi" , }, |
1136 | { /* sentinel */ } |
1137 | }; |
1138 | MODULE_DEVICE_TABLE(of, nwl_dsi_dt_ids); |
1139 | |
1140 | static const struct soc_device_attribute nwl_dsi_quirks_match[] = { |
1141 | { .soc_id = "i.MX8MQ" , .revision = "2.0" , |
1142 | .data = (void *)E11418_HS_MODE_QUIRK }, |
1143 | { /* sentinel. */ } |
1144 | }; |
1145 | |
1146 | static int nwl_dsi_probe(struct platform_device *pdev) |
1147 | { |
1148 | struct device *dev = &pdev->dev; |
1149 | const struct soc_device_attribute *attr; |
1150 | struct nwl_dsi *dsi; |
1151 | int ret; |
1152 | |
1153 | dsi = devm_kzalloc(dev, size: sizeof(*dsi), GFP_KERNEL); |
1154 | if (!dsi) |
1155 | return -ENOMEM; |
1156 | |
1157 | dsi->dev = dev; |
1158 | |
1159 | ret = nwl_dsi_parse_dt(dsi); |
1160 | if (ret) |
1161 | return ret; |
1162 | |
1163 | ret = devm_request_irq(dev, irq: dsi->irq, handler: nwl_dsi_irq_handler, irqflags: 0, |
1164 | devname: dev_name(dev), dev_id: dsi); |
1165 | if (ret < 0) { |
1166 | DRM_DEV_ERROR(dev, "Failed to request IRQ %d: %d\n" , dsi->irq, |
1167 | ret); |
1168 | return ret; |
1169 | } |
1170 | |
1171 | dsi->dsi_host.ops = &nwl_dsi_host_ops; |
1172 | dsi->dsi_host.dev = dev; |
1173 | ret = mipi_dsi_host_register(host: &dsi->dsi_host); |
1174 | if (ret) { |
1175 | DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n" , ret); |
1176 | return ret; |
1177 | } |
1178 | |
1179 | attr = soc_device_match(matches: nwl_dsi_quirks_match); |
1180 | if (attr) |
1181 | dsi->quirks = (uintptr_t)attr->data; |
1182 | |
1183 | dsi->bridge.driver_private = dsi; |
1184 | dsi->bridge.funcs = &nwl_dsi_bridge_funcs; |
1185 | dsi->bridge.of_node = dev->of_node; |
1186 | dsi->bridge.timings = &nwl_dsi_timings; |
1187 | |
1188 | dev_set_drvdata(dev, data: dsi); |
1189 | pm_runtime_enable(dev); |
1190 | |
1191 | ret = nwl_dsi_select_input(dsi); |
1192 | if (ret < 0) { |
1193 | pm_runtime_disable(dev); |
1194 | mipi_dsi_host_unregister(host: &dsi->dsi_host); |
1195 | return ret; |
1196 | } |
1197 | |
1198 | drm_bridge_add(bridge: &dsi->bridge); |
1199 | return 0; |
1200 | } |
1201 | |
1202 | static void nwl_dsi_remove(struct platform_device *pdev) |
1203 | { |
1204 | struct nwl_dsi *dsi = platform_get_drvdata(pdev); |
1205 | |
1206 | nwl_dsi_deselect_input(dsi); |
1207 | mipi_dsi_host_unregister(host: &dsi->dsi_host); |
1208 | drm_bridge_remove(bridge: &dsi->bridge); |
1209 | pm_runtime_disable(dev: &pdev->dev); |
1210 | } |
1211 | |
1212 | static struct platform_driver nwl_dsi_driver = { |
1213 | .probe = nwl_dsi_probe, |
1214 | .remove_new = nwl_dsi_remove, |
1215 | .driver = { |
1216 | .of_match_table = nwl_dsi_dt_ids, |
1217 | .name = DRV_NAME, |
1218 | }, |
1219 | }; |
1220 | |
1221 | module_platform_driver(nwl_dsi_driver); |
1222 | |
1223 | MODULE_AUTHOR("NXP Semiconductor" ); |
1224 | MODULE_AUTHOR("Purism SPC" ); |
1225 | MODULE_DESCRIPTION("Northwest Logic MIPI-DSI driver" ); |
1226 | MODULE_LICENSE("GPL" ); /* GPLv2 or later */ |
1227 | |