1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2015 Broadcom |
4 | * Copyright (c) 2014 The Linux Foundation. All rights reserved. |
5 | * Copyright (C) 2013 Red Hat |
6 | * Author: Rob Clark <robdclark@gmail.com> |
7 | */ |
8 | |
9 | /** |
10 | * DOC: VC4 Falcon HDMI module |
11 | * |
12 | * The HDMI core has a state machine and a PHY. On BCM2835, most of |
13 | * the unit operates off of the HSM clock from CPRMAN. It also |
14 | * internally uses the PLLH_PIX clock for the PHY. |
15 | * |
16 | * HDMI infoframes are kept within a small packet ram, where each |
17 | * packet can be individually enabled for including in a frame. |
18 | * |
19 | * HDMI audio is implemented entirely within the HDMI IP block. A |
20 | * register in the HDMI encoder takes SPDIF frames from the DMA engine |
21 | * and transfers them over an internal MAI (multi-channel audio |
22 | * interconnect) bus to the encoder side for insertion into the video |
23 | * blank regions. |
24 | * |
25 | * The driver's HDMI encoder does not yet support power management. |
26 | * The HDMI encoder's power domain and the HSM/pixel clocks are kept |
27 | * continuously running, and only the HDMI logic and packet ram are |
28 | * powered off/on at disable/enable time. |
29 | * |
30 | * The driver does not yet support CEC control, though the HDMI |
31 | * encoder block has CEC support. |
32 | */ |
33 | |
34 | #include <drm/display/drm_hdmi_helper.h> |
35 | #include <drm/display/drm_scdc_helper.h> |
36 | #include <drm/drm_atomic_helper.h> |
37 | #include <drm/drm_drv.h> |
38 | #include <drm/drm_edid.h> |
39 | #include <drm/drm_probe_helper.h> |
40 | #include <drm/drm_simple_kms_helper.h> |
41 | #include <linux/clk.h> |
42 | #include <linux/component.h> |
43 | #include <linux/gpio/consumer.h> |
44 | #include <linux/i2c.h> |
45 | #include <linux/of.h> |
46 | #include <linux/of_address.h> |
47 | #include <linux/pm_runtime.h> |
48 | #include <linux/rational.h> |
49 | #include <linux/reset.h> |
50 | #include <sound/dmaengine_pcm.h> |
51 | #include <sound/hdmi-codec.h> |
52 | #include <sound/pcm_drm_eld.h> |
53 | #include <sound/pcm_params.h> |
54 | #include <sound/soc.h> |
55 | #include "media/cec.h" |
56 | #include "vc4_drv.h" |
57 | #include "vc4_hdmi.h" |
58 | #include "vc4_hdmi_regs.h" |
59 | #include "vc4_regs.h" |
60 | |
61 | #define VC5_HDMI_HORZA_HFP_SHIFT 16 |
62 | #define VC5_HDMI_HORZA_HFP_MASK VC4_MASK(28, 16) |
63 | #define VC5_HDMI_HORZA_VPOS BIT(15) |
64 | #define VC5_HDMI_HORZA_HPOS BIT(14) |
65 | #define VC5_HDMI_HORZA_HAP_SHIFT 0 |
66 | #define VC5_HDMI_HORZA_HAP_MASK VC4_MASK(13, 0) |
67 | |
68 | #define VC5_HDMI_HORZB_HBP_SHIFT 16 |
69 | #define VC5_HDMI_HORZB_HBP_MASK VC4_MASK(26, 16) |
70 | #define VC5_HDMI_HORZB_HSP_SHIFT 0 |
71 | #define VC5_HDMI_HORZB_HSP_MASK VC4_MASK(10, 0) |
72 | |
73 | #define VC5_HDMI_VERTA_VSP_SHIFT 24 |
74 | #define VC5_HDMI_VERTA_VSP_MASK VC4_MASK(28, 24) |
75 | #define VC5_HDMI_VERTA_VFP_SHIFT 16 |
76 | #define VC5_HDMI_VERTA_VFP_MASK VC4_MASK(22, 16) |
77 | #define VC5_HDMI_VERTA_VAL_SHIFT 0 |
78 | #define VC5_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0) |
79 | |
80 | #define VC5_HDMI_VERTB_VSPO_SHIFT 16 |
81 | #define VC5_HDMI_VERTB_VSPO_MASK VC4_MASK(29, 16) |
82 | |
83 | #define VC4_HDMI_MISC_CONTROL_PIXEL_REP_SHIFT 0 |
84 | #define VC4_HDMI_MISC_CONTROL_PIXEL_REP_MASK VC4_MASK(3, 0) |
85 | #define VC5_HDMI_MISC_CONTROL_PIXEL_REP_SHIFT 0 |
86 | #define VC5_HDMI_MISC_CONTROL_PIXEL_REP_MASK VC4_MASK(3, 0) |
87 | |
88 | #define VC5_HDMI_SCRAMBLER_CTL_ENABLE BIT(0) |
89 | |
90 | #define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_SHIFT 8 |
91 | #define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK VC4_MASK(10, 8) |
92 | |
93 | #define VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_SHIFT 0 |
94 | #define VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_MASK VC4_MASK(3, 0) |
95 | |
96 | #define VC5_HDMI_GCP_CONFIG_GCP_ENABLE BIT(31) |
97 | |
98 | #define VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1_SHIFT 8 |
99 | #define VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1_MASK VC4_MASK(15, 8) |
100 | |
101 | #define VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_0_MASK VC4_MASK(7, 0) |
102 | #define VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_0_SET_AVMUTE BIT(0) |
103 | #define VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_0_CLEAR_AVMUTE BIT(4) |
104 | |
105 | # define VC4_HD_M_SW_RST BIT(2) |
106 | # define VC4_HD_M_ENABLE BIT(0) |
107 | |
108 | #define HSM_MIN_CLOCK_FREQ 120000000 |
109 | #define CEC_CLOCK_FREQ 40000 |
110 | |
111 | #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) |
112 | |
113 | static const char * const output_format_str[] = { |
114 | [VC4_HDMI_OUTPUT_RGB] = "RGB" , |
115 | [VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0" , |
116 | [VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2" , |
117 | [VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4" , |
118 | }; |
119 | |
120 | static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt) |
121 | { |
122 | if (fmt >= ARRAY_SIZE(output_format_str)) |
123 | return "invalid" ; |
124 | |
125 | return output_format_str[fmt]; |
126 | } |
127 | |
128 | static unsigned long long |
129 | vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, |
130 | unsigned int bpc, enum vc4_hdmi_output_format fmt); |
131 | |
132 | static bool vc4_hdmi_supports_scrambling(struct vc4_hdmi *vc4_hdmi) |
133 | { |
134 | struct drm_display_info *display = &vc4_hdmi->connector.display_info; |
135 | |
136 | lockdep_assert_held(&vc4_hdmi->mutex); |
137 | |
138 | if (!display->is_hdmi) |
139 | return false; |
140 | |
141 | if (!display->hdmi.scdc.supported || |
142 | !display->hdmi.scdc.scrambling.supported) |
143 | return false; |
144 | |
145 | return true; |
146 | } |
147 | |
148 | static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, |
149 | unsigned int bpc, |
150 | enum vc4_hdmi_output_format fmt) |
151 | { |
152 | unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); |
153 | |
154 | return clock > HDMI_14_MAX_TMDS_CLK; |
155 | } |
156 | |
157 | static bool vc4_hdmi_is_full_range(struct vc4_hdmi *vc4_hdmi, |
158 | struct vc4_hdmi_connector_state *vc4_state) |
159 | { |
160 | const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; |
161 | struct drm_display_info *display = &vc4_hdmi->connector.display_info; |
162 | |
163 | if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_LIMITED) |
164 | return false; |
165 | else if (vc4_state->broadcast_rgb == VC4_HDMI_BROADCAST_RGB_FULL) |
166 | return true; |
167 | |
168 | return !display->is_hdmi || |
169 | drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_FULL; |
170 | } |
171 | |
172 | static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) |
173 | { |
174 | struct drm_debugfs_entry *entry = m->private; |
175 | struct vc4_hdmi *vc4_hdmi = entry->file.data; |
176 | struct drm_device *drm = vc4_hdmi->connector.dev; |
177 | struct drm_printer p = drm_seq_file_printer(f: m); |
178 | int idx; |
179 | |
180 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
181 | return -ENODEV; |
182 | |
183 | drm_print_regset32(p: &p, regset: &vc4_hdmi->hdmi_regset); |
184 | drm_print_regset32(p: &p, regset: &vc4_hdmi->hd_regset); |
185 | drm_print_regset32(p: &p, regset: &vc4_hdmi->cec_regset); |
186 | drm_print_regset32(p: &p, regset: &vc4_hdmi->csc_regset); |
187 | drm_print_regset32(p: &p, regset: &vc4_hdmi->dvp_regset); |
188 | drm_print_regset32(p: &p, regset: &vc4_hdmi->phy_regset); |
189 | drm_print_regset32(p: &p, regset: &vc4_hdmi->ram_regset); |
190 | drm_print_regset32(p: &p, regset: &vc4_hdmi->rm_regset); |
191 | |
192 | drm_dev_exit(idx); |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi) |
198 | { |
199 | struct drm_device *drm = vc4_hdmi->connector.dev; |
200 | unsigned long flags; |
201 | int idx; |
202 | |
203 | /* |
204 | * We can be called by our bind callback, when the |
205 | * connector->dev pointer might not be initialised yet. |
206 | */ |
207 | if (drm && !drm_dev_enter(dev: drm, idx: &idx)) |
208 | return; |
209 | |
210 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
211 | |
212 | HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_SW_RST); |
213 | udelay(1); |
214 | HDMI_WRITE(HDMI_M_CTL, 0); |
215 | |
216 | HDMI_WRITE(HDMI_M_CTL, VC4_HD_M_ENABLE); |
217 | |
218 | HDMI_WRITE(HDMI_SW_RESET_CONTROL, |
219 | VC4_HDMI_SW_RESET_HDMI | |
220 | VC4_HDMI_SW_RESET_FORMAT_DETECT); |
221 | |
222 | HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0); |
223 | |
224 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
225 | |
226 | if (drm) |
227 | drm_dev_exit(idx); |
228 | } |
229 | |
230 | static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi) |
231 | { |
232 | struct drm_device *drm = vc4_hdmi->connector.dev; |
233 | unsigned long flags; |
234 | int idx; |
235 | |
236 | /* |
237 | * We can be called by our bind callback, when the |
238 | * connector->dev pointer might not be initialised yet. |
239 | */ |
240 | if (drm && !drm_dev_enter(dev: drm, idx: &idx)) |
241 | return; |
242 | |
243 | reset_control_reset(rstc: vc4_hdmi->reset); |
244 | |
245 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
246 | |
247 | HDMI_WRITE(HDMI_DVP_CTL, 0); |
248 | |
249 | HDMI_WRITE(HDMI_CLOCK_STOP, |
250 | HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL); |
251 | |
252 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
253 | |
254 | if (drm) |
255 | drm_dev_exit(idx); |
256 | } |
257 | |
258 | #ifdef CONFIG_DRM_VC4_HDMI_CEC |
259 | static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) |
260 | { |
261 | struct drm_device *drm = vc4_hdmi->connector.dev; |
262 | unsigned long cec_rate; |
263 | unsigned long flags; |
264 | u16 clk_cnt; |
265 | u32 value; |
266 | int idx; |
267 | |
268 | /* |
269 | * This function is called by our runtime_resume implementation |
270 | * and thus at bind time, when we haven't registered our |
271 | * connector yet and thus don't have a pointer to the DRM |
272 | * device. |
273 | */ |
274 | if (drm && !drm_dev_enter(dev: drm, idx: &idx)) |
275 | return; |
276 | |
277 | cec_rate = clk_get_rate(clk: vc4_hdmi->cec_clock); |
278 | |
279 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
280 | |
281 | value = HDMI_READ(HDMI_CEC_CNTRL_1); |
282 | value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK; |
283 | |
284 | /* |
285 | * Set the clock divider: the hsm_clock rate and this divider |
286 | * setting will give a 40 kHz CEC clock. |
287 | */ |
288 | clk_cnt = cec_rate / CEC_CLOCK_FREQ; |
289 | value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT; |
290 | HDMI_WRITE(HDMI_CEC_CNTRL_1, value); |
291 | |
292 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
293 | |
294 | if (drm) |
295 | drm_dev_exit(idx); |
296 | } |
297 | #else |
298 | static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {} |
299 | #endif |
300 | |
301 | static int reset_pipe(struct drm_crtc *crtc, |
302 | struct drm_modeset_acquire_ctx *ctx) |
303 | { |
304 | struct drm_atomic_state *state; |
305 | struct drm_crtc_state *crtc_state; |
306 | int ret; |
307 | |
308 | state = drm_atomic_state_alloc(dev: crtc->dev); |
309 | if (!state) |
310 | return -ENOMEM; |
311 | |
312 | state->acquire_ctx = ctx; |
313 | |
314 | crtc_state = drm_atomic_get_crtc_state(state, crtc); |
315 | if (IS_ERR(ptr: crtc_state)) { |
316 | ret = PTR_ERR(ptr: crtc_state); |
317 | goto out; |
318 | } |
319 | |
320 | crtc_state->connectors_changed = true; |
321 | |
322 | ret = drm_atomic_commit(state); |
323 | out: |
324 | drm_atomic_state_put(state); |
325 | |
326 | return ret; |
327 | } |
328 | |
329 | static int vc4_hdmi_reset_link(struct drm_connector *connector, |
330 | struct drm_modeset_acquire_ctx *ctx) |
331 | { |
332 | struct drm_device *drm; |
333 | struct vc4_hdmi *vc4_hdmi; |
334 | struct drm_connector_state *conn_state; |
335 | struct drm_crtc_state *crtc_state; |
336 | struct drm_crtc *crtc; |
337 | bool scrambling_needed; |
338 | u8 config; |
339 | int ret; |
340 | |
341 | if (!connector) |
342 | return 0; |
343 | |
344 | drm = connector->dev; |
345 | ret = drm_modeset_lock(lock: &drm->mode_config.connection_mutex, ctx); |
346 | if (ret) |
347 | return ret; |
348 | |
349 | conn_state = connector->state; |
350 | crtc = conn_state->crtc; |
351 | if (!crtc) |
352 | return 0; |
353 | |
354 | ret = drm_modeset_lock(lock: &crtc->mutex, ctx); |
355 | if (ret) |
356 | return ret; |
357 | |
358 | crtc_state = crtc->state; |
359 | if (!crtc_state->active) |
360 | return 0; |
361 | |
362 | vc4_hdmi = connector_to_vc4_hdmi(connector); |
363 | mutex_lock(&vc4_hdmi->mutex); |
364 | |
365 | if (!vc4_hdmi_supports_scrambling(vc4_hdmi)) { |
366 | mutex_unlock(lock: &vc4_hdmi->mutex); |
367 | return 0; |
368 | } |
369 | |
370 | scrambling_needed = vc4_hdmi_mode_needs_scrambling(mode: &vc4_hdmi->saved_adjusted_mode, |
371 | bpc: vc4_hdmi->output_bpc, |
372 | fmt: vc4_hdmi->output_format); |
373 | if (!scrambling_needed) { |
374 | mutex_unlock(lock: &vc4_hdmi->mutex); |
375 | return 0; |
376 | } |
377 | |
378 | if (conn_state->commit && |
379 | !try_wait_for_completion(x: &conn_state->commit->hw_done)) { |
380 | mutex_unlock(lock: &vc4_hdmi->mutex); |
381 | return 0; |
382 | } |
383 | |
384 | ret = drm_scdc_readb(adapter: connector->ddc, SCDC_TMDS_CONFIG, value: &config); |
385 | if (ret < 0) { |
386 | drm_err(drm, "Failed to read TMDS config: %d\n" , ret); |
387 | mutex_unlock(lock: &vc4_hdmi->mutex); |
388 | return 0; |
389 | } |
390 | |
391 | if (!!(config & SCDC_SCRAMBLING_ENABLE) == scrambling_needed) { |
392 | mutex_unlock(lock: &vc4_hdmi->mutex); |
393 | return 0; |
394 | } |
395 | |
396 | mutex_unlock(lock: &vc4_hdmi->mutex); |
397 | |
398 | /* |
399 | * HDMI 2.0 says that one should not send scrambled data |
400 | * prior to configuring the sink scrambling, and that |
401 | * TMDS clock/data transmission should be suspended when |
402 | * changing the TMDS clock rate in the sink. So let's |
403 | * just do a full modeset here, even though some sinks |
404 | * would be perfectly happy if were to just reconfigure |
405 | * the SCDC settings on the fly. |
406 | */ |
407 | return reset_pipe(crtc, ctx); |
408 | } |
409 | |
410 | static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi, |
411 | struct drm_modeset_acquire_ctx *ctx, |
412 | enum drm_connector_status status) |
413 | { |
414 | struct drm_connector *connector = &vc4_hdmi->connector; |
415 | struct edid *edid; |
416 | int ret; |
417 | |
418 | /* |
419 | * NOTE: This function should really be called with |
420 | * vc4_hdmi->mutex held, but doing so results in reentrancy |
421 | * issues since cec_s_phys_addr_from_edid might call |
422 | * .adap_enable, which leads to that funtion being called with |
423 | * our mutex held. |
424 | * |
425 | * A similar situation occurs with vc4_hdmi_reset_link() that |
426 | * will call into our KMS hooks if the scrambling was enabled. |
427 | * |
428 | * Concurrency isn't an issue at the moment since we don't share |
429 | * any state with any of the other frameworks so we can ignore |
430 | * the lock for now. |
431 | */ |
432 | |
433 | if (status == connector_status_disconnected) { |
434 | cec_phys_addr_invalidate(adap: vc4_hdmi->cec_adap); |
435 | return; |
436 | } |
437 | |
438 | edid = drm_get_edid(connector, adapter: vc4_hdmi->ddc); |
439 | if (!edid) |
440 | return; |
441 | |
442 | cec_s_phys_addr_from_edid(adap: vc4_hdmi->cec_adap, edid); |
443 | kfree(objp: edid); |
444 | |
445 | for (;;) { |
446 | ret = vc4_hdmi_reset_link(connector, ctx); |
447 | if (ret == -EDEADLK) { |
448 | drm_modeset_backoff(ctx); |
449 | continue; |
450 | } |
451 | |
452 | break; |
453 | } |
454 | } |
455 | |
456 | static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector, |
457 | struct drm_modeset_acquire_ctx *ctx, |
458 | bool force) |
459 | { |
460 | struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); |
461 | enum drm_connector_status status = connector_status_disconnected; |
462 | |
463 | /* |
464 | * NOTE: This function should really take vc4_hdmi->mutex, but |
465 | * doing so results in reentrancy issues since |
466 | * vc4_hdmi_handle_hotplug() can call into other functions that |
467 | * would take the mutex while it's held here. |
468 | * |
469 | * Concurrency isn't an issue at the moment since we don't share |
470 | * any state with any of the other frameworks so we can ignore |
471 | * the lock for now. |
472 | */ |
473 | |
474 | WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev)); |
475 | |
476 | if (vc4_hdmi->hpd_gpio) { |
477 | if (gpiod_get_value_cansleep(desc: vc4_hdmi->hpd_gpio)) |
478 | status = connector_status_connected; |
479 | } else { |
480 | if (vc4_hdmi->variant->hp_detect && |
481 | vc4_hdmi->variant->hp_detect(vc4_hdmi)) |
482 | status = connector_status_connected; |
483 | } |
484 | |
485 | vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status); |
486 | pm_runtime_put(dev: &vc4_hdmi->pdev->dev); |
487 | |
488 | return status; |
489 | } |
490 | |
491 | static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) |
492 | { |
493 | struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); |
494 | struct vc4_dev *vc4 = to_vc4_dev(connector->dev); |
495 | int ret = 0; |
496 | struct edid *edid; |
497 | |
498 | /* |
499 | * NOTE: This function should really take vc4_hdmi->mutex, but |
500 | * doing so results in reentrancy issues since |
501 | * cec_s_phys_addr_from_edid might call .adap_enable, which |
502 | * leads to that funtion being called with our mutex held. |
503 | * |
504 | * Concurrency isn't an issue at the moment since we don't share |
505 | * any state with any of the other frameworks so we can ignore |
506 | * the lock for now. |
507 | */ |
508 | |
509 | edid = drm_get_edid(connector, adapter: vc4_hdmi->ddc); |
510 | cec_s_phys_addr_from_edid(adap: vc4_hdmi->cec_adap, edid); |
511 | if (!edid) |
512 | return 0; |
513 | |
514 | drm_connector_update_edid_property(connector, edid); |
515 | ret = drm_add_edid_modes(connector, edid); |
516 | kfree(objp: edid); |
517 | |
518 | if (!vc4->hvs->vc5_hdmi_enable_hdmi_20) { |
519 | struct drm_device *drm = connector->dev; |
520 | const struct drm_display_mode *mode; |
521 | |
522 | list_for_each_entry(mode, &connector->probed_modes, head) { |
523 | if (vc4_hdmi_mode_needs_scrambling(mode, bpc: 8, fmt: VC4_HDMI_OUTPUT_RGB)) { |
524 | drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz." ); |
525 | drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60." ); |
526 | } |
527 | } |
528 | } |
529 | |
530 | return ret; |
531 | } |
532 | |
533 | static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, |
534 | struct drm_atomic_state *state) |
535 | { |
536 | struct drm_connector_state *old_state = |
537 | drm_atomic_get_old_connector_state(state, connector); |
538 | struct vc4_hdmi_connector_state *old_vc4_state = |
539 | conn_state_to_vc4_hdmi_conn_state(old_state); |
540 | struct drm_connector_state *new_state = |
541 | drm_atomic_get_new_connector_state(state, connector); |
542 | struct vc4_hdmi_connector_state *new_vc4_state = |
543 | conn_state_to_vc4_hdmi_conn_state(new_state); |
544 | struct drm_crtc *crtc = new_state->crtc; |
545 | |
546 | if (!crtc) |
547 | return 0; |
548 | |
549 | if (old_state->tv.margins.left != new_state->tv.margins.left || |
550 | old_state->tv.margins.right != new_state->tv.margins.right || |
551 | old_state->tv.margins.top != new_state->tv.margins.top || |
552 | old_state->tv.margins.bottom != new_state->tv.margins.bottom) { |
553 | struct drm_crtc_state *crtc_state; |
554 | int ret; |
555 | |
556 | crtc_state = drm_atomic_get_crtc_state(state, crtc); |
557 | if (IS_ERR(ptr: crtc_state)) |
558 | return PTR_ERR(ptr: crtc_state); |
559 | |
560 | /* |
561 | * Strictly speaking, we should be calling |
562 | * drm_atomic_helper_check_planes() after our call to |
563 | * drm_atomic_add_affected_planes(). However, the |
564 | * connector atomic_check is called as part of |
565 | * drm_atomic_helper_check_modeset() that already |
566 | * happens before a call to |
567 | * drm_atomic_helper_check_planes() in |
568 | * drm_atomic_helper_check(). |
569 | */ |
570 | ret = drm_atomic_add_affected_planes(state, crtc); |
571 | if (ret) |
572 | return ret; |
573 | } |
574 | |
575 | if (old_state->colorspace != new_state->colorspace || |
576 | old_vc4_state->broadcast_rgb != new_vc4_state->broadcast_rgb || |
577 | !drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) { |
578 | struct drm_crtc_state *crtc_state; |
579 | |
580 | crtc_state = drm_atomic_get_crtc_state(state, crtc); |
581 | if (IS_ERR(ptr: crtc_state)) |
582 | return PTR_ERR(ptr: crtc_state); |
583 | |
584 | crtc_state->mode_changed = true; |
585 | } |
586 | |
587 | return 0; |
588 | } |
589 | |
590 | static int vc4_hdmi_connector_get_property(struct drm_connector *connector, |
591 | const struct drm_connector_state *state, |
592 | struct drm_property *property, |
593 | uint64_t *val) |
594 | { |
595 | struct drm_device *drm = connector->dev; |
596 | struct vc4_hdmi *vc4_hdmi = |
597 | connector_to_vc4_hdmi(connector); |
598 | const struct vc4_hdmi_connector_state *vc4_conn_state = |
599 | conn_state_to_vc4_hdmi_conn_state(state); |
600 | |
601 | if (property == vc4_hdmi->broadcast_rgb_property) { |
602 | *val = vc4_conn_state->broadcast_rgb; |
603 | } else { |
604 | drm_dbg(drm, "Unknown property [PROP:%d:%s]\n" , |
605 | property->base.id, property->name); |
606 | return -EINVAL; |
607 | } |
608 | |
609 | return 0; |
610 | } |
611 | |
612 | static int vc4_hdmi_connector_set_property(struct drm_connector *connector, |
613 | struct drm_connector_state *state, |
614 | struct drm_property *property, |
615 | uint64_t val) |
616 | { |
617 | struct drm_device *drm = connector->dev; |
618 | struct vc4_hdmi *vc4_hdmi = |
619 | connector_to_vc4_hdmi(connector); |
620 | struct vc4_hdmi_connector_state *vc4_conn_state = |
621 | conn_state_to_vc4_hdmi_conn_state(state); |
622 | |
623 | if (property == vc4_hdmi->broadcast_rgb_property) { |
624 | vc4_conn_state->broadcast_rgb = val; |
625 | return 0; |
626 | } |
627 | |
628 | drm_dbg(drm, "Unknown property [PROP:%d:%s]\n" , |
629 | property->base.id, property->name); |
630 | return -EINVAL; |
631 | } |
632 | |
633 | static void vc4_hdmi_connector_reset(struct drm_connector *connector) |
634 | { |
635 | struct vc4_hdmi_connector_state *old_state = |
636 | conn_state_to_vc4_hdmi_conn_state(connector->state); |
637 | struct vc4_hdmi_connector_state *new_state = |
638 | kzalloc(size: sizeof(*new_state), GFP_KERNEL); |
639 | |
640 | if (connector->state) |
641 | __drm_atomic_helper_connector_destroy_state(state: connector->state); |
642 | |
643 | kfree(objp: old_state); |
644 | __drm_atomic_helper_connector_reset(connector, conn_state: &new_state->base); |
645 | |
646 | if (!new_state) |
647 | return; |
648 | |
649 | new_state->base.max_bpc = 8; |
650 | new_state->base.max_requested_bpc = 8; |
651 | new_state->output_format = VC4_HDMI_OUTPUT_RGB; |
652 | new_state->broadcast_rgb = VC4_HDMI_BROADCAST_RGB_AUTO; |
653 | drm_atomic_helper_connector_tv_margins_reset(connector); |
654 | } |
655 | |
656 | static struct drm_connector_state * |
657 | vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) |
658 | { |
659 | struct drm_connector_state *conn_state = connector->state; |
660 | struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state); |
661 | struct vc4_hdmi_connector_state *new_state; |
662 | |
663 | new_state = kzalloc(size: sizeof(*new_state), GFP_KERNEL); |
664 | if (!new_state) |
665 | return NULL; |
666 | |
667 | new_state->tmds_char_rate = vc4_state->tmds_char_rate; |
668 | new_state->output_bpc = vc4_state->output_bpc; |
669 | new_state->output_format = vc4_state->output_format; |
670 | new_state->broadcast_rgb = vc4_state->broadcast_rgb; |
671 | __drm_atomic_helper_connector_duplicate_state(connector, state: &new_state->base); |
672 | |
673 | return &new_state->base; |
674 | } |
675 | |
676 | static void vc4_hdmi_connector_destroy_state(struct drm_connector *connector, |
677 | struct drm_connector_state *state) |
678 | { |
679 | struct vc4_hdmi_connector_state *vc4_state = |
680 | conn_state_to_vc4_hdmi_conn_state(state); |
681 | |
682 | __drm_atomic_helper_connector_destroy_state(state); |
683 | kfree(objp: vc4_state); |
684 | } |
685 | |
686 | static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { |
687 | .fill_modes = drm_helper_probe_single_connector_modes, |
688 | .reset = vc4_hdmi_connector_reset, |
689 | .atomic_duplicate_state = vc4_hdmi_connector_duplicate_state, |
690 | .atomic_destroy_state = vc4_hdmi_connector_destroy_state, |
691 | .atomic_get_property = vc4_hdmi_connector_get_property, |
692 | .atomic_set_property = vc4_hdmi_connector_set_property, |
693 | }; |
694 | |
695 | static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = { |
696 | .detect_ctx = vc4_hdmi_connector_detect_ctx, |
697 | .get_modes = vc4_hdmi_connector_get_modes, |
698 | .atomic_check = vc4_hdmi_connector_atomic_check, |
699 | }; |
700 | |
701 | static const struct drm_prop_enum_list broadcast_rgb_names[] = { |
702 | { VC4_HDMI_BROADCAST_RGB_AUTO, "Automatic" }, |
703 | { VC4_HDMI_BROADCAST_RGB_FULL, "Full" }, |
704 | { VC4_HDMI_BROADCAST_RGB_LIMITED, "Limited 16:235" }, |
705 | }; |
706 | |
707 | static void |
708 | vc4_hdmi_attach_broadcast_rgb_property(struct drm_device *dev, |
709 | struct vc4_hdmi *vc4_hdmi) |
710 | { |
711 | struct drm_property *prop = vc4_hdmi->broadcast_rgb_property; |
712 | |
713 | if (!prop) { |
714 | prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, |
715 | name: "Broadcast RGB" , |
716 | props: broadcast_rgb_names, |
717 | ARRAY_SIZE(broadcast_rgb_names)); |
718 | if (!prop) |
719 | return; |
720 | |
721 | vc4_hdmi->broadcast_rgb_property = prop; |
722 | } |
723 | |
724 | drm_object_attach_property(obj: &vc4_hdmi->connector.base, property: prop, |
725 | init_val: VC4_HDMI_BROADCAST_RGB_AUTO); |
726 | } |
727 | |
728 | static int vc4_hdmi_connector_init(struct drm_device *dev, |
729 | struct vc4_hdmi *vc4_hdmi) |
730 | { |
731 | struct drm_connector *connector = &vc4_hdmi->connector; |
732 | struct drm_encoder *encoder = &vc4_hdmi->encoder.base; |
733 | int ret; |
734 | |
735 | ret = drmm_connector_init(dev, connector, |
736 | funcs: &vc4_hdmi_connector_funcs, |
737 | DRM_MODE_CONNECTOR_HDMIA, |
738 | ddc: vc4_hdmi->ddc); |
739 | if (ret) |
740 | return ret; |
741 | |
742 | drm_connector_helper_add(connector, funcs: &vc4_hdmi_connector_helper_funcs); |
743 | |
744 | /* |
745 | * Some of the properties below require access to state, like bpc. |
746 | * Allocate some default initial connector state with our reset helper. |
747 | */ |
748 | if (connector->funcs->reset) |
749 | connector->funcs->reset(connector); |
750 | |
751 | /* Create and attach TV margin props to this connector. */ |
752 | ret = drm_mode_create_tv_margin_properties(dev); |
753 | if (ret) |
754 | return ret; |
755 | |
756 | ret = drm_mode_create_hdmi_colorspace_property(connector, supported_colorspaces: 0); |
757 | if (ret) |
758 | return ret; |
759 | |
760 | drm_connector_attach_colorspace_property(connector); |
761 | drm_connector_attach_tv_margin_properties(conn: connector); |
762 | drm_connector_attach_max_bpc_property(connector, min: 8, max: 12); |
763 | |
764 | connector->polled = (DRM_CONNECTOR_POLL_CONNECT | |
765 | DRM_CONNECTOR_POLL_DISCONNECT); |
766 | |
767 | connector->interlace_allowed = 1; |
768 | connector->doublescan_allowed = 0; |
769 | connector->stereo_allowed = 1; |
770 | |
771 | if (vc4_hdmi->variant->supports_hdr) |
772 | drm_connector_attach_hdr_output_metadata_property(connector); |
773 | |
774 | vc4_hdmi_attach_broadcast_rgb_property(dev, vc4_hdmi); |
775 | |
776 | drm_connector_attach_encoder(connector, encoder); |
777 | |
778 | return 0; |
779 | } |
780 | |
781 | static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, |
782 | enum hdmi_infoframe_type type, |
783 | bool poll) |
784 | { |
785 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
786 | struct drm_device *drm = vc4_hdmi->connector.dev; |
787 | u32 packet_id = type - 0x80; |
788 | unsigned long flags; |
789 | int ret = 0; |
790 | int idx; |
791 | |
792 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
793 | return -ENODEV; |
794 | |
795 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
796 | HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, |
797 | HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id)); |
798 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
799 | |
800 | if (poll) { |
801 | ret = wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) & |
802 | BIT(packet_id)), 100); |
803 | } |
804 | |
805 | drm_dev_exit(idx); |
806 | return ret; |
807 | } |
808 | |
809 | static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, |
810 | union hdmi_infoframe *frame) |
811 | { |
812 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
813 | struct drm_device *drm = vc4_hdmi->connector.dev; |
814 | u32 packet_id = frame->any.type - 0x80; |
815 | const struct vc4_hdmi_register *ram_packet_start = |
816 | &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START]; |
817 | u32 packet_reg = ram_packet_start->offset + VC4_HDMI_PACKET_STRIDE * packet_id; |
818 | u32 packet_reg_next = ram_packet_start->offset + |
819 | VC4_HDMI_PACKET_STRIDE * (packet_id + 1); |
820 | void __iomem *base = __vc4_hdmi_get_field_base(hdmi: vc4_hdmi, |
821 | reg: ram_packet_start->reg); |
822 | uint8_t buffer[VC4_HDMI_PACKET_STRIDE] = {}; |
823 | unsigned long flags; |
824 | ssize_t len, i; |
825 | int ret; |
826 | int idx; |
827 | |
828 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
829 | return; |
830 | |
831 | WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & |
832 | VC4_HDMI_RAM_PACKET_ENABLE), |
833 | "Packet RAM has to be on to store the packet." ); |
834 | |
835 | len = hdmi_infoframe_pack(frame, buffer, size: sizeof(buffer)); |
836 | if (len < 0) |
837 | goto out; |
838 | |
839 | ret = vc4_hdmi_stop_packet(encoder, type: frame->any.type, poll: true); |
840 | if (ret) { |
841 | DRM_ERROR("Failed to wait for infoframe to go idle: %d\n" , ret); |
842 | goto out; |
843 | } |
844 | |
845 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
846 | |
847 | for (i = 0; i < len; i += 7) { |
848 | writel(val: buffer[i + 0] << 0 | |
849 | buffer[i + 1] << 8 | |
850 | buffer[i + 2] << 16, |
851 | addr: base + packet_reg); |
852 | packet_reg += 4; |
853 | |
854 | writel(val: buffer[i + 3] << 0 | |
855 | buffer[i + 4] << 8 | |
856 | buffer[i + 5] << 16 | |
857 | buffer[i + 6] << 24, |
858 | addr: base + packet_reg); |
859 | packet_reg += 4; |
860 | } |
861 | |
862 | /* |
863 | * clear remainder of packet ram as it's included in the |
864 | * infoframe and triggers a checksum error on hdmi analyser |
865 | */ |
866 | for (; packet_reg < packet_reg_next; packet_reg += 4) |
867 | writel(val: 0, addr: base + packet_reg); |
868 | |
869 | HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, |
870 | HDMI_READ(HDMI_RAM_PACKET_CONFIG) | BIT(packet_id)); |
871 | |
872 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
873 | |
874 | ret = wait_for((HDMI_READ(HDMI_RAM_PACKET_STATUS) & |
875 | BIT(packet_id)), 100); |
876 | if (ret) |
877 | DRM_ERROR("Failed to wait for infoframe to start: %d\n" , ret); |
878 | |
879 | out: |
880 | drm_dev_exit(idx); |
881 | } |
882 | |
883 | static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, |
884 | enum vc4_hdmi_output_format fmt) |
885 | { |
886 | switch (fmt) { |
887 | case VC4_HDMI_OUTPUT_RGB: |
888 | frame->colorspace = HDMI_COLORSPACE_RGB; |
889 | break; |
890 | |
891 | case VC4_HDMI_OUTPUT_YUV420: |
892 | frame->colorspace = HDMI_COLORSPACE_YUV420; |
893 | break; |
894 | |
895 | case VC4_HDMI_OUTPUT_YUV422: |
896 | frame->colorspace = HDMI_COLORSPACE_YUV422; |
897 | break; |
898 | |
899 | case VC4_HDMI_OUTPUT_YUV444: |
900 | frame->colorspace = HDMI_COLORSPACE_YUV444; |
901 | break; |
902 | |
903 | default: |
904 | break; |
905 | } |
906 | } |
907 | |
908 | static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) |
909 | { |
910 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
911 | struct drm_connector *connector = &vc4_hdmi->connector; |
912 | struct drm_connector_state *cstate = connector->state; |
913 | struct vc4_hdmi_connector_state *vc4_state = |
914 | conn_state_to_vc4_hdmi_conn_state(cstate); |
915 | const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; |
916 | union hdmi_infoframe frame; |
917 | int ret; |
918 | |
919 | lockdep_assert_held(&vc4_hdmi->mutex); |
920 | |
921 | ret = drm_hdmi_avi_infoframe_from_display_mode(frame: &frame.avi, |
922 | connector, mode); |
923 | if (ret < 0) { |
924 | DRM_ERROR("couldn't fill AVI infoframe\n" ); |
925 | return; |
926 | } |
927 | |
928 | drm_hdmi_avi_infoframe_quant_range(frame: &frame.avi, |
929 | connector, mode, |
930 | rgb_quant_range: vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ? |
931 | HDMI_QUANTIZATION_RANGE_FULL : |
932 | HDMI_QUANTIZATION_RANGE_LIMITED); |
933 | drm_hdmi_avi_infoframe_colorimetry(frame: &frame.avi, conn_state: cstate); |
934 | vc4_hdmi_avi_infoframe_colorspace(frame: &frame.avi, fmt: vc4_state->output_format); |
935 | drm_hdmi_avi_infoframe_bars(frame: &frame.avi, conn_state: cstate); |
936 | |
937 | vc4_hdmi_write_infoframe(encoder, frame: &frame); |
938 | } |
939 | |
940 | static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) |
941 | { |
942 | union hdmi_infoframe frame; |
943 | int ret; |
944 | |
945 | ret = hdmi_spd_infoframe_init(frame: &frame.spd, vendor: "Broadcom" , product: "Videocore" ); |
946 | if (ret < 0) { |
947 | DRM_ERROR("couldn't fill SPD infoframe\n" ); |
948 | return; |
949 | } |
950 | |
951 | frame.spd.sdi = HDMI_SPD_SDI_PC; |
952 | |
953 | vc4_hdmi_write_infoframe(encoder, frame: &frame); |
954 | } |
955 | |
956 | static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) |
957 | { |
958 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
959 | struct hdmi_audio_infoframe *audio = &vc4_hdmi->audio.infoframe; |
960 | union hdmi_infoframe frame; |
961 | |
962 | memcpy(&frame.audio, audio, sizeof(*audio)); |
963 | |
964 | if (vc4_hdmi->packet_ram_enabled) |
965 | vc4_hdmi_write_infoframe(encoder, frame: &frame); |
966 | } |
967 | |
968 | static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder) |
969 | { |
970 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
971 | struct drm_connector *connector = &vc4_hdmi->connector; |
972 | struct drm_connector_state *conn_state = connector->state; |
973 | union hdmi_infoframe frame; |
974 | |
975 | lockdep_assert_held(&vc4_hdmi->mutex); |
976 | |
977 | if (!vc4_hdmi->variant->supports_hdr) |
978 | return; |
979 | |
980 | if (!conn_state->hdr_output_metadata) |
981 | return; |
982 | |
983 | if (drm_hdmi_infoframe_set_hdr_metadata(frame: &frame.drm, conn_state)) |
984 | return; |
985 | |
986 | vc4_hdmi_write_infoframe(encoder, frame: &frame); |
987 | } |
988 | |
989 | static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) |
990 | { |
991 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
992 | |
993 | lockdep_assert_held(&vc4_hdmi->mutex); |
994 | |
995 | vc4_hdmi_set_avi_infoframe(encoder); |
996 | vc4_hdmi_set_spd_infoframe(encoder); |
997 | /* |
998 | * If audio was streaming, then we need to reenabled the audio |
999 | * infoframe here during encoder_enable. |
1000 | */ |
1001 | if (vc4_hdmi->audio.streaming) |
1002 | vc4_hdmi_set_audio_infoframe(encoder); |
1003 | |
1004 | vc4_hdmi_set_hdr_infoframe(encoder); |
1005 | } |
1006 | |
1007 | #define SCRAMBLING_POLLING_DELAY_MS 1000 |
1008 | |
1009 | static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) |
1010 | { |
1011 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1012 | struct drm_connector *connector = &vc4_hdmi->connector; |
1013 | struct drm_device *drm = connector->dev; |
1014 | const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; |
1015 | unsigned long flags; |
1016 | int idx; |
1017 | |
1018 | lockdep_assert_held(&vc4_hdmi->mutex); |
1019 | |
1020 | if (!vc4_hdmi_supports_scrambling(vc4_hdmi)) |
1021 | return; |
1022 | |
1023 | if (!vc4_hdmi_mode_needs_scrambling(mode, |
1024 | bpc: vc4_hdmi->output_bpc, |
1025 | fmt: vc4_hdmi->output_format)) |
1026 | return; |
1027 | |
1028 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1029 | return; |
1030 | |
1031 | drm_scdc_set_high_tmds_clock_ratio(connector, set: true); |
1032 | drm_scdc_set_scrambling(connector, enable: true); |
1033 | |
1034 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1035 | HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) | |
1036 | VC5_HDMI_SCRAMBLER_CTL_ENABLE); |
1037 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1038 | |
1039 | drm_dev_exit(idx); |
1040 | |
1041 | vc4_hdmi->scdc_enabled = true; |
1042 | |
1043 | queue_delayed_work(wq: system_wq, dwork: &vc4_hdmi->scrambling_work, |
1044 | delay: msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS)); |
1045 | } |
1046 | |
1047 | static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder) |
1048 | { |
1049 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1050 | struct drm_connector *connector = &vc4_hdmi->connector; |
1051 | struct drm_device *drm = connector->dev; |
1052 | unsigned long flags; |
1053 | int idx; |
1054 | |
1055 | lockdep_assert_held(&vc4_hdmi->mutex); |
1056 | |
1057 | if (!vc4_hdmi->scdc_enabled) |
1058 | return; |
1059 | |
1060 | vc4_hdmi->scdc_enabled = false; |
1061 | |
1062 | if (delayed_work_pending(&vc4_hdmi->scrambling_work)) |
1063 | cancel_delayed_work_sync(dwork: &vc4_hdmi->scrambling_work); |
1064 | |
1065 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1066 | return; |
1067 | |
1068 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1069 | HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) & |
1070 | ~VC5_HDMI_SCRAMBLER_CTL_ENABLE); |
1071 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1072 | |
1073 | drm_scdc_set_scrambling(connector, enable: false); |
1074 | drm_scdc_set_high_tmds_clock_ratio(connector, set: false); |
1075 | |
1076 | drm_dev_exit(idx); |
1077 | } |
1078 | |
1079 | static void vc4_hdmi_scrambling_wq(struct work_struct *work) |
1080 | { |
1081 | struct vc4_hdmi *vc4_hdmi = container_of(to_delayed_work(work), |
1082 | struct vc4_hdmi, |
1083 | scrambling_work); |
1084 | struct drm_connector *connector = &vc4_hdmi->connector; |
1085 | |
1086 | if (drm_scdc_get_scrambling_status(connector)) |
1087 | return; |
1088 | |
1089 | drm_scdc_set_high_tmds_clock_ratio(connector, set: true); |
1090 | drm_scdc_set_scrambling(connector, enable: true); |
1091 | |
1092 | queue_delayed_work(wq: system_wq, dwork: &vc4_hdmi->scrambling_work, |
1093 | delay: msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS)); |
1094 | } |
1095 | |
1096 | static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, |
1097 | struct drm_atomic_state *state) |
1098 | { |
1099 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1100 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1101 | unsigned long flags; |
1102 | int idx; |
1103 | |
1104 | mutex_lock(&vc4_hdmi->mutex); |
1105 | |
1106 | vc4_hdmi->packet_ram_enabled = false; |
1107 | |
1108 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1109 | goto out; |
1110 | |
1111 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1112 | |
1113 | HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); |
1114 | |
1115 | HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB); |
1116 | |
1117 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1118 | |
1119 | mdelay(1); |
1120 | |
1121 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1122 | HDMI_WRITE(HDMI_VID_CTL, |
1123 | HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); |
1124 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1125 | |
1126 | vc4_hdmi_disable_scrambling(encoder); |
1127 | |
1128 | drm_dev_exit(idx); |
1129 | |
1130 | out: |
1131 | mutex_unlock(lock: &vc4_hdmi->mutex); |
1132 | } |
1133 | |
1134 | static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, |
1135 | struct drm_atomic_state *state) |
1136 | { |
1137 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1138 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1139 | unsigned long flags; |
1140 | int ret; |
1141 | int idx; |
1142 | |
1143 | mutex_lock(&vc4_hdmi->mutex); |
1144 | |
1145 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1146 | goto out; |
1147 | |
1148 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1149 | HDMI_WRITE(HDMI_VID_CTL, |
1150 | HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX); |
1151 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1152 | |
1153 | if (vc4_hdmi->variant->phy_disable) |
1154 | vc4_hdmi->variant->phy_disable(vc4_hdmi); |
1155 | |
1156 | clk_disable_unprepare(clk: vc4_hdmi->pixel_bvb_clock); |
1157 | clk_disable_unprepare(clk: vc4_hdmi->pixel_clock); |
1158 | |
1159 | ret = pm_runtime_put(dev: &vc4_hdmi->pdev->dev); |
1160 | if (ret < 0) |
1161 | DRM_ERROR("Failed to release power domain: %d\n" , ret); |
1162 | |
1163 | drm_dev_exit(idx); |
1164 | |
1165 | out: |
1166 | mutex_unlock(lock: &vc4_hdmi->mutex); |
1167 | } |
1168 | |
1169 | static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, |
1170 | struct drm_connector_state *state, |
1171 | const struct drm_display_mode *mode) |
1172 | { |
1173 | struct vc4_hdmi_connector_state *vc4_state = |
1174 | conn_state_to_vc4_hdmi_conn_state(state); |
1175 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1176 | unsigned long flags; |
1177 | u32 csc_ctl; |
1178 | int idx; |
1179 | |
1180 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1181 | return; |
1182 | |
1183 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1184 | |
1185 | csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, |
1186 | VC4_HD_CSC_CTL_ORDER); |
1187 | |
1188 | if (!vc4_hdmi_is_full_range(vc4_hdmi, vc4_state)) { |
1189 | /* CEA VICs other than #1 requre limited range RGB |
1190 | * output unless overridden by an AVI infoframe. |
1191 | * Apply a colorspace conversion to squash 0-255 down |
1192 | * to 16-235. The matrix here is: |
1193 | * |
1194 | * [ 0 0 0.8594 16] |
1195 | * [ 0 0.8594 0 16] |
1196 | * [ 0.8594 0 0 16] |
1197 | * [ 0 0 0 1] |
1198 | */ |
1199 | csc_ctl |= VC4_HD_CSC_CTL_ENABLE; |
1200 | csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC; |
1201 | csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, |
1202 | VC4_HD_CSC_CTL_MODE); |
1203 | |
1204 | HDMI_WRITE(HDMI_CSC_12_11, (0x000 << 16) | 0x000); |
1205 | HDMI_WRITE(HDMI_CSC_14_13, (0x100 << 16) | 0x6e0); |
1206 | HDMI_WRITE(HDMI_CSC_22_21, (0x6e0 << 16) | 0x000); |
1207 | HDMI_WRITE(HDMI_CSC_24_23, (0x100 << 16) | 0x000); |
1208 | HDMI_WRITE(HDMI_CSC_32_31, (0x000 << 16) | 0x6e0); |
1209 | HDMI_WRITE(HDMI_CSC_34_33, (0x100 << 16) | 0x000); |
1210 | } |
1211 | |
1212 | /* The RGB order applies even when CSC is disabled. */ |
1213 | HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); |
1214 | |
1215 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1216 | |
1217 | drm_dev_exit(idx); |
1218 | } |
1219 | |
1220 | /* |
1221 | * Matrices for (internal) RGB to RGB output. |
1222 | * |
1223 | * Matrices are signed 2p13 fixed point, with signed 9p6 offsets |
1224 | */ |
1225 | static const u16 vc5_hdmi_csc_full_rgb_to_rgb[2][3][4] = { |
1226 | { |
1227 | /* |
1228 | * Full range - unity |
1229 | * |
1230 | * [ 1 0 0 0] |
1231 | * [ 0 1 0 0] |
1232 | * [ 0 0 1 0] |
1233 | */ |
1234 | { 0x2000, 0x0000, 0x0000, 0x0000 }, |
1235 | { 0x0000, 0x2000, 0x0000, 0x0000 }, |
1236 | { 0x0000, 0x0000, 0x2000, 0x0000 }, |
1237 | }, |
1238 | { |
1239 | /* |
1240 | * Limited range |
1241 | * |
1242 | * CEA VICs other than #1 require limited range RGB |
1243 | * output unless overridden by an AVI infoframe. Apply a |
1244 | * colorspace conversion to squash 0-255 down to 16-235. |
1245 | * The matrix here is: |
1246 | * |
1247 | * [ 0.8594 0 0 16] |
1248 | * [ 0 0.8594 0 16] |
1249 | * [ 0 0 0.8594 16] |
1250 | */ |
1251 | { 0x1b80, 0x0000, 0x0000, 0x0400 }, |
1252 | { 0x0000, 0x1b80, 0x0000, 0x0400 }, |
1253 | { 0x0000, 0x0000, 0x1b80, 0x0400 }, |
1254 | }, |
1255 | }; |
1256 | |
1257 | /* |
1258 | * Conversion between Full Range RGB and YUV using the BT.601 Colorspace |
1259 | * |
1260 | * Matrices are signed 2p13 fixed point, with signed 9p6 offsets |
1261 | */ |
1262 | static const u16 vc5_hdmi_csc_full_rgb_to_yuv_bt601[2][3][4] = { |
1263 | { |
1264 | /* |
1265 | * Full Range |
1266 | * |
1267 | * [ 0.299000 0.587000 0.114000 0 ] |
1268 | * [ -0.168736 -0.331264 0.500000 128 ] |
1269 | * [ 0.500000 -0.418688 -0.081312 128 ] |
1270 | */ |
1271 | { 0x0991, 0x12c9, 0x03a6, 0x0000 }, |
1272 | { 0xfa9b, 0xf567, 0x1000, 0x2000 }, |
1273 | { 0x1000, 0xf29b, 0xfd67, 0x2000 }, |
1274 | }, |
1275 | { |
1276 | /* Limited Range |
1277 | * |
1278 | * [ 0.255785 0.502160 0.097523 16 ] |
1279 | * [ -0.147644 -0.289856 0.437500 128 ] |
1280 | * [ 0.437500 -0.366352 -0.071148 128 ] |
1281 | */ |
1282 | { 0x082f, 0x1012, 0x031f, 0x0400 }, |
1283 | { 0xfb48, 0xf6ba, 0x0e00, 0x2000 }, |
1284 | { 0x0e00, 0xf448, 0xfdba, 0x2000 }, |
1285 | }, |
1286 | }; |
1287 | |
1288 | /* |
1289 | * Conversion between Full Range RGB and YUV using the BT.709 Colorspace |
1290 | * |
1291 | * Matrices are signed 2p13 fixed point, with signed 9p6 offsets |
1292 | */ |
1293 | static const u16 vc5_hdmi_csc_full_rgb_to_yuv_bt709[2][3][4] = { |
1294 | { |
1295 | /* |
1296 | * Full Range |
1297 | * |
1298 | * [ 0.212600 0.715200 0.072200 0 ] |
1299 | * [ -0.114572 -0.385428 0.500000 128 ] |
1300 | * [ 0.500000 -0.454153 -0.045847 128 ] |
1301 | */ |
1302 | { 0x06ce, 0x16e3, 0x024f, 0x0000 }, |
1303 | { 0xfc56, 0xf3ac, 0x1000, 0x2000 }, |
1304 | { 0x1000, 0xf179, 0xfe89, 0x2000 }, |
1305 | }, |
1306 | { |
1307 | /* |
1308 | * Limited Range |
1309 | * |
1310 | * [ 0.181906 0.611804 0.061758 16 ] |
1311 | * [ -0.100268 -0.337232 0.437500 128 ] |
1312 | * [ 0.437500 -0.397386 -0.040114 128 ] |
1313 | */ |
1314 | { 0x05d2, 0x1394, 0x01fa, 0x0400 }, |
1315 | { 0xfccc, 0xf536, 0x0e00, 0x2000 }, |
1316 | { 0x0e00, 0xf34a, 0xfeb8, 0x2000 }, |
1317 | }, |
1318 | }; |
1319 | |
1320 | /* |
1321 | * Conversion between Full Range RGB and YUV using the BT.2020 Colorspace |
1322 | * |
1323 | * Matrices are signed 2p13 fixed point, with signed 9p6 offsets |
1324 | */ |
1325 | static const u16 vc5_hdmi_csc_full_rgb_to_yuv_bt2020[2][3][4] = { |
1326 | { |
1327 | /* |
1328 | * Full Range |
1329 | * |
1330 | * [ 0.262700 0.678000 0.059300 0 ] |
1331 | * [ -0.139630 -0.360370 0.500000 128 ] |
1332 | * [ 0.500000 -0.459786 -0.040214 128 ] |
1333 | */ |
1334 | { 0x0868, 0x15b2, 0x01e6, 0x0000 }, |
1335 | { 0xfb89, 0xf479, 0x1000, 0x2000 }, |
1336 | { 0x1000, 0xf14a, 0xfeb8, 0x2000 }, |
1337 | }, |
1338 | { |
1339 | /* Limited Range |
1340 | * |
1341 | * [ 0.224732 0.580008 0.050729 16 ] |
1342 | * [ -0.122176 -0.315324 0.437500 128 ] |
1343 | * [ 0.437500 -0.402312 -0.035188 128 ] |
1344 | */ |
1345 | { 0x082f, 0x1012, 0x031f, 0x0400 }, |
1346 | { 0xfb48, 0xf6ba, 0x0e00, 0x2000 }, |
1347 | { 0x0e00, 0xf448, 0xfdba, 0x2000 }, |
1348 | }, |
1349 | }; |
1350 | |
1351 | static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi, |
1352 | const u16 coeffs[3][4]) |
1353 | { |
1354 | lockdep_assert_held(&vc4_hdmi->hw_lock); |
1355 | |
1356 | HDMI_WRITE(HDMI_CSC_12_11, (coeffs[0][1] << 16) | coeffs[0][0]); |
1357 | HDMI_WRITE(HDMI_CSC_14_13, (coeffs[0][3] << 16) | coeffs[0][2]); |
1358 | HDMI_WRITE(HDMI_CSC_22_21, (coeffs[1][1] << 16) | coeffs[1][0]); |
1359 | HDMI_WRITE(HDMI_CSC_24_23, (coeffs[1][3] << 16) | coeffs[1][2]); |
1360 | HDMI_WRITE(HDMI_CSC_32_31, (coeffs[2][1] << 16) | coeffs[2][0]); |
1361 | HDMI_WRITE(HDMI_CSC_34_33, (coeffs[2][3] << 16) | coeffs[2][2]); |
1362 | } |
1363 | |
1364 | static void vc5_hdmi_set_csc_coeffs_swap(struct vc4_hdmi *vc4_hdmi, |
1365 | const u16 coeffs[3][4]) |
1366 | { |
1367 | lockdep_assert_held(&vc4_hdmi->hw_lock); |
1368 | |
1369 | /* YUV444 needs the CSC matrices using the channels in a different order */ |
1370 | HDMI_WRITE(HDMI_CSC_12_11, (coeffs[1][1] << 16) | coeffs[1][0]); |
1371 | HDMI_WRITE(HDMI_CSC_14_13, (coeffs[1][3] << 16) | coeffs[1][2]); |
1372 | HDMI_WRITE(HDMI_CSC_22_21, (coeffs[2][1] << 16) | coeffs[2][0]); |
1373 | HDMI_WRITE(HDMI_CSC_24_23, (coeffs[2][3] << 16) | coeffs[2][2]); |
1374 | HDMI_WRITE(HDMI_CSC_32_31, (coeffs[0][1] << 16) | coeffs[0][0]); |
1375 | HDMI_WRITE(HDMI_CSC_34_33, (coeffs[0][3] << 16) | coeffs[0][2]); |
1376 | } |
1377 | |
1378 | static const u16 |
1379 | (*vc5_hdmi_find_yuv_csc_coeffs(struct vc4_hdmi *vc4_hdmi, u32 colorspace, bool limited))[4] |
1380 | { |
1381 | switch (colorspace) { |
1382 | case DRM_MODE_COLORIMETRY_SMPTE_170M_YCC: |
1383 | case DRM_MODE_COLORIMETRY_XVYCC_601: |
1384 | case DRM_MODE_COLORIMETRY_SYCC_601: |
1385 | case DRM_MODE_COLORIMETRY_OPYCC_601: |
1386 | case DRM_MODE_COLORIMETRY_BT601_YCC: |
1387 | return vc5_hdmi_csc_full_rgb_to_yuv_bt601[limited]; |
1388 | |
1389 | default: |
1390 | case DRM_MODE_COLORIMETRY_NO_DATA: |
1391 | case DRM_MODE_COLORIMETRY_BT709_YCC: |
1392 | case DRM_MODE_COLORIMETRY_XVYCC_709: |
1393 | case DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED: |
1394 | case DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT: |
1395 | return vc5_hdmi_csc_full_rgb_to_yuv_bt709[limited]; |
1396 | |
1397 | case DRM_MODE_COLORIMETRY_BT2020_CYCC: |
1398 | case DRM_MODE_COLORIMETRY_BT2020_YCC: |
1399 | case DRM_MODE_COLORIMETRY_BT2020_RGB: |
1400 | case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65: |
1401 | case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER: |
1402 | return vc5_hdmi_csc_full_rgb_to_yuv_bt2020[limited]; |
1403 | } |
1404 | } |
1405 | |
1406 | static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, |
1407 | struct drm_connector_state *state, |
1408 | const struct drm_display_mode *mode) |
1409 | { |
1410 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1411 | struct vc4_hdmi_connector_state *vc4_state = |
1412 | conn_state_to_vc4_hdmi_conn_state(state); |
1413 | unsigned int lim_range = vc4_hdmi_is_full_range(vc4_hdmi, vc4_state) ? 0 : 1; |
1414 | unsigned long flags; |
1415 | const u16 (*csc)[4]; |
1416 | u32 if_cfg = 0; |
1417 | u32 if_xbar = 0x543210; |
1418 | u32 csc_chan_ctl = 0; |
1419 | u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, |
1420 | VC5_MT_CP_CSC_CTL_MODE); |
1421 | int idx; |
1422 | |
1423 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1424 | return; |
1425 | |
1426 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1427 | |
1428 | switch (vc4_state->output_format) { |
1429 | case VC4_HDMI_OUTPUT_YUV444: |
1430 | csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, colorspace: state->colorspace, limited: !!lim_range); |
1431 | |
1432 | vc5_hdmi_set_csc_coeffs_swap(vc4_hdmi, coeffs: csc); |
1433 | break; |
1434 | |
1435 | case VC4_HDMI_OUTPUT_YUV422: |
1436 | csc = vc5_hdmi_find_yuv_csc_coeffs(vc4_hdmi, colorspace: state->colorspace, limited: !!lim_range); |
1437 | |
1438 | csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD, |
1439 | VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) | |
1440 | VC5_MT_CP_CSC_CTL_USE_444_TO_422 | |
1441 | VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION; |
1442 | |
1443 | csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE, |
1444 | VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP); |
1445 | |
1446 | if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY, |
1447 | VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422); |
1448 | |
1449 | vc5_hdmi_set_csc_coeffs(vc4_hdmi, coeffs: csc); |
1450 | break; |
1451 | |
1452 | case VC4_HDMI_OUTPUT_RGB: |
1453 | if_xbar = 0x354021; |
1454 | |
1455 | vc5_hdmi_set_csc_coeffs(vc4_hdmi, coeffs: vc5_hdmi_csc_full_rgb_to_rgb[lim_range]); |
1456 | break; |
1457 | |
1458 | default: |
1459 | break; |
1460 | } |
1461 | |
1462 | HDMI_WRITE(HDMI_VEC_INTERFACE_CFG, if_cfg); |
1463 | HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, if_xbar); |
1464 | HDMI_WRITE(HDMI_CSC_CHANNEL_CTL, csc_chan_ctl); |
1465 | HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); |
1466 | |
1467 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1468 | |
1469 | drm_dev_exit(idx); |
1470 | } |
1471 | |
1472 | static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, |
1473 | struct drm_connector_state *state, |
1474 | const struct drm_display_mode *mode) |
1475 | { |
1476 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1477 | bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; |
1478 | bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; |
1479 | bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; |
1480 | u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; |
1481 | u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start, |
1482 | VC4_HDMI_VERTA_VSP) | |
1483 | VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay, |
1484 | VC4_HDMI_VERTA_VFP) | |
1485 | VC4_SET_FIELD(mode->crtc_vdisplay, VC4_HDMI_VERTA_VAL)); |
1486 | u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) | |
1487 | VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end + |
1488 | interlaced, |
1489 | VC4_HDMI_VERTB_VBP)); |
1490 | u32 vertb_even = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) | |
1491 | VC4_SET_FIELD(mode->crtc_vtotal - |
1492 | mode->crtc_vsync_end, |
1493 | VC4_HDMI_VERTB_VBP)); |
1494 | unsigned long flags; |
1495 | u32 reg; |
1496 | int idx; |
1497 | |
1498 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1499 | return; |
1500 | |
1501 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1502 | |
1503 | HDMI_WRITE(HDMI_HORZA, |
1504 | (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | |
1505 | (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) | |
1506 | VC4_SET_FIELD(mode->hdisplay * pixel_rep, |
1507 | VC4_HDMI_HORZA_HAP)); |
1508 | |
1509 | HDMI_WRITE(HDMI_HORZB, |
1510 | VC4_SET_FIELD((mode->htotal - |
1511 | mode->hsync_end) * pixel_rep, |
1512 | VC4_HDMI_HORZB_HBP) | |
1513 | VC4_SET_FIELD((mode->hsync_end - |
1514 | mode->hsync_start) * pixel_rep, |
1515 | VC4_HDMI_HORZB_HSP) | |
1516 | VC4_SET_FIELD((mode->hsync_start - |
1517 | mode->hdisplay) * pixel_rep, |
1518 | VC4_HDMI_HORZB_HFP)); |
1519 | |
1520 | HDMI_WRITE(HDMI_VERTA0, verta); |
1521 | HDMI_WRITE(HDMI_VERTA1, verta); |
1522 | |
1523 | HDMI_WRITE(HDMI_VERTB0, vertb_even); |
1524 | HDMI_WRITE(HDMI_VERTB1, vertb); |
1525 | |
1526 | reg = HDMI_READ(HDMI_MISC_CONTROL); |
1527 | reg &= ~VC4_HDMI_MISC_CONTROL_PIXEL_REP_MASK; |
1528 | reg |= VC4_SET_FIELD(pixel_rep - 1, VC4_HDMI_MISC_CONTROL_PIXEL_REP); |
1529 | HDMI_WRITE(HDMI_MISC_CONTROL, reg); |
1530 | |
1531 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1532 | |
1533 | drm_dev_exit(idx); |
1534 | } |
1535 | |
1536 | static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, |
1537 | struct drm_connector_state *state, |
1538 | const struct drm_display_mode *mode) |
1539 | { |
1540 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1541 | const struct vc4_hdmi_connector_state *vc4_state = |
1542 | conn_state_to_vc4_hdmi_conn_state(state); |
1543 | bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; |
1544 | bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; |
1545 | bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; |
1546 | u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; |
1547 | u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start, |
1548 | VC5_HDMI_VERTA_VSP) | |
1549 | VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay, |
1550 | VC5_HDMI_VERTA_VFP) | |
1551 | VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL)); |
1552 | u32 vertb = (VC4_SET_FIELD(mode->htotal >> (2 - pixel_rep), |
1553 | VC5_HDMI_VERTB_VSPO) | |
1554 | VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end + |
1555 | interlaced, |
1556 | VC4_HDMI_VERTB_VBP)); |
1557 | u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) | |
1558 | VC4_SET_FIELD(mode->crtc_vtotal - |
1559 | mode->crtc_vsync_end, |
1560 | VC4_HDMI_VERTB_VBP)); |
1561 | unsigned long flags; |
1562 | unsigned char gcp; |
1563 | u32 reg; |
1564 | int idx; |
1565 | |
1566 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1567 | return; |
1568 | |
1569 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1570 | |
1571 | HDMI_WRITE(HDMI_HORZA, |
1572 | (vsync_pos ? VC5_HDMI_HORZA_VPOS : 0) | |
1573 | (hsync_pos ? VC5_HDMI_HORZA_HPOS : 0) | |
1574 | VC4_SET_FIELD(mode->hdisplay * pixel_rep, |
1575 | VC5_HDMI_HORZA_HAP) | |
1576 | VC4_SET_FIELD((mode->hsync_start - |
1577 | mode->hdisplay) * pixel_rep, |
1578 | VC5_HDMI_HORZA_HFP)); |
1579 | |
1580 | HDMI_WRITE(HDMI_HORZB, |
1581 | VC4_SET_FIELD((mode->htotal - |
1582 | mode->hsync_end) * pixel_rep, |
1583 | VC5_HDMI_HORZB_HBP) | |
1584 | VC4_SET_FIELD((mode->hsync_end - |
1585 | mode->hsync_start) * pixel_rep, |
1586 | VC5_HDMI_HORZB_HSP)); |
1587 | |
1588 | HDMI_WRITE(HDMI_VERTA0, verta); |
1589 | HDMI_WRITE(HDMI_VERTA1, verta); |
1590 | |
1591 | HDMI_WRITE(HDMI_VERTB0, vertb_even); |
1592 | HDMI_WRITE(HDMI_VERTB1, vertb); |
1593 | |
1594 | switch (vc4_state->output_bpc) { |
1595 | case 12: |
1596 | gcp = 6; |
1597 | break; |
1598 | case 10: |
1599 | gcp = 5; |
1600 | break; |
1601 | case 8: |
1602 | default: |
1603 | gcp = 0; |
1604 | break; |
1605 | } |
1606 | |
1607 | /* |
1608 | * YCC422 is always 36-bit and not considered deep colour so |
1609 | * doesn't signal in GCP. |
1610 | */ |
1611 | if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) { |
1612 | gcp = 0; |
1613 | } |
1614 | |
1615 | reg = HDMI_READ(HDMI_DEEP_COLOR_CONFIG_1); |
1616 | reg &= ~(VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK | |
1617 | VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_MASK); |
1618 | reg |= VC4_SET_FIELD(2, VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE) | |
1619 | VC4_SET_FIELD(gcp, VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH); |
1620 | HDMI_WRITE(HDMI_DEEP_COLOR_CONFIG_1, reg); |
1621 | |
1622 | reg = HDMI_READ(HDMI_GCP_WORD_1); |
1623 | reg &= ~VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1_MASK; |
1624 | reg |= VC4_SET_FIELD(gcp, VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_1); |
1625 | reg &= ~VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_0_MASK; |
1626 | reg |= VC5_HDMI_GCP_WORD_1_GCP_SUBPACKET_BYTE_0_CLEAR_AVMUTE; |
1627 | HDMI_WRITE(HDMI_GCP_WORD_1, reg); |
1628 | |
1629 | reg = HDMI_READ(HDMI_GCP_CONFIG); |
1630 | reg |= VC5_HDMI_GCP_CONFIG_GCP_ENABLE; |
1631 | HDMI_WRITE(HDMI_GCP_CONFIG, reg); |
1632 | |
1633 | reg = HDMI_READ(HDMI_MISC_CONTROL); |
1634 | reg &= ~VC5_HDMI_MISC_CONTROL_PIXEL_REP_MASK; |
1635 | reg |= VC4_SET_FIELD(pixel_rep - 1, VC5_HDMI_MISC_CONTROL_PIXEL_REP); |
1636 | HDMI_WRITE(HDMI_MISC_CONTROL, reg); |
1637 | |
1638 | HDMI_WRITE(HDMI_CLOCK_STOP, 0); |
1639 | |
1640 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1641 | |
1642 | drm_dev_exit(idx); |
1643 | } |
1644 | |
1645 | static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi) |
1646 | { |
1647 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1648 | unsigned long flags; |
1649 | u32 drift; |
1650 | int ret; |
1651 | int idx; |
1652 | |
1653 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1654 | return; |
1655 | |
1656 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1657 | |
1658 | drift = HDMI_READ(HDMI_FIFO_CTL); |
1659 | drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK; |
1660 | |
1661 | HDMI_WRITE(HDMI_FIFO_CTL, |
1662 | drift & ~VC4_HDMI_FIFO_CTL_RECENTER); |
1663 | HDMI_WRITE(HDMI_FIFO_CTL, |
1664 | drift | VC4_HDMI_FIFO_CTL_RECENTER); |
1665 | |
1666 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1667 | |
1668 | usleep_range(min: 1000, max: 1100); |
1669 | |
1670 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1671 | |
1672 | HDMI_WRITE(HDMI_FIFO_CTL, |
1673 | drift & ~VC4_HDMI_FIFO_CTL_RECENTER); |
1674 | HDMI_WRITE(HDMI_FIFO_CTL, |
1675 | drift | VC4_HDMI_FIFO_CTL_RECENTER); |
1676 | |
1677 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1678 | |
1679 | ret = wait_for(HDMI_READ(HDMI_FIFO_CTL) & |
1680 | VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1); |
1681 | WARN_ONCE(ret, "Timeout waiting for " |
1682 | "VC4_HDMI_FIFO_CTL_RECENTER_DONE" ); |
1683 | |
1684 | drm_dev_exit(idx); |
1685 | } |
1686 | |
1687 | static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, |
1688 | struct drm_atomic_state *state) |
1689 | { |
1690 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1691 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1692 | struct drm_connector *connector = &vc4_hdmi->connector; |
1693 | struct drm_connector_state *conn_state = |
1694 | drm_atomic_get_new_connector_state(state, connector); |
1695 | struct vc4_hdmi_connector_state *vc4_conn_state = |
1696 | conn_state_to_vc4_hdmi_conn_state(conn_state); |
1697 | const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; |
1698 | unsigned long tmds_char_rate = vc4_conn_state->tmds_char_rate; |
1699 | unsigned long bvb_rate, hsm_rate; |
1700 | unsigned long flags; |
1701 | int ret; |
1702 | int idx; |
1703 | |
1704 | mutex_lock(&vc4_hdmi->mutex); |
1705 | |
1706 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1707 | goto out; |
1708 | |
1709 | ret = pm_runtime_resume_and_get(dev: &vc4_hdmi->pdev->dev); |
1710 | if (ret < 0) { |
1711 | DRM_ERROR("Failed to retain power domain: %d\n" , ret); |
1712 | goto err_dev_exit; |
1713 | } |
1714 | |
1715 | /* |
1716 | * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must |
1717 | * be faster than pixel clock, infinitesimally faster, tested in |
1718 | * simulation. Otherwise, exact value is unimportant for HDMI |
1719 | * operation." This conflicts with bcm2835's vc4 documentation, which |
1720 | * states HSM's clock has to be at least 108% of the pixel clock. |
1721 | * |
1722 | * Real life tests reveal that vc4's firmware statement holds up, and |
1723 | * users are able to use pixel clocks closer to HSM's, namely for |
1724 | * 1920x1200@60Hz. So it was decided to have leave a 1% margin between |
1725 | * both clocks. Which, for RPi0-3 implies a maximum pixel clock of |
1726 | * 162MHz. |
1727 | * |
1728 | * Additionally, the AXI clock needs to be at least 25% of |
1729 | * pixel clock, but HSM ends up being the limiting factor. |
1730 | */ |
1731 | hsm_rate = max_t(unsigned long, |
1732 | HSM_MIN_CLOCK_FREQ, |
1733 | (tmds_char_rate / 100) * 101); |
1734 | ret = clk_set_min_rate(clk: vc4_hdmi->hsm_clock, rate: hsm_rate); |
1735 | if (ret) { |
1736 | DRM_ERROR("Failed to set HSM clock rate: %d\n" , ret); |
1737 | goto err_put_runtime_pm; |
1738 | } |
1739 | |
1740 | ret = clk_set_rate(clk: vc4_hdmi->pixel_clock, rate: tmds_char_rate); |
1741 | if (ret) { |
1742 | DRM_ERROR("Failed to set pixel clock rate: %d\n" , ret); |
1743 | goto err_put_runtime_pm; |
1744 | } |
1745 | |
1746 | ret = clk_prepare_enable(clk: vc4_hdmi->pixel_clock); |
1747 | if (ret) { |
1748 | DRM_ERROR("Failed to turn on pixel clock: %d\n" , ret); |
1749 | goto err_put_runtime_pm; |
1750 | } |
1751 | |
1752 | |
1753 | vc4_hdmi_cec_update_clk_div(vc4_hdmi); |
1754 | |
1755 | if (tmds_char_rate > 297000000) |
1756 | bvb_rate = 300000000; |
1757 | else if (tmds_char_rate > 148500000) |
1758 | bvb_rate = 150000000; |
1759 | else |
1760 | bvb_rate = 75000000; |
1761 | |
1762 | ret = clk_set_min_rate(clk: vc4_hdmi->pixel_bvb_clock, rate: bvb_rate); |
1763 | if (ret) { |
1764 | DRM_ERROR("Failed to set pixel bvb clock rate: %d\n" , ret); |
1765 | goto err_disable_pixel_clock; |
1766 | } |
1767 | |
1768 | ret = clk_prepare_enable(clk: vc4_hdmi->pixel_bvb_clock); |
1769 | if (ret) { |
1770 | DRM_ERROR("Failed to turn on pixel bvb clock: %d\n" , ret); |
1771 | goto err_disable_pixel_clock; |
1772 | } |
1773 | |
1774 | if (vc4_hdmi->variant->phy_init) |
1775 | vc4_hdmi->variant->phy_init(vc4_hdmi, vc4_conn_state); |
1776 | |
1777 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1778 | |
1779 | HDMI_WRITE(HDMI_SCHEDULER_CONTROL, |
1780 | HDMI_READ(HDMI_SCHEDULER_CONTROL) | |
1781 | VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT | |
1782 | VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS); |
1783 | |
1784 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1785 | |
1786 | if (vc4_hdmi->variant->set_timings) |
1787 | vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode); |
1788 | |
1789 | drm_dev_exit(idx); |
1790 | |
1791 | mutex_unlock(lock: &vc4_hdmi->mutex); |
1792 | |
1793 | return; |
1794 | |
1795 | err_disable_pixel_clock: |
1796 | clk_disable_unprepare(clk: vc4_hdmi->pixel_clock); |
1797 | err_put_runtime_pm: |
1798 | pm_runtime_put(dev: &vc4_hdmi->pdev->dev); |
1799 | err_dev_exit: |
1800 | drm_dev_exit(idx); |
1801 | out: |
1802 | mutex_unlock(lock: &vc4_hdmi->mutex); |
1803 | return; |
1804 | } |
1805 | |
1806 | static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, |
1807 | struct drm_atomic_state *state) |
1808 | { |
1809 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1810 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1811 | struct drm_connector *connector = &vc4_hdmi->connector; |
1812 | const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; |
1813 | struct drm_connector_state *conn_state = |
1814 | drm_atomic_get_new_connector_state(state, connector); |
1815 | unsigned long flags; |
1816 | int idx; |
1817 | |
1818 | mutex_lock(&vc4_hdmi->mutex); |
1819 | |
1820 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1821 | goto out; |
1822 | |
1823 | if (vc4_hdmi->variant->csc_setup) |
1824 | vc4_hdmi->variant->csc_setup(vc4_hdmi, conn_state, mode); |
1825 | |
1826 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1827 | HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); |
1828 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1829 | |
1830 | drm_dev_exit(idx); |
1831 | |
1832 | out: |
1833 | mutex_unlock(lock: &vc4_hdmi->mutex); |
1834 | } |
1835 | |
1836 | static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, |
1837 | struct drm_atomic_state *state) |
1838 | { |
1839 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1840 | struct drm_device *drm = vc4_hdmi->connector.dev; |
1841 | const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; |
1842 | struct drm_display_info *display = &vc4_hdmi->connector.display_info; |
1843 | bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; |
1844 | bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; |
1845 | unsigned long flags; |
1846 | int ret; |
1847 | int idx; |
1848 | |
1849 | mutex_lock(&vc4_hdmi->mutex); |
1850 | |
1851 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
1852 | goto out; |
1853 | |
1854 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1855 | |
1856 | HDMI_WRITE(HDMI_VID_CTL, |
1857 | VC4_HD_VID_CTL_ENABLE | |
1858 | VC4_HD_VID_CTL_CLRRGB | |
1859 | VC4_HD_VID_CTL_UNDERFLOW_ENABLE | |
1860 | VC4_HD_VID_CTL_FRAME_COUNTER_RESET | |
1861 | (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | |
1862 | (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); |
1863 | |
1864 | HDMI_WRITE(HDMI_VID_CTL, |
1865 | HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_BLANKPIX); |
1866 | |
1867 | if (display->is_hdmi) { |
1868 | HDMI_WRITE(HDMI_SCHEDULER_CONTROL, |
1869 | HDMI_READ(HDMI_SCHEDULER_CONTROL) | |
1870 | VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); |
1871 | |
1872 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1873 | |
1874 | ret = wait_for(HDMI_READ(HDMI_SCHEDULER_CONTROL) & |
1875 | VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1000); |
1876 | WARN_ONCE(ret, "Timeout waiting for " |
1877 | "VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n" ); |
1878 | } else { |
1879 | HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, |
1880 | HDMI_READ(HDMI_RAM_PACKET_CONFIG) & |
1881 | ~(VC4_HDMI_RAM_PACKET_ENABLE)); |
1882 | HDMI_WRITE(HDMI_SCHEDULER_CONTROL, |
1883 | HDMI_READ(HDMI_SCHEDULER_CONTROL) & |
1884 | ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); |
1885 | |
1886 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1887 | |
1888 | ret = wait_for(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) & |
1889 | VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1000); |
1890 | WARN_ONCE(ret, "Timeout waiting for " |
1891 | "!VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n" ); |
1892 | } |
1893 | |
1894 | if (display->is_hdmi) { |
1895 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
1896 | |
1897 | WARN_ON(!(HDMI_READ(HDMI_SCHEDULER_CONTROL) & |
1898 | VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)); |
1899 | |
1900 | HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, |
1901 | VC4_HDMI_RAM_PACKET_ENABLE); |
1902 | |
1903 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
1904 | vc4_hdmi->packet_ram_enabled = true; |
1905 | |
1906 | vc4_hdmi_set_infoframes(encoder); |
1907 | } |
1908 | |
1909 | vc4_hdmi_recenter_fifo(vc4_hdmi); |
1910 | vc4_hdmi_enable_scrambling(encoder); |
1911 | |
1912 | drm_dev_exit(idx); |
1913 | |
1914 | out: |
1915 | mutex_unlock(lock: &vc4_hdmi->mutex); |
1916 | } |
1917 | |
1918 | static void vc4_hdmi_encoder_atomic_mode_set(struct drm_encoder *encoder, |
1919 | struct drm_crtc_state *crtc_state, |
1920 | struct drm_connector_state *conn_state) |
1921 | { |
1922 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
1923 | struct vc4_hdmi_connector_state *vc4_state = |
1924 | conn_state_to_vc4_hdmi_conn_state(conn_state); |
1925 | |
1926 | mutex_lock(&vc4_hdmi->mutex); |
1927 | drm_mode_copy(dst: &vc4_hdmi->saved_adjusted_mode, |
1928 | src: &crtc_state->adjusted_mode); |
1929 | vc4_hdmi->output_bpc = vc4_state->output_bpc; |
1930 | vc4_hdmi->output_format = vc4_state->output_format; |
1931 | mutex_unlock(lock: &vc4_hdmi->mutex); |
1932 | } |
1933 | |
1934 | static bool |
1935 | vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi, |
1936 | const struct drm_display_info *info, |
1937 | const struct drm_display_mode *mode, |
1938 | unsigned int format, unsigned int bpc) |
1939 | { |
1940 | struct drm_device *dev = vc4_hdmi->connector.dev; |
1941 | u8 vic = drm_match_cea_mode(to_match: mode); |
1942 | |
1943 | if (vic == 1 && bpc != 8) { |
1944 | drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n" , bpc); |
1945 | return false; |
1946 | } |
1947 | |
1948 | if (!info->is_hdmi && |
1949 | (format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) { |
1950 | drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n" ); |
1951 | return false; |
1952 | } |
1953 | |
1954 | switch (format) { |
1955 | case VC4_HDMI_OUTPUT_RGB: |
1956 | drm_dbg(dev, "RGB Format, checking the constraints.\n" ); |
1957 | |
1958 | if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444)) |
1959 | return false; |
1960 | |
1961 | if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) { |
1962 | drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n" ); |
1963 | return false; |
1964 | } |
1965 | |
1966 | if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) { |
1967 | drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n" ); |
1968 | return false; |
1969 | } |
1970 | |
1971 | drm_dbg(dev, "RGB format supported in that configuration.\n" ); |
1972 | |
1973 | return true; |
1974 | |
1975 | case VC4_HDMI_OUTPUT_YUV422: |
1976 | drm_dbg(dev, "YUV422 format, checking the constraints.\n" ); |
1977 | |
1978 | if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) { |
1979 | drm_dbg(dev, "Sink doesn't support YUV422.\n" ); |
1980 | return false; |
1981 | } |
1982 | |
1983 | if (bpc != 12) { |
1984 | drm_dbg(dev, "YUV422 only supports 12 bpc.\n" ); |
1985 | return false; |
1986 | } |
1987 | |
1988 | drm_dbg(dev, "YUV422 format supported in that configuration.\n" ); |
1989 | |
1990 | return true; |
1991 | |
1992 | case VC4_HDMI_OUTPUT_YUV444: |
1993 | drm_dbg(dev, "YUV444 format, checking the constraints.\n" ); |
1994 | |
1995 | if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) { |
1996 | drm_dbg(dev, "Sink doesn't support YUV444.\n" ); |
1997 | return false; |
1998 | } |
1999 | |
2000 | if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) { |
2001 | drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n" ); |
2002 | return false; |
2003 | } |
2004 | |
2005 | if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) { |
2006 | drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n" ); |
2007 | return false; |
2008 | } |
2009 | |
2010 | drm_dbg(dev, "YUV444 format supported in that configuration.\n" ); |
2011 | |
2012 | return true; |
2013 | } |
2014 | |
2015 | return false; |
2016 | } |
2017 | |
2018 | static enum drm_mode_status |
2019 | vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi, |
2020 | const struct drm_display_mode *mode, |
2021 | unsigned long long clock) |
2022 | { |
2023 | const struct drm_connector *connector = &vc4_hdmi->connector; |
2024 | const struct drm_display_info *info = &connector->display_info; |
2025 | struct vc4_dev *vc4 = to_vc4_dev(connector->dev); |
2026 | |
2027 | if (clock > vc4_hdmi->variant->max_pixel_clock) |
2028 | return MODE_CLOCK_HIGH; |
2029 | |
2030 | if (!vc4->hvs->vc5_hdmi_enable_hdmi_20 && clock > HDMI_14_MAX_TMDS_CLK) |
2031 | return MODE_CLOCK_HIGH; |
2032 | |
2033 | /* 4096x2160@60 is not reliable without overclocking core */ |
2034 | if (!vc4->hvs->vc5_hdmi_enable_4096by2160 && |
2035 | mode->hdisplay > 3840 && mode->vdisplay >= 2160 && |
2036 | drm_mode_vrefresh(mode) >= 50) |
2037 | return MODE_CLOCK_HIGH; |
2038 | |
2039 | if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000)) |
2040 | return MODE_CLOCK_HIGH; |
2041 | |
2042 | return MODE_OK; |
2043 | } |
2044 | |
2045 | static unsigned long long |
2046 | vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode, |
2047 | unsigned int bpc, |
2048 | enum vc4_hdmi_output_format fmt) |
2049 | { |
2050 | unsigned long long clock = mode->clock * 1000ULL; |
2051 | |
2052 | if (mode->flags & DRM_MODE_FLAG_DBLCLK) |
2053 | clock = clock * 2; |
2054 | |
2055 | if (fmt == VC4_HDMI_OUTPUT_YUV422) |
2056 | bpc = 8; |
2057 | |
2058 | clock = clock * bpc; |
2059 | do_div(clock, 8); |
2060 | |
2061 | return clock; |
2062 | } |
2063 | |
2064 | static int |
2065 | vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi, |
2066 | struct vc4_hdmi_connector_state *vc4_state, |
2067 | const struct drm_display_mode *mode, |
2068 | unsigned int bpc, unsigned int fmt) |
2069 | { |
2070 | unsigned long long clock; |
2071 | |
2072 | clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt); |
2073 | if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, clock) != MODE_OK) |
2074 | return -EINVAL; |
2075 | |
2076 | vc4_state->tmds_char_rate = clock; |
2077 | |
2078 | return 0; |
2079 | } |
2080 | |
2081 | static int |
2082 | vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi, |
2083 | struct vc4_hdmi_connector_state *vc4_state, |
2084 | const struct drm_display_mode *mode, |
2085 | unsigned int bpc) |
2086 | { |
2087 | struct drm_device *dev = vc4_hdmi->connector.dev; |
2088 | const struct drm_connector *connector = &vc4_hdmi->connector; |
2089 | const struct drm_display_info *info = &connector->display_info; |
2090 | unsigned int format; |
2091 | |
2092 | drm_dbg(dev, "Trying with an RGB output\n" ); |
2093 | |
2094 | format = VC4_HDMI_OUTPUT_RGB; |
2095 | if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { |
2096 | int ret; |
2097 | |
2098 | ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, |
2099 | mode, bpc, fmt: format); |
2100 | if (!ret) { |
2101 | vc4_state->output_format = format; |
2102 | return 0; |
2103 | } |
2104 | } |
2105 | |
2106 | drm_dbg(dev, "Failed, Trying with an YUV422 output\n" ); |
2107 | |
2108 | format = VC4_HDMI_OUTPUT_YUV422; |
2109 | if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) { |
2110 | int ret; |
2111 | |
2112 | ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state, |
2113 | mode, bpc, fmt: format); |
2114 | if (!ret) { |
2115 | vc4_state->output_format = format; |
2116 | return 0; |
2117 | } |
2118 | } |
2119 | |
2120 | drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n" ); |
2121 | |
2122 | return -EINVAL; |
2123 | } |
2124 | |
2125 | static int |
2126 | vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi, |
2127 | struct vc4_hdmi_connector_state *vc4_state, |
2128 | const struct drm_display_mode *mode) |
2129 | { |
2130 | struct drm_device *dev = vc4_hdmi->connector.dev; |
2131 | struct drm_connector_state *conn_state = &vc4_state->base; |
2132 | unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12); |
2133 | unsigned int bpc; |
2134 | int ret; |
2135 | |
2136 | for (bpc = max_bpc; bpc >= 8; bpc -= 2) { |
2137 | drm_dbg(dev, "Trying with a %d bpc output\n" , bpc); |
2138 | |
2139 | ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state, |
2140 | mode, bpc); |
2141 | if (ret) |
2142 | continue; |
2143 | |
2144 | vc4_state->output_bpc = bpc; |
2145 | |
2146 | drm_dbg(dev, |
2147 | "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n" , |
2148 | mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), |
2149 | vc4_state->output_bpc, |
2150 | vc4_hdmi_output_fmt_str(vc4_state->output_format), |
2151 | vc4_state->tmds_char_rate); |
2152 | |
2153 | break; |
2154 | } |
2155 | |
2156 | return ret; |
2157 | } |
2158 | |
2159 | #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL |
2160 | #define WIFI_2_4GHz_CH1_MAX_FREQ 2422000000ULL |
2161 | |
2162 | static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, |
2163 | struct drm_crtc_state *crtc_state, |
2164 | struct drm_connector_state *conn_state) |
2165 | { |
2166 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
2167 | struct drm_connector *connector = &vc4_hdmi->connector; |
2168 | struct drm_connector_state *old_conn_state = |
2169 | drm_atomic_get_old_connector_state(state: conn_state->state, connector); |
2170 | struct vc4_hdmi_connector_state *old_vc4_state = |
2171 | conn_state_to_vc4_hdmi_conn_state(old_conn_state); |
2172 | struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(conn_state); |
2173 | struct drm_display_mode *mode = &crtc_state->adjusted_mode; |
2174 | unsigned long long tmds_char_rate = mode->clock * 1000; |
2175 | unsigned long long tmds_bit_rate; |
2176 | int ret; |
2177 | |
2178 | if (vc4_hdmi->variant->unsupported_odd_h_timings) { |
2179 | if (mode->flags & DRM_MODE_FLAG_DBLCLK) { |
2180 | /* Only try to fixup DBLCLK modes to get 480i and 576i |
2181 | * working. |
2182 | * A generic solution for all modes with odd horizontal |
2183 | * timing values seems impossible based on trying to |
2184 | * solve it for 1366x768 monitors. |
2185 | */ |
2186 | if ((mode->hsync_start - mode->hdisplay) & 1) |
2187 | mode->hsync_start--; |
2188 | if ((mode->hsync_end - mode->hsync_start) & 1) |
2189 | mode->hsync_end--; |
2190 | } |
2191 | |
2192 | /* Now check whether we still have odd values remaining */ |
2193 | if ((mode->hdisplay % 2) || (mode->hsync_start % 2) || |
2194 | (mode->hsync_end % 2) || (mode->htotal % 2)) |
2195 | return -EINVAL; |
2196 | } |
2197 | |
2198 | /* |
2199 | * The 1440p@60 pixel rate is in the same range than the first |
2200 | * WiFi channel (between 2.4GHz and 2.422GHz with 22MHz |
2201 | * bandwidth). Slightly lower the frequency to bring it out of |
2202 | * the WiFi range. |
2203 | */ |
2204 | tmds_bit_rate = tmds_char_rate * 10; |
2205 | if (vc4_hdmi->disable_wifi_frequencies && |
2206 | (tmds_bit_rate >= WIFI_2_4GHz_CH1_MIN_FREQ && |
2207 | tmds_bit_rate <= WIFI_2_4GHz_CH1_MAX_FREQ)) { |
2208 | mode->clock = 238560; |
2209 | tmds_char_rate = mode->clock * 1000; |
2210 | } |
2211 | |
2212 | ret = vc4_hdmi_encoder_compute_config(vc4_hdmi, vc4_state, mode); |
2213 | if (ret) |
2214 | return ret; |
2215 | |
2216 | /* vc4_hdmi_encoder_compute_config may have changed output_bpc and/or output_format */ |
2217 | if (vc4_state->output_bpc != old_vc4_state->output_bpc || |
2218 | vc4_state->output_format != old_vc4_state->output_format) |
2219 | crtc_state->mode_changed = true; |
2220 | |
2221 | return 0; |
2222 | } |
2223 | |
2224 | static enum drm_mode_status |
2225 | vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, |
2226 | const struct drm_display_mode *mode) |
2227 | { |
2228 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
2229 | |
2230 | if (vc4_hdmi->variant->unsupported_odd_h_timings && |
2231 | !(mode->flags & DRM_MODE_FLAG_DBLCLK) && |
2232 | ((mode->hdisplay % 2) || (mode->hsync_start % 2) || |
2233 | (mode->hsync_end % 2) || (mode->htotal % 2))) |
2234 | return MODE_H_ILLEGAL; |
2235 | |
2236 | return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, clock: mode->clock * 1000); |
2237 | } |
2238 | |
2239 | static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { |
2240 | .atomic_check = vc4_hdmi_encoder_atomic_check, |
2241 | .atomic_mode_set = vc4_hdmi_encoder_atomic_mode_set, |
2242 | .mode_valid = vc4_hdmi_encoder_mode_valid, |
2243 | }; |
2244 | |
2245 | static int vc4_hdmi_late_register(struct drm_encoder *encoder) |
2246 | { |
2247 | struct drm_device *drm = encoder->dev; |
2248 | struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); |
2249 | const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; |
2250 | |
2251 | drm_debugfs_add_file(dev: drm, name: variant->debugfs_name, |
2252 | show: vc4_hdmi_debugfs_regs, data: vc4_hdmi); |
2253 | |
2254 | return 0; |
2255 | } |
2256 | |
2257 | static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = { |
2258 | .late_register = vc4_hdmi_late_register, |
2259 | }; |
2260 | |
2261 | static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) |
2262 | { |
2263 | int i; |
2264 | u32 channel_map = 0; |
2265 | |
2266 | for (i = 0; i < 8; i++) { |
2267 | if (channel_mask & BIT(i)) |
2268 | channel_map |= i << (3 * i); |
2269 | } |
2270 | return channel_map; |
2271 | } |
2272 | |
2273 | static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) |
2274 | { |
2275 | int i; |
2276 | u32 channel_map = 0; |
2277 | |
2278 | for (i = 0; i < 8; i++) { |
2279 | if (channel_mask & BIT(i)) |
2280 | channel_map |= i << (4 * i); |
2281 | } |
2282 | return channel_map; |
2283 | } |
2284 | |
2285 | static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi) |
2286 | { |
2287 | struct drm_device *drm = vc4_hdmi->connector.dev; |
2288 | unsigned long flags; |
2289 | u32 hotplug; |
2290 | int idx; |
2291 | |
2292 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
2293 | return false; |
2294 | |
2295 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
2296 | hotplug = HDMI_READ(HDMI_HOTPLUG); |
2297 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
2298 | |
2299 | drm_dev_exit(idx); |
2300 | |
2301 | return !!(hotplug & VC4_HDMI_HOTPLUG_CONNECTED); |
2302 | } |
2303 | |
2304 | /* HDMI audio codec callbacks */ |
2305 | static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi, |
2306 | unsigned int samplerate) |
2307 | { |
2308 | struct drm_device *drm = vc4_hdmi->connector.dev; |
2309 | u32 hsm_clock; |
2310 | unsigned long flags; |
2311 | unsigned long n, m; |
2312 | int idx; |
2313 | |
2314 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
2315 | return; |
2316 | |
2317 | hsm_clock = clk_get_rate(clk: vc4_hdmi->audio_clock); |
2318 | rational_best_approximation(given_numerator: hsm_clock, given_denominator: samplerate, |
2319 | VC4_HD_MAI_SMP_N_MASK >> |
2320 | VC4_HD_MAI_SMP_N_SHIFT, |
2321 | max_denominator: (VC4_HD_MAI_SMP_M_MASK >> |
2322 | VC4_HD_MAI_SMP_M_SHIFT) + 1, |
2323 | best_numerator: &n, best_denominator: &m); |
2324 | |
2325 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
2326 | HDMI_WRITE(HDMI_MAI_SMP, |
2327 | VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) | |
2328 | VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); |
2329 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
2330 | |
2331 | drm_dev_exit(idx); |
2332 | } |
2333 | |
2334 | static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate) |
2335 | { |
2336 | const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; |
2337 | u32 n, cts; |
2338 | u64 tmp; |
2339 | |
2340 | lockdep_assert_held(&vc4_hdmi->mutex); |
2341 | lockdep_assert_held(&vc4_hdmi->hw_lock); |
2342 | |
2343 | n = 128 * samplerate / 1000; |
2344 | tmp = (u64)(mode->clock * 1000) * n; |
2345 | do_div(tmp, 128 * samplerate); |
2346 | cts = tmp; |
2347 | |
2348 | HDMI_WRITE(HDMI_CRP_CFG, |
2349 | VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN | |
2350 | VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N)); |
2351 | |
2352 | /* |
2353 | * We could get slightly more accurate clocks in some cases by |
2354 | * providing a CTS_1 value. The two CTS values are alternated |
2355 | * between based on the period fields |
2356 | */ |
2357 | HDMI_WRITE(HDMI_CTS_0, cts); |
2358 | HDMI_WRITE(HDMI_CTS_1, cts); |
2359 | } |
2360 | |
2361 | static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai) |
2362 | { |
2363 | struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); |
2364 | |
2365 | return snd_soc_card_get_drvdata(card); |
2366 | } |
2367 | |
2368 | static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi) |
2369 | { |
2370 | struct drm_display_info *display = &vc4_hdmi->connector.display_info; |
2371 | |
2372 | lockdep_assert_held(&vc4_hdmi->mutex); |
2373 | |
2374 | /* |
2375 | * If the encoder is currently in DVI mode, treat the codec DAI |
2376 | * as missing. |
2377 | */ |
2378 | if (!display->is_hdmi) |
2379 | return false; |
2380 | |
2381 | return true; |
2382 | } |
2383 | |
2384 | static int vc4_hdmi_audio_startup(struct device *dev, void *data) |
2385 | { |
2386 | struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); |
2387 | struct drm_device *drm = vc4_hdmi->connector.dev; |
2388 | unsigned long flags; |
2389 | int ret = 0; |
2390 | int idx; |
2391 | |
2392 | mutex_lock(&vc4_hdmi->mutex); |
2393 | |
2394 | if (!drm_dev_enter(dev: drm, idx: &idx)) { |
2395 | ret = -ENODEV; |
2396 | goto out; |
2397 | } |
2398 | |
2399 | if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) { |
2400 | ret = -ENODEV; |
2401 | goto out_dev_exit; |
2402 | } |
2403 | |
2404 | vc4_hdmi->audio.streaming = true; |
2405 | |
2406 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
2407 | HDMI_WRITE(HDMI_MAI_CTL, |
2408 | VC4_HD_MAI_CTL_RESET | |
2409 | VC4_HD_MAI_CTL_FLUSH | |
2410 | VC4_HD_MAI_CTL_DLATE | |
2411 | VC4_HD_MAI_CTL_ERRORE | |
2412 | VC4_HD_MAI_CTL_ERRORF); |
2413 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
2414 | |
2415 | if (vc4_hdmi->variant->phy_rng_enable) |
2416 | vc4_hdmi->variant->phy_rng_enable(vc4_hdmi); |
2417 | |
2418 | out_dev_exit: |
2419 | drm_dev_exit(idx); |
2420 | out: |
2421 | mutex_unlock(lock: &vc4_hdmi->mutex); |
2422 | |
2423 | return ret; |
2424 | } |
2425 | |
2426 | static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) |
2427 | { |
2428 | struct drm_encoder *encoder = &vc4_hdmi->encoder.base; |
2429 | struct device *dev = &vc4_hdmi->pdev->dev; |
2430 | unsigned long flags; |
2431 | int ret; |
2432 | |
2433 | lockdep_assert_held(&vc4_hdmi->mutex); |
2434 | |
2435 | vc4_hdmi->audio.streaming = false; |
2436 | ret = vc4_hdmi_stop_packet(encoder, type: HDMI_INFOFRAME_TYPE_AUDIO, poll: false); |
2437 | if (ret) |
2438 | dev_err(dev, "Failed to stop audio infoframe: %d\n" , ret); |
2439 | |
2440 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
2441 | |
2442 | HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_RESET); |
2443 | HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_ERRORF); |
2444 | HDMI_WRITE(HDMI_MAI_CTL, VC4_HD_MAI_CTL_FLUSH); |
2445 | |
2446 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
2447 | } |
2448 | |
2449 | static void vc4_hdmi_audio_shutdown(struct device *dev, void *data) |
2450 | { |
2451 | struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); |
2452 | struct drm_device *drm = vc4_hdmi->connector.dev; |
2453 | unsigned long flags; |
2454 | int idx; |
2455 | |
2456 | mutex_lock(&vc4_hdmi->mutex); |
2457 | |
2458 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
2459 | goto out; |
2460 | |
2461 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
2462 | |
2463 | HDMI_WRITE(HDMI_MAI_CTL, |
2464 | VC4_HD_MAI_CTL_DLATE | |
2465 | VC4_HD_MAI_CTL_ERRORE | |
2466 | VC4_HD_MAI_CTL_ERRORF); |
2467 | |
2468 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
2469 | |
2470 | if (vc4_hdmi->variant->phy_rng_disable) |
2471 | vc4_hdmi->variant->phy_rng_disable(vc4_hdmi); |
2472 | |
2473 | vc4_hdmi->audio.streaming = false; |
2474 | vc4_hdmi_audio_reset(vc4_hdmi); |
2475 | |
2476 | drm_dev_exit(idx); |
2477 | |
2478 | out: |
2479 | mutex_unlock(lock: &vc4_hdmi->mutex); |
2480 | } |
2481 | |
2482 | static int sample_rate_to_mai_fmt(int samplerate) |
2483 | { |
2484 | switch (samplerate) { |
2485 | case 8000: |
2486 | return VC4_HDMI_MAI_SAMPLE_RATE_8000; |
2487 | case 11025: |
2488 | return VC4_HDMI_MAI_SAMPLE_RATE_11025; |
2489 | case 12000: |
2490 | return VC4_HDMI_MAI_SAMPLE_RATE_12000; |
2491 | case 16000: |
2492 | return VC4_HDMI_MAI_SAMPLE_RATE_16000; |
2493 | case 22050: |
2494 | return VC4_HDMI_MAI_SAMPLE_RATE_22050; |
2495 | case 24000: |
2496 | return VC4_HDMI_MAI_SAMPLE_RATE_24000; |
2497 | case 32000: |
2498 | return VC4_HDMI_MAI_SAMPLE_RATE_32000; |
2499 | case 44100: |
2500 | return VC4_HDMI_MAI_SAMPLE_RATE_44100; |
2501 | case 48000: |
2502 | return VC4_HDMI_MAI_SAMPLE_RATE_48000; |
2503 | case 64000: |
2504 | return VC4_HDMI_MAI_SAMPLE_RATE_64000; |
2505 | case 88200: |
2506 | return VC4_HDMI_MAI_SAMPLE_RATE_88200; |
2507 | case 96000: |
2508 | return VC4_HDMI_MAI_SAMPLE_RATE_96000; |
2509 | case 128000: |
2510 | return VC4_HDMI_MAI_SAMPLE_RATE_128000; |
2511 | case 176400: |
2512 | return VC4_HDMI_MAI_SAMPLE_RATE_176400; |
2513 | case 192000: |
2514 | return VC4_HDMI_MAI_SAMPLE_RATE_192000; |
2515 | default: |
2516 | return VC4_HDMI_MAI_SAMPLE_RATE_NOT_INDICATED; |
2517 | } |
2518 | } |
2519 | |
2520 | /* HDMI audio codec callbacks */ |
2521 | static int vc4_hdmi_audio_prepare(struct device *dev, void *data, |
2522 | struct hdmi_codec_daifmt *daifmt, |
2523 | struct hdmi_codec_params *params) |
2524 | { |
2525 | struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); |
2526 | struct drm_device *drm = vc4_hdmi->connector.dev; |
2527 | struct drm_encoder *encoder = &vc4_hdmi->encoder.base; |
2528 | unsigned int sample_rate = params->sample_rate; |
2529 | unsigned int channels = params->channels; |
2530 | unsigned long flags; |
2531 | u32 audio_packet_config, channel_mask; |
2532 | u32 channel_map; |
2533 | u32 mai_audio_format; |
2534 | u32 mai_sample_rate; |
2535 | int ret = 0; |
2536 | int idx; |
2537 | |
2538 | dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n" , __func__, |
2539 | sample_rate, params->sample_width, channels); |
2540 | |
2541 | mutex_lock(&vc4_hdmi->mutex); |
2542 | |
2543 | if (!drm_dev_enter(dev: drm, idx: &idx)) { |
2544 | ret = -ENODEV; |
2545 | goto out; |
2546 | } |
2547 | |
2548 | if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) { |
2549 | ret = -EINVAL; |
2550 | goto out_dev_exit; |
2551 | } |
2552 | |
2553 | vc4_hdmi_audio_set_mai_clock(vc4_hdmi, samplerate: sample_rate); |
2554 | |
2555 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
2556 | HDMI_WRITE(HDMI_MAI_CTL, |
2557 | VC4_SET_FIELD(channels, VC4_HD_MAI_CTL_CHNUM) | |
2558 | VC4_HD_MAI_CTL_WHOLSMP | |
2559 | VC4_HD_MAI_CTL_CHALIGN | |
2560 | VC4_HD_MAI_CTL_ENABLE); |
2561 | |
2562 | mai_sample_rate = sample_rate_to_mai_fmt(samplerate: sample_rate); |
2563 | if (params->iec.status[0] & IEC958_AES0_NONAUDIO && |
2564 | params->channels == 8) |
2565 | mai_audio_format = VC4_HDMI_MAI_FORMAT_HBR; |
2566 | else |
2567 | mai_audio_format = VC4_HDMI_MAI_FORMAT_PCM; |
2568 | HDMI_WRITE(HDMI_MAI_FMT, |
2569 | VC4_SET_FIELD(mai_sample_rate, |
2570 | VC4_HDMI_MAI_FORMAT_SAMPLE_RATE) | |
2571 | VC4_SET_FIELD(mai_audio_format, |
2572 | VC4_HDMI_MAI_FORMAT_AUDIO_FORMAT)); |
2573 | |
2574 | /* The B frame identifier should match the value used by alsa-lib (8) */ |
2575 | audio_packet_config = |
2576 | VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT | |
2577 | VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS | |
2578 | VC4_SET_FIELD(0x8, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER); |
2579 | |
2580 | channel_mask = GENMASK(channels - 1, 0); |
2581 | audio_packet_config |= VC4_SET_FIELD(channel_mask, |
2582 | VC4_HDMI_AUDIO_PACKET_CEA_MASK); |
2583 | |
2584 | /* Set the MAI threshold */ |
2585 | HDMI_WRITE(HDMI_MAI_THR, |
2586 | VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICHIGH) | |
2587 | VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_PANICLOW) | |
2588 | VC4_SET_FIELD(0x06, VC4_HD_MAI_THR_DREQHIGH) | |
2589 | VC4_SET_FIELD(0x08, VC4_HD_MAI_THR_DREQLOW)); |
2590 | |
2591 | HDMI_WRITE(HDMI_MAI_CONFIG, |
2592 | VC4_HDMI_MAI_CONFIG_BIT_REVERSE | |
2593 | VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE | |
2594 | VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK)); |
2595 | |
2596 | channel_map = vc4_hdmi->variant->channel_map(vc4_hdmi, channel_mask); |
2597 | HDMI_WRITE(HDMI_MAI_CHANNEL_MAP, channel_map); |
2598 | HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); |
2599 | |
2600 | vc4_hdmi_set_n_cts(vc4_hdmi, samplerate: sample_rate); |
2601 | |
2602 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
2603 | |
2604 | memcpy(&vc4_hdmi->audio.infoframe, ¶ms->cea, sizeof(params->cea)); |
2605 | vc4_hdmi_set_audio_infoframe(encoder); |
2606 | |
2607 | out_dev_exit: |
2608 | drm_dev_exit(idx); |
2609 | out: |
2610 | mutex_unlock(lock: &vc4_hdmi->mutex); |
2611 | |
2612 | return ret; |
2613 | } |
2614 | |
2615 | static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = { |
2616 | .name = "vc4-hdmi-cpu-dai-component" , |
2617 | .legacy_dai_naming = 1, |
2618 | }; |
2619 | |
2620 | static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai) |
2621 | { |
2622 | struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); |
2623 | |
2624 | snd_soc_dai_init_dma_data(dai, playback: &vc4_hdmi->audio.dma_data, NULL); |
2625 | |
2626 | return 0; |
2627 | } |
2628 | |
2629 | static const struct snd_soc_dai_ops vc4_snd_dai_ops = { |
2630 | .probe = vc4_hdmi_audio_cpu_dai_probe, |
2631 | }; |
2632 | |
2633 | static struct snd_soc_dai_driver vc4_hdmi_audio_cpu_dai_drv = { |
2634 | .name = "vc4-hdmi-cpu-dai" , |
2635 | .ops = &vc4_snd_dai_ops, |
2636 | .playback = { |
2637 | .stream_name = "Playback" , |
2638 | .channels_min = 1, |
2639 | .channels_max = 8, |
2640 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | |
2641 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | |
2642 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | |
2643 | SNDRV_PCM_RATE_192000, |
2644 | .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, |
2645 | }, |
2646 | }; |
2647 | |
2648 | static const struct snd_dmaengine_pcm_config pcm_conf = { |
2649 | .chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "audio-rx" , |
2650 | .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, |
2651 | }; |
2652 | |
2653 | static int vc4_hdmi_audio_get_eld(struct device *dev, void *data, |
2654 | uint8_t *buf, size_t len) |
2655 | { |
2656 | struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); |
2657 | struct drm_connector *connector = &vc4_hdmi->connector; |
2658 | |
2659 | mutex_lock(&vc4_hdmi->mutex); |
2660 | memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); |
2661 | mutex_unlock(lock: &vc4_hdmi->mutex); |
2662 | |
2663 | return 0; |
2664 | } |
2665 | |
2666 | static const struct hdmi_codec_ops vc4_hdmi_codec_ops = { |
2667 | .get_eld = vc4_hdmi_audio_get_eld, |
2668 | .prepare = vc4_hdmi_audio_prepare, |
2669 | .audio_shutdown = vc4_hdmi_audio_shutdown, |
2670 | .audio_startup = vc4_hdmi_audio_startup, |
2671 | }; |
2672 | |
2673 | static struct hdmi_codec_pdata vc4_hdmi_codec_pdata = { |
2674 | .ops = &vc4_hdmi_codec_ops, |
2675 | .max_i2s_channels = 8, |
2676 | .i2s = 1, |
2677 | }; |
2678 | |
2679 | static void vc4_hdmi_audio_codec_release(void *ptr) |
2680 | { |
2681 | struct vc4_hdmi *vc4_hdmi = ptr; |
2682 | |
2683 | platform_device_unregister(vc4_hdmi->audio.codec_pdev); |
2684 | vc4_hdmi->audio.codec_pdev = NULL; |
2685 | } |
2686 | |
2687 | static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) |
2688 | { |
2689 | const struct vc4_hdmi_register *mai_data = |
2690 | &vc4_hdmi->variant->registers[HDMI_MAI_DATA]; |
2691 | struct snd_soc_dai_link *dai_link = &vc4_hdmi->audio.link; |
2692 | struct snd_soc_card *card = &vc4_hdmi->audio.card; |
2693 | struct device *dev = &vc4_hdmi->pdev->dev; |
2694 | struct platform_device *codec_pdev; |
2695 | const __be32 *addr; |
2696 | int index, len; |
2697 | int ret; |
2698 | |
2699 | /* |
2700 | * ASoC makes it a bit hard to retrieve a pointer to the |
2701 | * vc4_hdmi structure. Registering the card will overwrite our |
2702 | * device drvdata with a pointer to the snd_soc_card structure, |
2703 | * which can then be used to retrieve whatever drvdata we want |
2704 | * to associate. |
2705 | * |
2706 | * However, that doesn't fly in the case where we wouldn't |
2707 | * register an ASoC card (because of an old DT that is missing |
2708 | * the dmas properties for example), then the card isn't |
2709 | * registered and the device drvdata wouldn't be set. |
2710 | * |
2711 | * We can deal with both cases by making sure a snd_soc_card |
2712 | * pointer and a vc4_hdmi structure are pointing to the same |
2713 | * memory address, so we can treat them indistinctly without any |
2714 | * issue. |
2715 | */ |
2716 | BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0); |
2717 | BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0); |
2718 | |
2719 | if (!of_find_property(np: dev->of_node, name: "dmas" , lenp: &len) || !len) { |
2720 | dev_warn(dev, |
2721 | "'dmas' DT property is missing or empty, no HDMI audio\n" ); |
2722 | return 0; |
2723 | } |
2724 | |
2725 | if (mai_data->reg != VC4_HD) { |
2726 | WARN_ONCE(true, "MAI isn't in the HD block\n" ); |
2727 | return -EINVAL; |
2728 | } |
2729 | |
2730 | /* |
2731 | * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve |
2732 | * the bus address specified in the DT, because the physical address |
2733 | * (the one returned by platform_get_resource()) is not appropriate |
2734 | * for DMA transfers. |
2735 | * This VC/MMU should probably be exposed to avoid this kind of hacks. |
2736 | */ |
2737 | index = of_property_match_string(np: dev->of_node, propname: "reg-names" , string: "hd" ); |
2738 | /* Before BCM2711, we don't have a named register range */ |
2739 | if (index < 0) |
2740 | index = 1; |
2741 | |
2742 | addr = of_get_address(dev: dev->of_node, index, NULL, NULL); |
2743 | |
2744 | vc4_hdmi->audio.dma_data.addr = be32_to_cpup(p: addr) + mai_data->offset; |
2745 | vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
2746 | vc4_hdmi->audio.dma_data.maxburst = 2; |
2747 | |
2748 | /* |
2749 | * NOTE: Strictly speaking, we should probably use a DRM-managed |
2750 | * registration there to avoid removing all the audio components |
2751 | * by the time the driver doesn't have any user anymore. |
2752 | * |
2753 | * However, the ASoC core uses a number of devm_kzalloc calls |
2754 | * when registering, even when using non-device-managed |
2755 | * functions (such as in snd_soc_register_component()). |
2756 | * |
2757 | * If we call snd_soc_unregister_component() in a DRM-managed |
2758 | * action, the device-managed actions have already been executed |
2759 | * and thus we would access memory that has been freed. |
2760 | * |
2761 | * Using device-managed hooks here probably leaves us open to a |
2762 | * bunch of issues if userspace still has a handle on the ALSA |
2763 | * device when the device is removed. However, this is mitigated |
2764 | * by the use of drm_dev_enter()/drm_dev_exit() in the audio |
2765 | * path to prevent the access to the device resources if it |
2766 | * isn't there anymore. |
2767 | * |
2768 | * Then, the vc4_hdmi structure is DRM-managed and thus only |
2769 | * freed whenever the last user has closed the DRM device file. |
2770 | * It should thus outlive ALSA in most situations. |
2771 | */ |
2772 | ret = devm_snd_dmaengine_pcm_register(dev, config: &pcm_conf, flags: 0); |
2773 | if (ret) { |
2774 | dev_err(dev, "Could not register PCM component: %d\n" , ret); |
2775 | return ret; |
2776 | } |
2777 | |
2778 | ret = devm_snd_soc_register_component(dev, component_driver: &vc4_hdmi_audio_cpu_dai_comp, |
2779 | dai_drv: &vc4_hdmi_audio_cpu_dai_drv, num_dai: 1); |
2780 | if (ret) { |
2781 | dev_err(dev, "Could not register CPU DAI: %d\n" , ret); |
2782 | return ret; |
2783 | } |
2784 | |
2785 | codec_pdev = platform_device_register_data(parent: dev, HDMI_CODEC_DRV_NAME, |
2786 | PLATFORM_DEVID_AUTO, |
2787 | data: &vc4_hdmi_codec_pdata, |
2788 | size: sizeof(vc4_hdmi_codec_pdata)); |
2789 | if (IS_ERR(ptr: codec_pdev)) { |
2790 | dev_err(dev, "Couldn't register the HDMI codec: %ld\n" , PTR_ERR(codec_pdev)); |
2791 | return PTR_ERR(ptr: codec_pdev); |
2792 | } |
2793 | vc4_hdmi->audio.codec_pdev = codec_pdev; |
2794 | |
2795 | ret = devm_add_action_or_reset(dev, vc4_hdmi_audio_codec_release, vc4_hdmi); |
2796 | if (ret) |
2797 | return ret; |
2798 | |
2799 | dai_link->cpus = &vc4_hdmi->audio.cpu; |
2800 | dai_link->codecs = &vc4_hdmi->audio.codec; |
2801 | dai_link->platforms = &vc4_hdmi->audio.platform; |
2802 | |
2803 | dai_link->num_cpus = 1; |
2804 | dai_link->num_codecs = 1; |
2805 | dai_link->num_platforms = 1; |
2806 | |
2807 | dai_link->name = "MAI" ; |
2808 | dai_link->stream_name = "MAI PCM" ; |
2809 | dai_link->codecs->dai_name = "i2s-hifi" ; |
2810 | dai_link->cpus->dai_name = dev_name(dev); |
2811 | dai_link->codecs->name = dev_name(dev: &codec_pdev->dev); |
2812 | dai_link->platforms->name = dev_name(dev); |
2813 | |
2814 | card->dai_link = dai_link; |
2815 | card->num_links = 1; |
2816 | card->name = vc4_hdmi->variant->card_name; |
2817 | card->driver_name = "vc4-hdmi" ; |
2818 | card->dev = dev; |
2819 | card->owner = THIS_MODULE; |
2820 | |
2821 | /* |
2822 | * Be careful, snd_soc_register_card() calls dev_set_drvdata() and |
2823 | * stores a pointer to the snd card object in dev->driver_data. This |
2824 | * means we cannot use it for something else. The hdmi back-pointer is |
2825 | * now stored in card->drvdata and should be retrieved with |
2826 | * snd_soc_card_get_drvdata() if needed. |
2827 | */ |
2828 | snd_soc_card_set_drvdata(card, data: vc4_hdmi); |
2829 | ret = devm_snd_soc_register_card(dev, card); |
2830 | if (ret) |
2831 | dev_err_probe(dev, err: ret, fmt: "Could not register sound card\n" ); |
2832 | |
2833 | return ret; |
2834 | |
2835 | } |
2836 | |
2837 | static irqreturn_t vc4_hdmi_hpd_irq_thread(int irq, void *priv) |
2838 | { |
2839 | struct vc4_hdmi *vc4_hdmi = priv; |
2840 | struct drm_connector *connector = &vc4_hdmi->connector; |
2841 | struct drm_device *dev = connector->dev; |
2842 | |
2843 | if (dev && dev->registered) |
2844 | drm_connector_helper_hpd_irq_event(connector); |
2845 | |
2846 | return IRQ_HANDLED; |
2847 | } |
2848 | |
2849 | static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi) |
2850 | { |
2851 | struct drm_connector *connector = &vc4_hdmi->connector; |
2852 | struct platform_device *pdev = vc4_hdmi->pdev; |
2853 | int ret; |
2854 | |
2855 | if (vc4_hdmi->variant->external_irq_controller) { |
2856 | unsigned int hpd_con = platform_get_irq_byname(pdev, "hpd-connected" ); |
2857 | unsigned int hpd_rm = platform_get_irq_byname(pdev, "hpd-removed" ); |
2858 | |
2859 | ret = devm_request_threaded_irq(dev: &pdev->dev, irq: hpd_con, |
2860 | NULL, |
2861 | thread_fn: vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, |
2862 | devname: "vc4 hdmi hpd connected" , dev_id: vc4_hdmi); |
2863 | if (ret) |
2864 | return ret; |
2865 | |
2866 | ret = devm_request_threaded_irq(dev: &pdev->dev, irq: hpd_rm, |
2867 | NULL, |
2868 | thread_fn: vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, |
2869 | devname: "vc4 hdmi hpd disconnected" , dev_id: vc4_hdmi); |
2870 | if (ret) |
2871 | return ret; |
2872 | |
2873 | connector->polled = DRM_CONNECTOR_POLL_HPD; |
2874 | } |
2875 | |
2876 | return 0; |
2877 | } |
2878 | |
2879 | #ifdef CONFIG_DRM_VC4_HDMI_CEC |
2880 | static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv) |
2881 | { |
2882 | struct vc4_hdmi *vc4_hdmi = priv; |
2883 | |
2884 | if (vc4_hdmi->cec_rx_msg.len) |
2885 | cec_received_msg(adap: vc4_hdmi->cec_adap, |
2886 | msg: &vc4_hdmi->cec_rx_msg); |
2887 | |
2888 | return IRQ_HANDLED; |
2889 | } |
2890 | |
2891 | static irqreturn_t vc4_cec_irq_handler_tx_thread(int irq, void *priv) |
2892 | { |
2893 | struct vc4_hdmi *vc4_hdmi = priv; |
2894 | |
2895 | if (vc4_hdmi->cec_tx_ok) { |
2896 | cec_transmit_done(adap: vc4_hdmi->cec_adap, CEC_TX_STATUS_OK, |
2897 | arb_lost_cnt: 0, nack_cnt: 0, low_drive_cnt: 0, error_cnt: 0); |
2898 | } else { |
2899 | /* |
2900 | * This CEC implementation makes 1 retry, so if we |
2901 | * get a NACK, then that means it made 2 attempts. |
2902 | */ |
2903 | cec_transmit_done(adap: vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK, |
2904 | arb_lost_cnt: 0, nack_cnt: 2, low_drive_cnt: 0, error_cnt: 0); |
2905 | } |
2906 | return IRQ_HANDLED; |
2907 | } |
2908 | |
2909 | static irqreturn_t vc4_cec_irq_handler_thread(int irq, void *priv) |
2910 | { |
2911 | struct vc4_hdmi *vc4_hdmi = priv; |
2912 | irqreturn_t ret; |
2913 | |
2914 | if (vc4_hdmi->cec_irq_was_rx) |
2915 | ret = vc4_cec_irq_handler_rx_thread(irq, priv); |
2916 | else |
2917 | ret = vc4_cec_irq_handler_tx_thread(irq, priv); |
2918 | |
2919 | return ret; |
2920 | } |
2921 | |
2922 | static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1) |
2923 | { |
2924 | struct drm_device *dev = vc4_hdmi->connector.dev; |
2925 | struct cec_msg *msg = &vc4_hdmi->cec_rx_msg; |
2926 | unsigned int i; |
2927 | |
2928 | lockdep_assert_held(&vc4_hdmi->hw_lock); |
2929 | |
2930 | msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >> |
2931 | VC4_HDMI_CEC_REC_WRD_CNT_SHIFT); |
2932 | |
2933 | if (msg->len > 16) { |
2934 | drm_err(dev, "Attempting to read too much data (%d)\n" , msg->len); |
2935 | return; |
2936 | } |
2937 | |
2938 | for (i = 0; i < msg->len; i += 4) { |
2939 | u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + (i >> 2)); |
2940 | |
2941 | msg->msg[i] = val & 0xff; |
2942 | msg->msg[i + 1] = (val >> 8) & 0xff; |
2943 | msg->msg[i + 2] = (val >> 16) & 0xff; |
2944 | msg->msg[i + 3] = (val >> 24) & 0xff; |
2945 | } |
2946 | } |
2947 | |
2948 | static irqreturn_t vc4_cec_irq_handler_tx_bare_locked(struct vc4_hdmi *vc4_hdmi) |
2949 | { |
2950 | u32 cntrl1; |
2951 | |
2952 | /* |
2953 | * We don't need to protect the register access using |
2954 | * drm_dev_enter() there because the interrupt handler lifetime |
2955 | * is tied to the device itself, and not to the DRM device. |
2956 | * |
2957 | * So when the device will be gone, one of the first thing we |
2958 | * will be doing will be to unregister the interrupt handler, |
2959 | * and then unregister the DRM device. drm_dev_enter() would |
2960 | * thus always succeed if we are here. |
2961 | */ |
2962 | |
2963 | lockdep_assert_held(&vc4_hdmi->hw_lock); |
2964 | |
2965 | cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); |
2966 | vc4_hdmi->cec_tx_ok = cntrl1 & VC4_HDMI_CEC_TX_STATUS_GOOD; |
2967 | cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN; |
2968 | HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1); |
2969 | |
2970 | return IRQ_WAKE_THREAD; |
2971 | } |
2972 | |
2973 | static irqreturn_t vc4_cec_irq_handler_tx_bare(int irq, void *priv) |
2974 | { |
2975 | struct vc4_hdmi *vc4_hdmi = priv; |
2976 | irqreturn_t ret; |
2977 | |
2978 | spin_lock(lock: &vc4_hdmi->hw_lock); |
2979 | ret = vc4_cec_irq_handler_tx_bare_locked(vc4_hdmi); |
2980 | spin_unlock(lock: &vc4_hdmi->hw_lock); |
2981 | |
2982 | return ret; |
2983 | } |
2984 | |
2985 | static irqreturn_t vc4_cec_irq_handler_rx_bare_locked(struct vc4_hdmi *vc4_hdmi) |
2986 | { |
2987 | u32 cntrl1; |
2988 | |
2989 | lockdep_assert_held(&vc4_hdmi->hw_lock); |
2990 | |
2991 | /* |
2992 | * We don't need to protect the register access using |
2993 | * drm_dev_enter() there because the interrupt handler lifetime |
2994 | * is tied to the device itself, and not to the DRM device. |
2995 | * |
2996 | * So when the device will be gone, one of the first thing we |
2997 | * will be doing will be to unregister the interrupt handler, |
2998 | * and then unregister the DRM device. drm_dev_enter() would |
2999 | * thus always succeed if we are here. |
3000 | */ |
3001 | |
3002 | vc4_hdmi->cec_rx_msg.len = 0; |
3003 | cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); |
3004 | vc4_cec_read_msg(vc4_hdmi, cntrl1); |
3005 | cntrl1 |= VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; |
3006 | HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1); |
3007 | cntrl1 &= ~VC4_HDMI_CEC_CLEAR_RECEIVE_OFF; |
3008 | |
3009 | HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1); |
3010 | |
3011 | return IRQ_WAKE_THREAD; |
3012 | } |
3013 | |
3014 | static irqreturn_t vc4_cec_irq_handler_rx_bare(int irq, void *priv) |
3015 | { |
3016 | struct vc4_hdmi *vc4_hdmi = priv; |
3017 | irqreturn_t ret; |
3018 | |
3019 | spin_lock(lock: &vc4_hdmi->hw_lock); |
3020 | ret = vc4_cec_irq_handler_rx_bare_locked(vc4_hdmi); |
3021 | spin_unlock(lock: &vc4_hdmi->hw_lock); |
3022 | |
3023 | return ret; |
3024 | } |
3025 | |
3026 | static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) |
3027 | { |
3028 | struct vc4_hdmi *vc4_hdmi = priv; |
3029 | u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS); |
3030 | irqreturn_t ret; |
3031 | u32 cntrl5; |
3032 | |
3033 | /* |
3034 | * We don't need to protect the register access using |
3035 | * drm_dev_enter() there because the interrupt handler lifetime |
3036 | * is tied to the device itself, and not to the DRM device. |
3037 | * |
3038 | * So when the device will be gone, one of the first thing we |
3039 | * will be doing will be to unregister the interrupt handler, |
3040 | * and then unregister the DRM device. drm_dev_enter() would |
3041 | * thus always succeed if we are here. |
3042 | */ |
3043 | |
3044 | if (!(stat & VC4_HDMI_CPU_CEC)) |
3045 | return IRQ_NONE; |
3046 | |
3047 | spin_lock(lock: &vc4_hdmi->hw_lock); |
3048 | cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5); |
3049 | vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT; |
3050 | if (vc4_hdmi->cec_irq_was_rx) |
3051 | ret = vc4_cec_irq_handler_rx_bare_locked(vc4_hdmi); |
3052 | else |
3053 | ret = vc4_cec_irq_handler_tx_bare_locked(vc4_hdmi); |
3054 | |
3055 | HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC); |
3056 | spin_unlock(lock: &vc4_hdmi->hw_lock); |
3057 | |
3058 | return ret; |
3059 | } |
3060 | |
3061 | static int vc4_hdmi_cec_enable(struct cec_adapter *adap) |
3062 | { |
3063 | struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); |
3064 | struct drm_device *drm = vc4_hdmi->connector.dev; |
3065 | /* clock period in microseconds */ |
3066 | const u32 usecs = 1000000 / CEC_CLOCK_FREQ; |
3067 | unsigned long flags; |
3068 | u32 val; |
3069 | int ret; |
3070 | int idx; |
3071 | |
3072 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
3073 | /* |
3074 | * We can't return an error code, because the CEC |
3075 | * framework will emit WARN_ON messages at unbind |
3076 | * otherwise. |
3077 | */ |
3078 | return 0; |
3079 | |
3080 | ret = pm_runtime_resume_and_get(dev: &vc4_hdmi->pdev->dev); |
3081 | if (ret) { |
3082 | drm_dev_exit(idx); |
3083 | return ret; |
3084 | } |
3085 | |
3086 | mutex_lock(&vc4_hdmi->mutex); |
3087 | |
3088 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
3089 | |
3090 | val = HDMI_READ(HDMI_CEC_CNTRL_5); |
3091 | val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET | |
3092 | VC4_HDMI_CEC_CNT_TO_4700_US_MASK | |
3093 | VC4_HDMI_CEC_CNT_TO_4500_US_MASK); |
3094 | val |= ((4700 / usecs) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT) | |
3095 | ((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT); |
3096 | |
3097 | HDMI_WRITE(HDMI_CEC_CNTRL_5, val | |
3098 | VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET); |
3099 | HDMI_WRITE(HDMI_CEC_CNTRL_5, val); |
3100 | HDMI_WRITE(HDMI_CEC_CNTRL_2, |
3101 | ((1500 / usecs) << VC4_HDMI_CEC_CNT_TO_1500_US_SHIFT) | |
3102 | ((1300 / usecs) << VC4_HDMI_CEC_CNT_TO_1300_US_SHIFT) | |
3103 | ((800 / usecs) << VC4_HDMI_CEC_CNT_TO_800_US_SHIFT) | |
3104 | ((600 / usecs) << VC4_HDMI_CEC_CNT_TO_600_US_SHIFT) | |
3105 | ((400 / usecs) << VC4_HDMI_CEC_CNT_TO_400_US_SHIFT)); |
3106 | HDMI_WRITE(HDMI_CEC_CNTRL_3, |
3107 | ((2750 / usecs) << VC4_HDMI_CEC_CNT_TO_2750_US_SHIFT) | |
3108 | ((2400 / usecs) << VC4_HDMI_CEC_CNT_TO_2400_US_SHIFT) | |
3109 | ((2050 / usecs) << VC4_HDMI_CEC_CNT_TO_2050_US_SHIFT) | |
3110 | ((1700 / usecs) << VC4_HDMI_CEC_CNT_TO_1700_US_SHIFT)); |
3111 | HDMI_WRITE(HDMI_CEC_CNTRL_4, |
3112 | ((4300 / usecs) << VC4_HDMI_CEC_CNT_TO_4300_US_SHIFT) | |
3113 | ((3900 / usecs) << VC4_HDMI_CEC_CNT_TO_3900_US_SHIFT) | |
3114 | ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) | |
3115 | ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT)); |
3116 | |
3117 | if (!vc4_hdmi->variant->external_irq_controller) |
3118 | HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC); |
3119 | |
3120 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
3121 | |
3122 | mutex_unlock(lock: &vc4_hdmi->mutex); |
3123 | drm_dev_exit(idx); |
3124 | |
3125 | return 0; |
3126 | } |
3127 | |
3128 | static int vc4_hdmi_cec_disable(struct cec_adapter *adap) |
3129 | { |
3130 | struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); |
3131 | struct drm_device *drm = vc4_hdmi->connector.dev; |
3132 | unsigned long flags; |
3133 | int idx; |
3134 | |
3135 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
3136 | /* |
3137 | * We can't return an error code, because the CEC |
3138 | * framework will emit WARN_ON messages at unbind |
3139 | * otherwise. |
3140 | */ |
3141 | return 0; |
3142 | |
3143 | mutex_lock(&vc4_hdmi->mutex); |
3144 | |
3145 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
3146 | |
3147 | if (!vc4_hdmi->variant->external_irq_controller) |
3148 | HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC); |
3149 | |
3150 | HDMI_WRITE(HDMI_CEC_CNTRL_5, HDMI_READ(HDMI_CEC_CNTRL_5) | |
3151 | VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET); |
3152 | |
3153 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
3154 | |
3155 | mutex_unlock(lock: &vc4_hdmi->mutex); |
3156 | |
3157 | pm_runtime_put(dev: &vc4_hdmi->pdev->dev); |
3158 | |
3159 | drm_dev_exit(idx); |
3160 | |
3161 | return 0; |
3162 | } |
3163 | |
3164 | static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) |
3165 | { |
3166 | if (enable) |
3167 | return vc4_hdmi_cec_enable(adap); |
3168 | else |
3169 | return vc4_hdmi_cec_disable(adap); |
3170 | } |
3171 | |
3172 | static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) |
3173 | { |
3174 | struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); |
3175 | struct drm_device *drm = vc4_hdmi->connector.dev; |
3176 | unsigned long flags; |
3177 | int idx; |
3178 | |
3179 | if (!drm_dev_enter(dev: drm, idx: &idx)) |
3180 | /* |
3181 | * We can't return an error code, because the CEC |
3182 | * framework will emit WARN_ON messages at unbind |
3183 | * otherwise. |
3184 | */ |
3185 | return 0; |
3186 | |
3187 | mutex_lock(&vc4_hdmi->mutex); |
3188 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
3189 | HDMI_WRITE(HDMI_CEC_CNTRL_1, |
3190 | (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) | |
3191 | (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT); |
3192 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
3193 | mutex_unlock(lock: &vc4_hdmi->mutex); |
3194 | |
3195 | drm_dev_exit(idx); |
3196 | |
3197 | return 0; |
3198 | } |
3199 | |
3200 | static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, |
3201 | u32 signal_free_time, struct cec_msg *msg) |
3202 | { |
3203 | struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); |
3204 | struct drm_device *dev = vc4_hdmi->connector.dev; |
3205 | unsigned long flags; |
3206 | u32 val; |
3207 | unsigned int i; |
3208 | int idx; |
3209 | |
3210 | if (!drm_dev_enter(dev, idx: &idx)) |
3211 | return -ENODEV; |
3212 | |
3213 | if (msg->len > 16) { |
3214 | drm_err(dev, "Attempting to transmit too much data (%d)\n" , msg->len); |
3215 | drm_dev_exit(idx); |
3216 | return -ENOMEM; |
3217 | } |
3218 | |
3219 | mutex_lock(&vc4_hdmi->mutex); |
3220 | |
3221 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
3222 | |
3223 | for (i = 0; i < msg->len; i += 4) |
3224 | HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i >> 2), |
3225 | (msg->msg[i]) | |
3226 | (msg->msg[i + 1] << 8) | |
3227 | (msg->msg[i + 2] << 16) | |
3228 | (msg->msg[i + 3] << 24)); |
3229 | |
3230 | val = HDMI_READ(HDMI_CEC_CNTRL_1); |
3231 | val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN; |
3232 | HDMI_WRITE(HDMI_CEC_CNTRL_1, val); |
3233 | val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK; |
3234 | val |= (msg->len - 1) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT; |
3235 | val |= VC4_HDMI_CEC_START_XMIT_BEGIN; |
3236 | |
3237 | HDMI_WRITE(HDMI_CEC_CNTRL_1, val); |
3238 | |
3239 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
3240 | mutex_unlock(lock: &vc4_hdmi->mutex); |
3241 | drm_dev_exit(idx); |
3242 | |
3243 | return 0; |
3244 | } |
3245 | |
3246 | static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = { |
3247 | .adap_enable = vc4_hdmi_cec_adap_enable, |
3248 | .adap_log_addr = vc4_hdmi_cec_adap_log_addr, |
3249 | .adap_transmit = vc4_hdmi_cec_adap_transmit, |
3250 | }; |
3251 | |
3252 | static void vc4_hdmi_cec_release(void *ptr) |
3253 | { |
3254 | struct vc4_hdmi *vc4_hdmi = ptr; |
3255 | |
3256 | cec_unregister_adapter(adap: vc4_hdmi->cec_adap); |
3257 | vc4_hdmi->cec_adap = NULL; |
3258 | } |
3259 | |
3260 | static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) |
3261 | { |
3262 | struct cec_connector_info conn_info; |
3263 | struct platform_device *pdev = vc4_hdmi->pdev; |
3264 | struct device *dev = &pdev->dev; |
3265 | int ret; |
3266 | |
3267 | if (!of_property_present(np: dev->of_node, propname: "interrupts" )) { |
3268 | dev_warn(dev, "'interrupts' DT property is missing, no CEC\n" ); |
3269 | return 0; |
3270 | } |
3271 | |
3272 | vc4_hdmi->cec_adap = cec_allocate_adapter(ops: &vc4_hdmi_cec_adap_ops, |
3273 | priv: vc4_hdmi, |
3274 | name: vc4_hdmi->variant->card_name, |
3275 | CEC_CAP_DEFAULTS | |
3276 | CEC_CAP_CONNECTOR_INFO, available_las: 1); |
3277 | ret = PTR_ERR_OR_ZERO(ptr: vc4_hdmi->cec_adap); |
3278 | if (ret < 0) |
3279 | return ret; |
3280 | |
3281 | cec_fill_conn_info_from_drm(conn_info: &conn_info, connector: &vc4_hdmi->connector); |
3282 | cec_s_conn_info(adap: vc4_hdmi->cec_adap, conn_info: &conn_info); |
3283 | |
3284 | if (vc4_hdmi->variant->external_irq_controller) { |
3285 | ret = devm_request_threaded_irq(dev, irq: platform_get_irq_byname(pdev, "cec-rx" ), |
3286 | handler: vc4_cec_irq_handler_rx_bare, |
3287 | thread_fn: vc4_cec_irq_handler_rx_thread, irqflags: 0, |
3288 | devname: "vc4 hdmi cec rx" , dev_id: vc4_hdmi); |
3289 | if (ret) |
3290 | goto err_delete_cec_adap; |
3291 | |
3292 | ret = devm_request_threaded_irq(dev, irq: platform_get_irq_byname(pdev, "cec-tx" ), |
3293 | handler: vc4_cec_irq_handler_tx_bare, |
3294 | thread_fn: vc4_cec_irq_handler_tx_thread, irqflags: 0, |
3295 | devname: "vc4 hdmi cec tx" , dev_id: vc4_hdmi); |
3296 | if (ret) |
3297 | goto err_delete_cec_adap; |
3298 | } else { |
3299 | ret = devm_request_threaded_irq(dev, irq: platform_get_irq(pdev, 0), |
3300 | handler: vc4_cec_irq_handler, |
3301 | thread_fn: vc4_cec_irq_handler_thread, irqflags: 0, |
3302 | devname: "vc4 hdmi cec" , dev_id: vc4_hdmi); |
3303 | if (ret) |
3304 | goto err_delete_cec_adap; |
3305 | } |
3306 | |
3307 | ret = cec_register_adapter(adap: vc4_hdmi->cec_adap, parent: &pdev->dev); |
3308 | if (ret < 0) |
3309 | goto err_delete_cec_adap; |
3310 | |
3311 | /* |
3312 | * NOTE: Strictly speaking, we should probably use a DRM-managed |
3313 | * registration there to avoid removing the CEC adapter by the |
3314 | * time the DRM driver doesn't have any user anymore. |
3315 | * |
3316 | * However, the CEC framework already cleans up the CEC adapter |
3317 | * only when the last user has closed its file descriptor, so we |
3318 | * don't need to handle it in DRM. |
3319 | * |
3320 | * By the time the device-managed hook is executed, we will give |
3321 | * up our reference to the CEC adapter and therefore don't |
3322 | * really care when it's actually freed. |
3323 | * |
3324 | * There's still a problematic sequence: if we unregister our |
3325 | * CEC adapter, but the userspace keeps a handle on the CEC |
3326 | * adapter but not the DRM device for some reason. In such a |
3327 | * case, our vc4_hdmi structure will be freed, but the |
3328 | * cec_adapter structure will have a dangling pointer to what |
3329 | * used to be our HDMI controller. If we get a CEC call at that |
3330 | * moment, we could end up with a use-after-free. Fortunately, |
3331 | * the CEC framework already handles this too, by calling |
3332 | * cec_is_registered() in cec_ioctl() and cec_poll(). |
3333 | */ |
3334 | ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi); |
3335 | if (ret) |
3336 | return ret; |
3337 | |
3338 | return 0; |
3339 | |
3340 | err_delete_cec_adap: |
3341 | cec_delete_adapter(adap: vc4_hdmi->cec_adap); |
3342 | |
3343 | return ret; |
3344 | } |
3345 | #else |
3346 | static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) |
3347 | { |
3348 | return 0; |
3349 | } |
3350 | #endif |
3351 | |
3352 | static void vc4_hdmi_free_regset(struct drm_device *drm, void *ptr) |
3353 | { |
3354 | struct debugfs_reg32 *regs = ptr; |
3355 | |
3356 | kfree(objp: regs); |
3357 | } |
3358 | |
3359 | static int vc4_hdmi_build_regset(struct drm_device *drm, |
3360 | struct vc4_hdmi *vc4_hdmi, |
3361 | struct debugfs_regset32 *regset, |
3362 | enum vc4_hdmi_regs reg) |
3363 | { |
3364 | const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; |
3365 | struct debugfs_reg32 *regs, *new_regs; |
3366 | unsigned int count = 0; |
3367 | unsigned int i; |
3368 | int ret; |
3369 | |
3370 | regs = kcalloc(n: variant->num_registers, size: sizeof(*regs), |
3371 | GFP_KERNEL); |
3372 | if (!regs) |
3373 | return -ENOMEM; |
3374 | |
3375 | for (i = 0; i < variant->num_registers; i++) { |
3376 | const struct vc4_hdmi_register *field = &variant->registers[i]; |
3377 | |
3378 | if (field->reg != reg) |
3379 | continue; |
3380 | |
3381 | regs[count].name = field->name; |
3382 | regs[count].offset = field->offset; |
3383 | count++; |
3384 | } |
3385 | |
3386 | new_regs = krealloc(objp: regs, new_size: count * sizeof(*regs), GFP_KERNEL); |
3387 | if (!new_regs) |
3388 | return -ENOMEM; |
3389 | |
3390 | regset->base = __vc4_hdmi_get_field_base(hdmi: vc4_hdmi, reg); |
3391 | regset->regs = new_regs; |
3392 | regset->nregs = count; |
3393 | |
3394 | ret = drmm_add_action_or_reset(drm, vc4_hdmi_free_regset, new_regs); |
3395 | if (ret) |
3396 | return ret; |
3397 | |
3398 | return 0; |
3399 | } |
3400 | |
3401 | static int vc4_hdmi_init_resources(struct drm_device *drm, |
3402 | struct vc4_hdmi *vc4_hdmi) |
3403 | { |
3404 | struct platform_device *pdev = vc4_hdmi->pdev; |
3405 | struct device *dev = &pdev->dev; |
3406 | int ret; |
3407 | |
3408 | vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(dev: pdev, index: 0); |
3409 | if (IS_ERR(ptr: vc4_hdmi->hdmicore_regs)) |
3410 | return PTR_ERR(ptr: vc4_hdmi->hdmicore_regs); |
3411 | |
3412 | vc4_hdmi->hd_regs = vc4_ioremap_regs(dev: pdev, index: 1); |
3413 | if (IS_ERR(ptr: vc4_hdmi->hd_regs)) |
3414 | return PTR_ERR(ptr: vc4_hdmi->hd_regs); |
3415 | |
3416 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->hd_regset, reg: VC4_HD); |
3417 | if (ret) |
3418 | return ret; |
3419 | |
3420 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->hdmi_regset, reg: VC4_HDMI); |
3421 | if (ret) |
3422 | return ret; |
3423 | |
3424 | vc4_hdmi->pixel_clock = devm_clk_get(dev, id: "pixel" ); |
3425 | if (IS_ERR(ptr: vc4_hdmi->pixel_clock)) { |
3426 | ret = PTR_ERR(ptr: vc4_hdmi->pixel_clock); |
3427 | if (ret != -EPROBE_DEFER) |
3428 | DRM_ERROR("Failed to get pixel clock\n" ); |
3429 | return ret; |
3430 | } |
3431 | |
3432 | vc4_hdmi->hsm_clock = devm_clk_get(dev, id: "hdmi" ); |
3433 | if (IS_ERR(ptr: vc4_hdmi->hsm_clock)) { |
3434 | DRM_ERROR("Failed to get HDMI state machine clock\n" ); |
3435 | return PTR_ERR(ptr: vc4_hdmi->hsm_clock); |
3436 | } |
3437 | vc4_hdmi->audio_clock = vc4_hdmi->hsm_clock; |
3438 | vc4_hdmi->cec_clock = vc4_hdmi->hsm_clock; |
3439 | |
3440 | return 0; |
3441 | } |
3442 | |
3443 | static int vc5_hdmi_init_resources(struct drm_device *drm, |
3444 | struct vc4_hdmi *vc4_hdmi) |
3445 | { |
3446 | struct platform_device *pdev = vc4_hdmi->pdev; |
3447 | struct device *dev = &pdev->dev; |
3448 | struct resource *res; |
3449 | int ret; |
3450 | |
3451 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi" ); |
3452 | if (!res) |
3453 | return -ENODEV; |
3454 | |
3455 | vc4_hdmi->hdmicore_regs = devm_ioremap(dev, offset: res->start, |
3456 | size: resource_size(res)); |
3457 | if (!vc4_hdmi->hdmicore_regs) |
3458 | return -ENOMEM; |
3459 | |
3460 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hd" ); |
3461 | if (!res) |
3462 | return -ENODEV; |
3463 | |
3464 | vc4_hdmi->hd_regs = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
3465 | if (!vc4_hdmi->hd_regs) |
3466 | return -ENOMEM; |
3467 | |
3468 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cec" ); |
3469 | if (!res) |
3470 | return -ENODEV; |
3471 | |
3472 | vc4_hdmi->cec_regs = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
3473 | if (!vc4_hdmi->cec_regs) |
3474 | return -ENOMEM; |
3475 | |
3476 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csc" ); |
3477 | if (!res) |
3478 | return -ENODEV; |
3479 | |
3480 | vc4_hdmi->csc_regs = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
3481 | if (!vc4_hdmi->csc_regs) |
3482 | return -ENOMEM; |
3483 | |
3484 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dvp" ); |
3485 | if (!res) |
3486 | return -ENODEV; |
3487 | |
3488 | vc4_hdmi->dvp_regs = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
3489 | if (!vc4_hdmi->dvp_regs) |
3490 | return -ENOMEM; |
3491 | |
3492 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy" ); |
3493 | if (!res) |
3494 | return -ENODEV; |
3495 | |
3496 | vc4_hdmi->phy_regs = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
3497 | if (!vc4_hdmi->phy_regs) |
3498 | return -ENOMEM; |
3499 | |
3500 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "packet" ); |
3501 | if (!res) |
3502 | return -ENODEV; |
3503 | |
3504 | vc4_hdmi->ram_regs = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
3505 | if (!vc4_hdmi->ram_regs) |
3506 | return -ENOMEM; |
3507 | |
3508 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rm" ); |
3509 | if (!res) |
3510 | return -ENODEV; |
3511 | |
3512 | vc4_hdmi->rm_regs = devm_ioremap(dev, offset: res->start, size: resource_size(res)); |
3513 | if (!vc4_hdmi->rm_regs) |
3514 | return -ENOMEM; |
3515 | |
3516 | vc4_hdmi->hsm_clock = devm_clk_get(dev, id: "hdmi" ); |
3517 | if (IS_ERR(ptr: vc4_hdmi->hsm_clock)) { |
3518 | DRM_ERROR("Failed to get HDMI state machine clock\n" ); |
3519 | return PTR_ERR(ptr: vc4_hdmi->hsm_clock); |
3520 | } |
3521 | |
3522 | vc4_hdmi->pixel_bvb_clock = devm_clk_get(dev, id: "bvb" ); |
3523 | if (IS_ERR(ptr: vc4_hdmi->pixel_bvb_clock)) { |
3524 | DRM_ERROR("Failed to get pixel bvb clock\n" ); |
3525 | return PTR_ERR(ptr: vc4_hdmi->pixel_bvb_clock); |
3526 | } |
3527 | |
3528 | vc4_hdmi->audio_clock = devm_clk_get(dev, id: "audio" ); |
3529 | if (IS_ERR(ptr: vc4_hdmi->audio_clock)) { |
3530 | DRM_ERROR("Failed to get audio clock\n" ); |
3531 | return PTR_ERR(ptr: vc4_hdmi->audio_clock); |
3532 | } |
3533 | |
3534 | vc4_hdmi->cec_clock = devm_clk_get(dev, id: "cec" ); |
3535 | if (IS_ERR(ptr: vc4_hdmi->cec_clock)) { |
3536 | DRM_ERROR("Failed to get CEC clock\n" ); |
3537 | return PTR_ERR(ptr: vc4_hdmi->cec_clock); |
3538 | } |
3539 | |
3540 | vc4_hdmi->reset = devm_reset_control_get(dev, NULL); |
3541 | if (IS_ERR(ptr: vc4_hdmi->reset)) { |
3542 | DRM_ERROR("Failed to get HDMI reset line\n" ); |
3543 | return PTR_ERR(ptr: vc4_hdmi->reset); |
3544 | } |
3545 | |
3546 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->hdmi_regset, reg: VC4_HDMI); |
3547 | if (ret) |
3548 | return ret; |
3549 | |
3550 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->hd_regset, reg: VC4_HD); |
3551 | if (ret) |
3552 | return ret; |
3553 | |
3554 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->cec_regset, reg: VC5_CEC); |
3555 | if (ret) |
3556 | return ret; |
3557 | |
3558 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->csc_regset, reg: VC5_CSC); |
3559 | if (ret) |
3560 | return ret; |
3561 | |
3562 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->dvp_regset, reg: VC5_DVP); |
3563 | if (ret) |
3564 | return ret; |
3565 | |
3566 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->phy_regset, reg: VC5_PHY); |
3567 | if (ret) |
3568 | return ret; |
3569 | |
3570 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->ram_regset, reg: VC5_RAM); |
3571 | if (ret) |
3572 | return ret; |
3573 | |
3574 | ret = vc4_hdmi_build_regset(drm, vc4_hdmi, regset: &vc4_hdmi->rm_regset, reg: VC5_RM); |
3575 | if (ret) |
3576 | return ret; |
3577 | |
3578 | return 0; |
3579 | } |
3580 | |
3581 | static int vc4_hdmi_runtime_suspend(struct device *dev) |
3582 | { |
3583 | struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); |
3584 | |
3585 | clk_disable_unprepare(clk: vc4_hdmi->hsm_clock); |
3586 | |
3587 | return 0; |
3588 | } |
3589 | |
3590 | static int vc4_hdmi_runtime_resume(struct device *dev) |
3591 | { |
3592 | struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); |
3593 | unsigned long __maybe_unused flags; |
3594 | u32 __maybe_unused value; |
3595 | unsigned long rate; |
3596 | int ret; |
3597 | |
3598 | ret = clk_prepare_enable(clk: vc4_hdmi->hsm_clock); |
3599 | if (ret) |
3600 | return ret; |
3601 | |
3602 | /* |
3603 | * Whenever the RaspberryPi boots without an HDMI monitor |
3604 | * plugged in, the firmware won't have initialized the HSM clock |
3605 | * rate and it will be reported as 0. |
3606 | * |
3607 | * If we try to access a register of the controller in such a |
3608 | * case, it will lead to a silent CPU stall. Let's make sure we |
3609 | * prevent such a case. |
3610 | */ |
3611 | rate = clk_get_rate(clk: vc4_hdmi->hsm_clock); |
3612 | if (!rate) { |
3613 | ret = -EINVAL; |
3614 | goto err_disable_clk; |
3615 | } |
3616 | |
3617 | if (vc4_hdmi->variant->reset) |
3618 | vc4_hdmi->variant->reset(vc4_hdmi); |
3619 | |
3620 | #ifdef CONFIG_DRM_VC4_HDMI_CEC |
3621 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
3622 | value = HDMI_READ(HDMI_CEC_CNTRL_1); |
3623 | /* Set the logical address to Unregistered */ |
3624 | value |= VC4_HDMI_CEC_ADDR_MASK; |
3625 | HDMI_WRITE(HDMI_CEC_CNTRL_1, value); |
3626 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
3627 | |
3628 | vc4_hdmi_cec_update_clk_div(vc4_hdmi); |
3629 | |
3630 | if (!vc4_hdmi->variant->external_irq_controller) { |
3631 | spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); |
3632 | HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff); |
3633 | spin_unlock_irqrestore(lock: &vc4_hdmi->hw_lock, flags); |
3634 | } |
3635 | #endif |
3636 | |
3637 | return 0; |
3638 | |
3639 | err_disable_clk: |
3640 | clk_disable_unprepare(clk: vc4_hdmi->hsm_clock); |
3641 | return ret; |
3642 | } |
3643 | |
3644 | static void vc4_hdmi_put_ddc_device(void *ptr) |
3645 | { |
3646 | struct vc4_hdmi *vc4_hdmi = ptr; |
3647 | |
3648 | put_device(dev: &vc4_hdmi->ddc->dev); |
3649 | } |
3650 | |
3651 | static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) |
3652 | { |
3653 | const struct vc4_hdmi_variant *variant = of_device_get_match_data(dev); |
3654 | struct platform_device *pdev = to_platform_device(dev); |
3655 | struct drm_device *drm = dev_get_drvdata(dev: master); |
3656 | struct vc4_hdmi *vc4_hdmi; |
3657 | struct drm_encoder *encoder; |
3658 | struct device_node *ddc_node; |
3659 | int ret; |
3660 | |
3661 | vc4_hdmi = drmm_kzalloc(dev: drm, size: sizeof(*vc4_hdmi), GFP_KERNEL); |
3662 | if (!vc4_hdmi) |
3663 | return -ENOMEM; |
3664 | |
3665 | ret = drmm_mutex_init(drm, &vc4_hdmi->mutex); |
3666 | if (ret) |
3667 | return ret; |
3668 | |
3669 | spin_lock_init(&vc4_hdmi->hw_lock); |
3670 | INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq); |
3671 | |
3672 | dev_set_drvdata(dev, data: vc4_hdmi); |
3673 | encoder = &vc4_hdmi->encoder.base; |
3674 | vc4_hdmi->encoder.type = variant->encoder_type; |
3675 | vc4_hdmi->encoder.pre_crtc_configure = vc4_hdmi_encoder_pre_crtc_configure; |
3676 | vc4_hdmi->encoder.pre_crtc_enable = vc4_hdmi_encoder_pre_crtc_enable; |
3677 | vc4_hdmi->encoder.post_crtc_enable = vc4_hdmi_encoder_post_crtc_enable; |
3678 | vc4_hdmi->encoder.post_crtc_disable = vc4_hdmi_encoder_post_crtc_disable; |
3679 | vc4_hdmi->encoder.post_crtc_powerdown = vc4_hdmi_encoder_post_crtc_powerdown; |
3680 | vc4_hdmi->pdev = pdev; |
3681 | vc4_hdmi->variant = variant; |
3682 | |
3683 | /* |
3684 | * Since we don't know the state of the controller and its |
3685 | * display (if any), let's assume it's always enabled. |
3686 | * vc4_hdmi_disable_scrambling() will thus run at boot, make |
3687 | * sure it's disabled, and avoid any inconsistency. |
3688 | */ |
3689 | if (variant->max_pixel_clock > HDMI_14_MAX_TMDS_CLK) |
3690 | vc4_hdmi->scdc_enabled = true; |
3691 | |
3692 | ret = variant->init_resources(drm, vc4_hdmi); |
3693 | if (ret) |
3694 | return ret; |
3695 | |
3696 | ddc_node = of_parse_phandle(np: dev->of_node, phandle_name: "ddc" , index: 0); |
3697 | if (!ddc_node) { |
3698 | DRM_ERROR("Failed to find ddc node in device tree\n" ); |
3699 | return -ENODEV; |
3700 | } |
3701 | |
3702 | vc4_hdmi->ddc = of_find_i2c_adapter_by_node(node: ddc_node); |
3703 | of_node_put(node: ddc_node); |
3704 | if (!vc4_hdmi->ddc) { |
3705 | DRM_DEBUG("Failed to get ddc i2c adapter by node\n" ); |
3706 | return -EPROBE_DEFER; |
3707 | } |
3708 | |
3709 | ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc_device, vc4_hdmi); |
3710 | if (ret) |
3711 | return ret; |
3712 | |
3713 | /* Only use the GPIO HPD pin if present in the DT, otherwise |
3714 | * we'll use the HDMI core's register. |
3715 | */ |
3716 | vc4_hdmi->hpd_gpio = devm_gpiod_get_optional(dev, con_id: "hpd" , flags: GPIOD_IN); |
3717 | if (IS_ERR(ptr: vc4_hdmi->hpd_gpio)) { |
3718 | return PTR_ERR(ptr: vc4_hdmi->hpd_gpio); |
3719 | } |
3720 | |
3721 | vc4_hdmi->disable_wifi_frequencies = |
3722 | of_property_read_bool(np: dev->of_node, propname: "wifi-2.4ghz-coexistence" ); |
3723 | |
3724 | ret = devm_pm_runtime_enable(dev); |
3725 | if (ret) |
3726 | return ret; |
3727 | |
3728 | /* |
3729 | * We need to have the device powered up at this point to call |
3730 | * our reset hook and for the CEC init. |
3731 | */ |
3732 | ret = pm_runtime_resume_and_get(dev); |
3733 | if (ret) |
3734 | return ret; |
3735 | |
3736 | if ((of_device_is_compatible(device: dev->of_node, "brcm,bcm2711-hdmi0" ) || |
3737 | of_device_is_compatible(device: dev->of_node, "brcm,bcm2711-hdmi1" )) && |
3738 | HDMI_READ(HDMI_VID_CTL) & VC4_HD_VID_CTL_ENABLE) { |
3739 | clk_prepare_enable(clk: vc4_hdmi->pixel_clock); |
3740 | clk_prepare_enable(clk: vc4_hdmi->hsm_clock); |
3741 | clk_prepare_enable(clk: vc4_hdmi->pixel_bvb_clock); |
3742 | } |
3743 | |
3744 | ret = drmm_encoder_init(dev: drm, encoder, |
3745 | funcs: &vc4_hdmi_encoder_funcs, |
3746 | DRM_MODE_ENCODER_TMDS, |
3747 | NULL); |
3748 | if (ret) |
3749 | goto err_put_runtime_pm; |
3750 | |
3751 | drm_encoder_helper_add(encoder, funcs: &vc4_hdmi_encoder_helper_funcs); |
3752 | |
3753 | ret = vc4_hdmi_connector_init(dev: drm, vc4_hdmi); |
3754 | if (ret) |
3755 | goto err_put_runtime_pm; |
3756 | |
3757 | ret = vc4_hdmi_hotplug_init(vc4_hdmi); |
3758 | if (ret) |
3759 | goto err_put_runtime_pm; |
3760 | |
3761 | ret = vc4_hdmi_cec_init(vc4_hdmi); |
3762 | if (ret) |
3763 | goto err_put_runtime_pm; |
3764 | |
3765 | ret = vc4_hdmi_audio_init(vc4_hdmi); |
3766 | if (ret) |
3767 | goto err_put_runtime_pm; |
3768 | |
3769 | pm_runtime_put_sync(dev); |
3770 | |
3771 | return 0; |
3772 | |
3773 | err_put_runtime_pm: |
3774 | pm_runtime_put_sync(dev); |
3775 | |
3776 | return ret; |
3777 | } |
3778 | |
3779 | static const struct component_ops vc4_hdmi_ops = { |
3780 | .bind = vc4_hdmi_bind, |
3781 | }; |
3782 | |
3783 | static int vc4_hdmi_dev_probe(struct platform_device *pdev) |
3784 | { |
3785 | return component_add(&pdev->dev, &vc4_hdmi_ops); |
3786 | } |
3787 | |
3788 | static void vc4_hdmi_dev_remove(struct platform_device *pdev) |
3789 | { |
3790 | component_del(&pdev->dev, &vc4_hdmi_ops); |
3791 | } |
3792 | |
3793 | static const struct vc4_hdmi_variant bcm2835_variant = { |
3794 | .encoder_type = VC4_ENCODER_TYPE_HDMI0, |
3795 | .debugfs_name = "hdmi_regs" , |
3796 | .card_name = "vc4-hdmi" , |
3797 | .max_pixel_clock = 162000000, |
3798 | .registers = vc4_hdmi_fields, |
3799 | .num_registers = ARRAY_SIZE(vc4_hdmi_fields), |
3800 | |
3801 | .init_resources = vc4_hdmi_init_resources, |
3802 | .csc_setup = vc4_hdmi_csc_setup, |
3803 | .reset = vc4_hdmi_reset, |
3804 | .set_timings = vc4_hdmi_set_timings, |
3805 | .phy_init = vc4_hdmi_phy_init, |
3806 | .phy_disable = vc4_hdmi_phy_disable, |
3807 | .phy_rng_enable = vc4_hdmi_phy_rng_enable, |
3808 | .phy_rng_disable = vc4_hdmi_phy_rng_disable, |
3809 | .channel_map = vc4_hdmi_channel_map, |
3810 | .supports_hdr = false, |
3811 | }; |
3812 | |
3813 | static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { |
3814 | .encoder_type = VC4_ENCODER_TYPE_HDMI0, |
3815 | .debugfs_name = "hdmi0_regs" , |
3816 | .card_name = "vc4-hdmi-0" , |
3817 | .max_pixel_clock = 600000000, |
3818 | .registers = vc5_hdmi_hdmi0_fields, |
3819 | .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields), |
3820 | .phy_lane_mapping = { |
3821 | PHY_LANE_0, |
3822 | PHY_LANE_1, |
3823 | PHY_LANE_2, |
3824 | PHY_LANE_CK, |
3825 | }, |
3826 | .unsupported_odd_h_timings = true, |
3827 | .external_irq_controller = true, |
3828 | |
3829 | .init_resources = vc5_hdmi_init_resources, |
3830 | .csc_setup = vc5_hdmi_csc_setup, |
3831 | .reset = vc5_hdmi_reset, |
3832 | .set_timings = vc5_hdmi_set_timings, |
3833 | .phy_init = vc5_hdmi_phy_init, |
3834 | .phy_disable = vc5_hdmi_phy_disable, |
3835 | .phy_rng_enable = vc5_hdmi_phy_rng_enable, |
3836 | .phy_rng_disable = vc5_hdmi_phy_rng_disable, |
3837 | .channel_map = vc5_hdmi_channel_map, |
3838 | .supports_hdr = true, |
3839 | .hp_detect = vc5_hdmi_hp_detect, |
3840 | }; |
3841 | |
3842 | static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { |
3843 | .encoder_type = VC4_ENCODER_TYPE_HDMI1, |
3844 | .debugfs_name = "hdmi1_regs" , |
3845 | .card_name = "vc4-hdmi-1" , |
3846 | .max_pixel_clock = HDMI_14_MAX_TMDS_CLK, |
3847 | .registers = vc5_hdmi_hdmi1_fields, |
3848 | .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields), |
3849 | .phy_lane_mapping = { |
3850 | PHY_LANE_1, |
3851 | PHY_LANE_0, |
3852 | PHY_LANE_CK, |
3853 | PHY_LANE_2, |
3854 | }, |
3855 | .unsupported_odd_h_timings = true, |
3856 | .external_irq_controller = true, |
3857 | |
3858 | .init_resources = vc5_hdmi_init_resources, |
3859 | .csc_setup = vc5_hdmi_csc_setup, |
3860 | .reset = vc5_hdmi_reset, |
3861 | .set_timings = vc5_hdmi_set_timings, |
3862 | .phy_init = vc5_hdmi_phy_init, |
3863 | .phy_disable = vc5_hdmi_phy_disable, |
3864 | .phy_rng_enable = vc5_hdmi_phy_rng_enable, |
3865 | .phy_rng_disable = vc5_hdmi_phy_rng_disable, |
3866 | .channel_map = vc5_hdmi_channel_map, |
3867 | .supports_hdr = true, |
3868 | .hp_detect = vc5_hdmi_hp_detect, |
3869 | }; |
3870 | |
3871 | static const struct of_device_id vc4_hdmi_dt_match[] = { |
3872 | { .compatible = "brcm,bcm2835-hdmi" , .data = &bcm2835_variant }, |
3873 | { .compatible = "brcm,bcm2711-hdmi0" , .data = &bcm2711_hdmi0_variant }, |
3874 | { .compatible = "brcm,bcm2711-hdmi1" , .data = &bcm2711_hdmi1_variant }, |
3875 | {} |
3876 | }; |
3877 | |
3878 | static const struct dev_pm_ops vc4_hdmi_pm_ops = { |
3879 | SET_RUNTIME_PM_OPS(vc4_hdmi_runtime_suspend, |
3880 | vc4_hdmi_runtime_resume, |
3881 | NULL) |
3882 | }; |
3883 | |
3884 | struct platform_driver vc4_hdmi_driver = { |
3885 | .probe = vc4_hdmi_dev_probe, |
3886 | .remove_new = vc4_hdmi_dev_remove, |
3887 | .driver = { |
3888 | .name = "vc4_hdmi" , |
3889 | .of_match_table = vc4_hdmi_dt_match, |
3890 | .pm = &vc4_hdmi_pm_ops, |
3891 | }, |
3892 | }; |
3893 | |