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