1 | // SPDX-License-Identifier: GPL-2.0-or-later |
---|---|
2 | /* |
3 | * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. |
4 | * Copyright (c) 2024 Collabora Ltd. |
5 | * |
6 | * Author: Algea Cao <algea.cao@rock-chips.com> |
7 | * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com> |
8 | */ |
9 | #include <linux/completion.h> |
10 | #include <linux/hdmi.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/irq.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mutex.h> |
15 | #include <linux/of.h> |
16 | #include <linux/workqueue.h> |
17 | |
18 | #include <drm/bridge/dw_hdmi_qp.h> |
19 | #include <drm/display/drm_hdmi_helper.h> |
20 | #include <drm/display/drm_hdmi_state_helper.h> |
21 | #include <drm/drm_atomic.h> |
22 | #include <drm/drm_atomic_helper.h> |
23 | #include <drm/drm_bridge.h> |
24 | #include <drm/drm_connector.h> |
25 | #include <drm/drm_edid.h> |
26 | #include <drm/drm_modes.h> |
27 | |
28 | #include <sound/hdmi-codec.h> |
29 | |
30 | #include "dw-hdmi-qp.h" |
31 | |
32 | #define DDC_CI_ADDR 0x37 |
33 | #define DDC_SEGMENT_ADDR 0x30 |
34 | |
35 | #define HDMI14_MAX_TMDSCLK 340000000 |
36 | |
37 | #define SCRAMB_POLL_DELAY_MS 3000 |
38 | |
39 | /* |
40 | * Unless otherwise noted, entries in this table are 100% optimization. |
41 | * Values can be obtained from dw_hdmi_qp_compute_n() but that function is |
42 | * slow so we pre-compute values we expect to see. |
43 | * |
44 | * The values for TMDS 25175, 25200, 27000, 54000, 74250 and 148500 kHz are |
45 | * the recommended N values specified in the Audio chapter of the HDMI |
46 | * specification. |
47 | */ |
48 | static const struct dw_hdmi_audio_tmds_n { |
49 | unsigned long tmds; |
50 | unsigned int n_32k; |
51 | unsigned int n_44k1; |
52 | unsigned int n_48k; |
53 | } common_tmds_n_table[] = { |
54 | { .tmds = 25175000, .n_32k = 4576, .n_44k1 = 7007, .n_48k = 6864, }, |
55 | { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, |
56 | { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, |
57 | { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, }, |
58 | { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, }, |
59 | { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, |
60 | { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, |
61 | { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, |
62 | { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, |
63 | { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, |
64 | { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, |
65 | { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, |
66 | { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, |
67 | { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, |
68 | { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, }, |
69 | { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, |
70 | { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, |
71 | { .tmds = 73250000, .n_32k = 11648, .n_44k1 = 14112, .n_48k = 6144, }, |
72 | { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, |
73 | { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, |
74 | { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, |
75 | { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, |
76 | { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, |
77 | { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, |
78 | { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, |
79 | { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, |
80 | { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, |
81 | { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, |
82 | { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, |
83 | { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, |
84 | { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, }, |
85 | { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, |
86 | { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, |
87 | { .tmds = 146250000, .n_32k = 11648, .n_44k1 = 6272, .n_48k = 6144, }, |
88 | { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, |
89 | { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, |
90 | { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, |
91 | |
92 | /* For 297 MHz+ HDMI spec have some other rule for setting N */ |
93 | { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, |
94 | { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240,}, |
95 | |
96 | /* End of table */ |
97 | { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, |
98 | }; |
99 | |
100 | /* |
101 | * These are the CTS values as recommended in the Audio chapter of the HDMI |
102 | * specification. |
103 | */ |
104 | static const struct dw_hdmi_audio_tmds_cts { |
105 | unsigned long tmds; |
106 | unsigned int cts_32k; |
107 | unsigned int cts_44k1; |
108 | unsigned int cts_48k; |
109 | } common_tmds_cts_table[] = { |
110 | { .tmds = 25175000, .cts_32k = 28125, .cts_44k1 = 31250, .cts_48k = 28125, }, |
111 | { .tmds = 25200000, .cts_32k = 25200, .cts_44k1 = 28000, .cts_48k = 25200, }, |
112 | { .tmds = 27000000, .cts_32k = 27000, .cts_44k1 = 30000, .cts_48k = 27000, }, |
113 | { .tmds = 54000000, .cts_32k = 54000, .cts_44k1 = 60000, .cts_48k = 54000, }, |
114 | { .tmds = 74250000, .cts_32k = 74250, .cts_44k1 = 82500, .cts_48k = 74250, }, |
115 | { .tmds = 148500000, .cts_32k = 148500, .cts_44k1 = 165000, .cts_48k = 148500, }, |
116 | |
117 | /* End of table */ |
118 | { .tmds = 0, .cts_32k = 0, .cts_44k1 = 0, .cts_48k = 0, }, |
119 | }; |
120 | |
121 | struct dw_hdmi_qp_i2c { |
122 | struct i2c_adapter adap; |
123 | |
124 | struct mutex lock; /* used to serialize data transfers */ |
125 | struct completion cmp; |
126 | u8 stat; |
127 | |
128 | u8 slave_reg; |
129 | bool is_regaddr; |
130 | bool is_segment; |
131 | }; |
132 | |
133 | struct dw_hdmi_qp { |
134 | struct drm_bridge bridge; |
135 | |
136 | struct device *dev; |
137 | struct dw_hdmi_qp_i2c *i2c; |
138 | |
139 | struct { |
140 | const struct dw_hdmi_qp_phy_ops *ops; |
141 | void *data; |
142 | } phy; |
143 | |
144 | struct regmap *regm; |
145 | |
146 | unsigned long tmds_char_rate; |
147 | }; |
148 | |
149 | static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val, |
150 | int offset) |
151 | { |
152 | regmap_write(map: hdmi->regm, reg: offset, val); |
153 | } |
154 | |
155 | static unsigned int dw_hdmi_qp_read(struct dw_hdmi_qp *hdmi, int offset) |
156 | { |
157 | unsigned int val = 0; |
158 | |
159 | regmap_read(map: hdmi->regm, reg: offset, val: &val); |
160 | |
161 | return val; |
162 | } |
163 | |
164 | static void dw_hdmi_qp_mod(struct dw_hdmi_qp *hdmi, unsigned int data, |
165 | unsigned int mask, unsigned int reg) |
166 | { |
167 | regmap_update_bits(map: hdmi->regm, reg, mask, val: data); |
168 | } |
169 | |
170 | static struct dw_hdmi_qp *dw_hdmi_qp_from_bridge(struct drm_bridge *bridge) |
171 | { |
172 | return container_of(bridge, struct dw_hdmi_qp, bridge); |
173 | } |
174 | |
175 | static void dw_hdmi_qp_set_cts_n(struct dw_hdmi_qp *hdmi, unsigned int cts, |
176 | unsigned int n) |
177 | { |
178 | /* Set N */ |
179 | dw_hdmi_qp_mod(hdmi, data: n, AUDPKT_ACR_N_VALUE, AUDPKT_ACR_CONTROL0); |
180 | |
181 | /* Set CTS */ |
182 | if (cts) |
183 | dw_hdmi_qp_mod(hdmi, AUDPKT_ACR_CTS_OVR_EN, AUDPKT_ACR_CTS_OVR_EN_MSK, |
184 | AUDPKT_ACR_CONTROL1); |
185 | else |
186 | dw_hdmi_qp_mod(hdmi, data: 0, AUDPKT_ACR_CTS_OVR_EN_MSK, |
187 | AUDPKT_ACR_CONTROL1); |
188 | |
189 | dw_hdmi_qp_mod(hdmi, AUDPKT_ACR_CTS_OVR_VAL(cts), AUDPKT_ACR_CTS_OVR_VAL_MSK, |
190 | AUDPKT_ACR_CONTROL1); |
191 | } |
192 | |
193 | static int dw_hdmi_qp_match_tmds_n_table(struct dw_hdmi_qp *hdmi, |
194 | unsigned long pixel_clk, |
195 | unsigned long freq) |
196 | { |
197 | const struct dw_hdmi_audio_tmds_n *tmds_n = NULL; |
198 | int i; |
199 | |
200 | for (i = 0; common_tmds_n_table[i].tmds != 0; i++) { |
201 | if (pixel_clk == common_tmds_n_table[i].tmds) { |
202 | tmds_n = &common_tmds_n_table[i]; |
203 | break; |
204 | } |
205 | } |
206 | |
207 | if (!tmds_n) |
208 | return -ENOENT; |
209 | |
210 | switch (freq) { |
211 | case 32000: |
212 | return tmds_n->n_32k; |
213 | case 44100: |
214 | case 88200: |
215 | case 176400: |
216 | return (freq / 44100) * tmds_n->n_44k1; |
217 | case 48000: |
218 | case 96000: |
219 | case 192000: |
220 | return (freq / 48000) * tmds_n->n_48k; |
221 | default: |
222 | return -ENOENT; |
223 | } |
224 | } |
225 | |
226 | static u32 dw_hdmi_qp_audio_math_diff(unsigned int freq, unsigned int n, |
227 | unsigned int pixel_clk) |
228 | { |
229 | u64 cts = mul_u32_u32(a: pixel_clk, b: n); |
230 | |
231 | return do_div(cts, 128 * freq); |
232 | } |
233 | |
234 | static unsigned int dw_hdmi_qp_compute_n(struct dw_hdmi_qp *hdmi, |
235 | unsigned long pixel_clk, |
236 | unsigned long freq) |
237 | { |
238 | unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); |
239 | unsigned int max_n = (128 * freq) / 300; |
240 | unsigned int ideal_n = (128 * freq) / 1000; |
241 | unsigned int best_n_distance = ideal_n; |
242 | unsigned int best_n = 0; |
243 | u64 best_diff = U64_MAX; |
244 | int n; |
245 | |
246 | /* If the ideal N could satisfy the audio math, then just take it */ |
247 | if (dw_hdmi_qp_audio_math_diff(freq, n: ideal_n, pixel_clk) == 0) |
248 | return ideal_n; |
249 | |
250 | for (n = min_n; n <= max_n; n++) { |
251 | u64 diff = dw_hdmi_qp_audio_math_diff(freq, n, pixel_clk); |
252 | |
253 | if (diff < best_diff || |
254 | (diff == best_diff && abs(n - ideal_n) < best_n_distance)) { |
255 | best_n = n; |
256 | best_diff = diff; |
257 | best_n_distance = abs(best_n - ideal_n); |
258 | } |
259 | |
260 | /* |
261 | * The best N already satisfy the audio math, and also be |
262 | * the closest value to ideal N, so just cut the loop. |
263 | */ |
264 | if (best_diff == 0 && (abs(n - ideal_n) > best_n_distance)) |
265 | break; |
266 | } |
267 | |
268 | return best_n; |
269 | } |
270 | |
271 | static unsigned int dw_hdmi_qp_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk, |
272 | unsigned long sample_rate) |
273 | { |
274 | int n = dw_hdmi_qp_match_tmds_n_table(hdmi, pixel_clk, freq: sample_rate); |
275 | |
276 | if (n > 0) |
277 | return n; |
278 | |
279 | dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n", |
280 | pixel_clk); |
281 | |
282 | return dw_hdmi_qp_compute_n(hdmi, pixel_clk, freq: sample_rate); |
283 | } |
284 | |
285 | static unsigned int dw_hdmi_qp_find_cts(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk, |
286 | unsigned long sample_rate) |
287 | { |
288 | const struct dw_hdmi_audio_tmds_cts *tmds_cts = NULL; |
289 | int i; |
290 | |
291 | for (i = 0; common_tmds_cts_table[i].tmds != 0; i++) { |
292 | if (pixel_clk == common_tmds_cts_table[i].tmds) { |
293 | tmds_cts = &common_tmds_cts_table[i]; |
294 | break; |
295 | } |
296 | } |
297 | |
298 | if (!tmds_cts) |
299 | return 0; |
300 | |
301 | switch (sample_rate) { |
302 | case 32000: |
303 | return tmds_cts->cts_32k; |
304 | case 44100: |
305 | case 88200: |
306 | case 176400: |
307 | return tmds_cts->cts_44k1; |
308 | case 48000: |
309 | case 96000: |
310 | case 192000: |
311 | return tmds_cts->cts_48k; |
312 | default: |
313 | return -ENOENT; |
314 | } |
315 | } |
316 | |
317 | static void dw_hdmi_qp_set_audio_interface(struct dw_hdmi_qp *hdmi, |
318 | struct hdmi_codec_daifmt *fmt, |
319 | struct hdmi_codec_params *hparms) |
320 | { |
321 | u32 conf0 = 0; |
322 | |
323 | /* Reset the audio data path of the AVP */ |
324 | dw_hdmi_qp_write(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWINIT_P, GLOBAL_SWRESET_REQUEST); |
325 | |
326 | /* Disable AUDS, ACR, AUDI */ |
327 | dw_hdmi_qp_mod(hdmi, data: 0, |
328 | PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | PKTSCHED_AUDI_TX_EN, |
329 | PKTSCHED_PKT_EN); |
330 | |
331 | /* Clear the audio FIFO */ |
332 | dw_hdmi_qp_write(hdmi, AUDIO_FIFO_CLR_P, AUDIO_INTERFACE_CONTROL0); |
333 | |
334 | /* Select I2S interface as the audio source */ |
335 | dw_hdmi_qp_mod(hdmi, AUD_IF_I2S, AUD_IF_SEL_MSK, AUDIO_INTERFACE_CONFIG0); |
336 | |
337 | /* Enable the active i2s lanes */ |
338 | switch (hparms->channels) { |
339 | case 7 ... 8: |
340 | conf0 |= I2S_LINES_EN(3); |
341 | fallthrough; |
342 | case 5 ... 6: |
343 | conf0 |= I2S_LINES_EN(2); |
344 | fallthrough; |
345 | case 3 ... 4: |
346 | conf0 |= I2S_LINES_EN(1); |
347 | fallthrough; |
348 | default: |
349 | conf0 |= I2S_LINES_EN(0); |
350 | break; |
351 | } |
352 | |
353 | dw_hdmi_qp_mod(hdmi, data: conf0, I2S_LINES_EN_MSK, AUDIO_INTERFACE_CONFIG0); |
354 | |
355 | /* |
356 | * Enable bpcuv generated internally for L-PCM, or received |
357 | * from stream for NLPCM/HBR. |
358 | */ |
359 | switch (fmt->bit_fmt) { |
360 | case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: |
361 | conf0 = (hparms->channels == 8) ? AUD_HBR : AUD_ASP; |
362 | conf0 |= I2S_BPCUV_RCV_EN; |
363 | break; |
364 | default: |
365 | conf0 = AUD_ASP | I2S_BPCUV_RCV_DIS; |
366 | break; |
367 | } |
368 | |
369 | dw_hdmi_qp_mod(hdmi, data: conf0, I2S_BPCUV_RCV_MSK | AUD_FORMAT_MSK, |
370 | AUDIO_INTERFACE_CONFIG0); |
371 | |
372 | /* Enable audio FIFO auto clear when overflow */ |
373 | dw_hdmi_qp_mod(hdmi, AUD_FIFO_INIT_ON_OVF_EN, AUD_FIFO_INIT_ON_OVF_MSK, |
374 | AUDIO_INTERFACE_CONFIG0); |
375 | } |
376 | |
377 | /* |
378 | * When transmitting IEC60958 linear PCM audio, these registers allow to |
379 | * configure the channel status information of all the channel status |
380 | * bits in the IEC60958 frame. For the moment this configuration is only |
381 | * used when the I2S audio interface, General Purpose Audio (GPA), |
382 | * or AHB audio DMA (AHBAUDDMA) interface is active |
383 | * (for S/PDIF interface this information comes from the stream). |
384 | */ |
385 | static void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, |
386 | u8 *channel_status, bool ref2stream) |
387 | { |
388 | /* |
389 | * AUDPKT_CHSTATUS_OVR0: { RSV, RSV, CS1, CS0 } |
390 | * AUDPKT_CHSTATUS_OVR1: { CS6, CS5, CS4, CS3 } |
391 | * |
392 | * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
393 | * CS0: | Mode | d | c | b | a | |
394 | * CS1: | Category Code | |
395 | * CS2: | Channel Number | Source Number | |
396 | * CS3: | Clock Accuracy | Sample Freq | |
397 | * CS4: | Ori Sample Freq | Word Length | |
398 | * CS5: | | CGMS-A | |
399 | * CS6~CS23: Reserved |
400 | * |
401 | * a: use of channel status block |
402 | * b: linear PCM identification: 0 for lpcm, 1 for nlpcm |
403 | * c: copyright information |
404 | * d: additional format information |
405 | */ |
406 | |
407 | if (ref2stream) |
408 | channel_status[0] |= IEC958_AES0_NONAUDIO; |
409 | |
410 | if ((dw_hdmi_qp_read(hdmi, AUDIO_INTERFACE_CONFIG0) & GENMASK(25, 24)) == AUD_HBR) { |
411 | /* fixup cs for HBR */ |
412 | channel_status[3] = (channel_status[3] & 0xf0) | IEC958_AES3_CON_FS_768000; |
413 | channel_status[4] = (channel_status[4] & 0x0f) | IEC958_AES4_CON_ORIGFS_NOTID; |
414 | } |
415 | |
416 | dw_hdmi_qp_write(hdmi, val: channel_status[0] | (channel_status[1] << 8), |
417 | AUDPKT_CHSTATUS_OVR0); |
418 | |
419 | regmap_bulk_write(map: hdmi->regm, AUDPKT_CHSTATUS_OVR1, val: &channel_status[3], val_count: 1); |
420 | |
421 | if (ref2stream) |
422 | dw_hdmi_qp_mod(hdmi, data: 0, |
423 | AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, |
424 | AUDPKT_CONTROL0); |
425 | else |
426 | dw_hdmi_qp_mod(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, |
427 | AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, |
428 | AUDPKT_CONTROL0); |
429 | } |
430 | |
431 | static void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned long long tmds_char_rate, |
432 | unsigned int sample_rate) |
433 | { |
434 | unsigned int n, cts; |
435 | |
436 | n = dw_hdmi_qp_find_n(hdmi, pixel_clk: tmds_char_rate, sample_rate); |
437 | cts = dw_hdmi_qp_find_cts(hdmi, pixel_clk: tmds_char_rate, sample_rate); |
438 | |
439 | dw_hdmi_qp_set_cts_n(hdmi, cts, n); |
440 | } |
441 | |
442 | static int dw_hdmi_qp_audio_enable(struct drm_connector *connector, |
443 | struct drm_bridge *bridge) |
444 | { |
445 | struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge); |
446 | |
447 | if (hdmi->tmds_char_rate) |
448 | dw_hdmi_qp_mod(hdmi, data: 0, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); |
449 | |
450 | return 0; |
451 | } |
452 | |
453 | static int dw_hdmi_qp_audio_prepare(struct drm_connector *connector, |
454 | struct drm_bridge *bridge, |
455 | struct hdmi_codec_daifmt *fmt, |
456 | struct hdmi_codec_params *hparms) |
457 | { |
458 | struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge); |
459 | bool ref2stream = false; |
460 | |
461 | if (!hdmi->tmds_char_rate) |
462 | return -ENODEV; |
463 | |
464 | if (fmt->bit_clk_provider | fmt->frame_clk_provider) { |
465 | dev_err(hdmi->dev, "unsupported clock settings\n"); |
466 | return -EINVAL; |
467 | } |
468 | |
469 | if (fmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) |
470 | ref2stream = true; |
471 | |
472 | dw_hdmi_qp_set_audio_interface(hdmi, fmt, hparms); |
473 | dw_hdmi_qp_set_sample_rate(hdmi, tmds_char_rate: hdmi->tmds_char_rate, sample_rate: hparms->sample_rate); |
474 | dw_hdmi_qp_set_channel_status(hdmi, channel_status: hparms->iec.status, ref2stream); |
475 | drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector, frame: &hparms->cea); |
476 | |
477 | return 0; |
478 | } |
479 | |
480 | static void dw_hdmi_qp_audio_disable_regs(struct dw_hdmi_qp *hdmi) |
481 | { |
482 | /* |
483 | * Keep ACR, AUDI, AUDS packet always on to make SINK device |
484 | * active for better compatibility and user experience. |
485 | * |
486 | * This also fix POP sound on some SINK devices which wakeup |
487 | * from suspend to active. |
488 | */ |
489 | dw_hdmi_qp_mod(hdmi, I2S_BPCUV_RCV_DIS, I2S_BPCUV_RCV_MSK, |
490 | AUDIO_INTERFACE_CONFIG0); |
491 | dw_hdmi_qp_mod(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, |
492 | AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, |
493 | AUDPKT_CONTROL0); |
494 | |
495 | dw_hdmi_qp_mod(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, |
496 | AVP_DATAPATH_PACKET_AUDIO_SWDISABLE, GLOBAL_SWDISABLE); |
497 | } |
498 | |
499 | static void dw_hdmi_qp_audio_disable(struct drm_connector *connector, |
500 | struct drm_bridge *bridge) |
501 | { |
502 | struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge); |
503 | |
504 | drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector); |
505 | |
506 | if (hdmi->tmds_char_rate) |
507 | dw_hdmi_qp_audio_disable_regs(hdmi); |
508 | } |
509 | |
510 | static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi, |
511 | unsigned char *buf, unsigned int length) |
512 | { |
513 | struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; |
514 | int stat; |
515 | |
516 | if (!i2c->is_regaddr) { |
517 | dev_dbg(hdmi->dev, "set read register address to 0\n"); |
518 | i2c->slave_reg = 0x00; |
519 | i2c->is_regaddr = true; |
520 | } |
521 | |
522 | while (length--) { |
523 | reinit_completion(x: &i2c->cmp); |
524 | |
525 | dw_hdmi_qp_mod(hdmi, data: i2c->slave_reg++ << 12, I2CM_ADDR, |
526 | I2CM_INTERFACE_CONTROL0); |
527 | |
528 | if (i2c->is_segment) |
529 | dw_hdmi_qp_mod(hdmi, I2CM_EXT_READ, I2CM_WR_MASK, |
530 | I2CM_INTERFACE_CONTROL0); |
531 | else |
532 | dw_hdmi_qp_mod(hdmi, I2CM_FM_READ, I2CM_WR_MASK, |
533 | I2CM_INTERFACE_CONTROL0); |
534 | |
535 | stat = wait_for_completion_timeout(x: &i2c->cmp, HZ / 10); |
536 | if (!stat) { |
537 | dev_err(hdmi->dev, "i2c read timed out\n"); |
538 | dw_hdmi_qp_write(hdmi, val: 0x01, I2CM_CONTROL0); |
539 | return -EAGAIN; |
540 | } |
541 | |
542 | /* Check for error condition on the bus */ |
543 | if (i2c->stat & I2CM_NACK_RCVD_IRQ) { |
544 | dev_err(hdmi->dev, "i2c read error\n"); |
545 | dw_hdmi_qp_write(hdmi, val: 0x01, I2CM_CONTROL0); |
546 | return -EIO; |
547 | } |
548 | |
549 | *buf++ = dw_hdmi_qp_read(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; |
550 | dw_hdmi_qp_mod(hdmi, data: 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); |
551 | } |
552 | |
553 | i2c->is_segment = false; |
554 | |
555 | return 0; |
556 | } |
557 | |
558 | static int dw_hdmi_qp_i2c_write(struct dw_hdmi_qp *hdmi, |
559 | unsigned char *buf, unsigned int length) |
560 | { |
561 | struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; |
562 | int stat; |
563 | |
564 | if (!i2c->is_regaddr) { |
565 | /* Use the first write byte as register address */ |
566 | i2c->slave_reg = buf[0]; |
567 | length--; |
568 | buf++; |
569 | i2c->is_regaddr = true; |
570 | } |
571 | |
572 | while (length--) { |
573 | reinit_completion(x: &i2c->cmp); |
574 | |
575 | dw_hdmi_qp_write(hdmi, val: *buf++, I2CM_INTERFACE_WRDATA_0_3); |
576 | dw_hdmi_qp_mod(hdmi, data: i2c->slave_reg++ << 12, I2CM_ADDR, |
577 | I2CM_INTERFACE_CONTROL0); |
578 | dw_hdmi_qp_mod(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, |
579 | I2CM_INTERFACE_CONTROL0); |
580 | |
581 | stat = wait_for_completion_timeout(x: &i2c->cmp, HZ / 10); |
582 | if (!stat) { |
583 | dev_err(hdmi->dev, "i2c write time out!\n"); |
584 | dw_hdmi_qp_write(hdmi, val: 0x01, I2CM_CONTROL0); |
585 | return -EAGAIN; |
586 | } |
587 | |
588 | /* Check for error condition on the bus */ |
589 | if (i2c->stat & I2CM_NACK_RCVD_IRQ) { |
590 | dev_err(hdmi->dev, "i2c write nack!\n"); |
591 | dw_hdmi_qp_write(hdmi, val: 0x01, I2CM_CONTROL0); |
592 | return -EIO; |
593 | } |
594 | |
595 | dw_hdmi_qp_mod(hdmi, data: 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); |
596 | } |
597 | |
598 | return 0; |
599 | } |
600 | |
601 | static int dw_hdmi_qp_i2c_xfer(struct i2c_adapter *adap, |
602 | struct i2c_msg *msgs, int num) |
603 | { |
604 | struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap); |
605 | struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; |
606 | u8 addr = msgs[0].addr; |
607 | int i, ret = 0; |
608 | |
609 | if (addr == DDC_CI_ADDR) |
610 | /* |
611 | * The internal I2C controller does not support the multi-byte |
612 | * read and write operations needed for DDC/CI. |
613 | * FIXME: Blacklist the DDC/CI address until we filter out |
614 | * unsupported I2C operations. |
615 | */ |
616 | return -EOPNOTSUPP; |
617 | |
618 | for (i = 0; i < num; i++) { |
619 | if (msgs[i].len == 0) { |
620 | dev_err(hdmi->dev, |
621 | "unsupported transfer %d/%d, no data\n", |
622 | i + 1, num); |
623 | return -EOPNOTSUPP; |
624 | } |
625 | } |
626 | |
627 | guard(mutex)(T: &i2c->lock); |
628 | |
629 | /* Unmute DONE and ERROR interrupts */ |
630 | dw_hdmi_qp_mod(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, |
631 | I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, |
632 | MAINUNIT_1_INT_MASK_N); |
633 | |
634 | /* Set slave device address taken from the first I2C message */ |
635 | if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) |
636 | addr = DDC_ADDR; |
637 | |
638 | dw_hdmi_qp_mod(hdmi, data: addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); |
639 | |
640 | /* Set slave device register address on transfer */ |
641 | i2c->is_regaddr = false; |
642 | |
643 | /* Set segment pointer for I2C extended read mode operation */ |
644 | i2c->is_segment = false; |
645 | |
646 | for (i = 0; i < num; i++) { |
647 | if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { |
648 | i2c->is_segment = true; |
649 | dw_hdmi_qp_mod(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, |
650 | I2CM_INTERFACE_CONTROL1); |
651 | dw_hdmi_qp_mod(hdmi, data: *msgs[i].buf << 7, I2CM_SEG_PTR, |
652 | I2CM_INTERFACE_CONTROL1); |
653 | } else { |
654 | if (msgs[i].flags & I2C_M_RD) |
655 | ret = dw_hdmi_qp_i2c_read(hdmi, buf: msgs[i].buf, |
656 | length: msgs[i].len); |
657 | else |
658 | ret = dw_hdmi_qp_i2c_write(hdmi, buf: msgs[i].buf, |
659 | length: msgs[i].len); |
660 | } |
661 | if (ret < 0) |
662 | break; |
663 | } |
664 | |
665 | if (!ret) |
666 | ret = num; |
667 | |
668 | /* Mute DONE and ERROR interrupts */ |
669 | dw_hdmi_qp_mod(hdmi, data: 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, |
670 | MAINUNIT_1_INT_MASK_N); |
671 | |
672 | return ret; |
673 | } |
674 | |
675 | static u32 dw_hdmi_qp_i2c_func(struct i2c_adapter *adapter) |
676 | { |
677 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
678 | } |
679 | |
680 | static const struct i2c_algorithm dw_hdmi_qp_algorithm = { |
681 | .master_xfer = dw_hdmi_qp_i2c_xfer, |
682 | .functionality = dw_hdmi_qp_i2c_func, |
683 | }; |
684 | |
685 | static struct i2c_adapter *dw_hdmi_qp_i2c_adapter(struct dw_hdmi_qp *hdmi) |
686 | { |
687 | struct dw_hdmi_qp_i2c *i2c; |
688 | struct i2c_adapter *adap; |
689 | int ret; |
690 | |
691 | i2c = devm_kzalloc(dev: hdmi->dev, size: sizeof(*i2c), GFP_KERNEL); |
692 | if (!i2c) |
693 | return ERR_PTR(error: -ENOMEM); |
694 | |
695 | mutex_init(&i2c->lock); |
696 | init_completion(x: &i2c->cmp); |
697 | |
698 | adap = &i2c->adap; |
699 | adap->owner = THIS_MODULE; |
700 | adap->dev.parent = hdmi->dev; |
701 | adap->algo = &dw_hdmi_qp_algorithm; |
702 | strscpy(adap->name, "DesignWare HDMI QP", sizeof(adap->name)); |
703 | |
704 | i2c_set_adapdata(adap, data: hdmi); |
705 | |
706 | ret = devm_i2c_add_adapter(dev: hdmi->dev, adapter: adap); |
707 | if (ret) { |
708 | dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); |
709 | devm_kfree(dev: hdmi->dev, p: i2c); |
710 | return ERR_PTR(error: ret); |
711 | } |
712 | |
713 | hdmi->i2c = i2c; |
714 | dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); |
715 | |
716 | return adap; |
717 | } |
718 | |
719 | static int dw_hdmi_qp_config_avi_infoframe(struct dw_hdmi_qp *hdmi, |
720 | const u8 *buffer, size_t len) |
721 | { |
722 | u32 val, i, j; |
723 | |
724 | if (len != HDMI_INFOFRAME_SIZE(AVI)) { |
725 | dev_err(hdmi->dev, "failed to configure avi infoframe\n"); |
726 | return -EINVAL; |
727 | } |
728 | |
729 | /* |
730 | * DW HDMI QP IP uses a different byte format from standard AVI info |
731 | * frames, though generally the bits are in the correct bytes. |
732 | */ |
733 | val = buffer[1] << 8 | buffer[2] << 16; |
734 | dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS0); |
735 | |
736 | for (i = 0; i < 4; i++) { |
737 | for (j = 0; j < 4; j++) { |
738 | if (i * 4 + j >= 14) |
739 | break; |
740 | if (!j) |
741 | val = buffer[i * 4 + j + 3]; |
742 | val |= buffer[i * 4 + j + 3] << (8 * j); |
743 | } |
744 | |
745 | dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS1 + i * 4); |
746 | } |
747 | |
748 | dw_hdmi_qp_mod(hdmi, data: 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1); |
749 | |
750 | dw_hdmi_qp_mod(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, |
751 | PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN); |
752 | |
753 | return 0; |
754 | } |
755 | |
756 | static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi, |
757 | const u8 *buffer, size_t len) |
758 | { |
759 | u32 val, i; |
760 | |
761 | if (len != HDMI_INFOFRAME_SIZE(DRM)) { |
762 | dev_err(hdmi->dev, "failed to configure drm infoframe\n"); |
763 | return -EINVAL; |
764 | } |
765 | |
766 | dw_hdmi_qp_mod(hdmi, data: 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); |
767 | |
768 | val = buffer[1] << 8 | buffer[2] << 16; |
769 | dw_hdmi_qp_write(hdmi, val, PKT_DRMI_CONTENTS0); |
770 | |
771 | for (i = 0; i <= buffer[2]; i++) { |
772 | if (i % 4 == 0) |
773 | val = buffer[3 + i]; |
774 | val |= buffer[3 + i] << ((i % 4) * 8); |
775 | |
776 | if ((i % 4 == 3) || i == buffer[2]) |
777 | dw_hdmi_qp_write(hdmi, val, |
778 | PKT_DRMI_CONTENTS1 + ((i / 4) * 4)); |
779 | } |
780 | |
781 | dw_hdmi_qp_mod(hdmi, data: 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1); |
782 | dw_hdmi_qp_mod(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, |
783 | PKTSCHED_PKT_EN); |
784 | |
785 | return 0; |
786 | } |
787 | |
788 | /* |
789 | * Static values documented in the TRM |
790 | * Different values are only used for debug purposes |
791 | */ |
792 | #define DW_HDMI_QP_AUDIO_INFOFRAME_HB1 0x1 |
793 | #define DW_HDMI_QP_AUDIO_INFOFRAME_HB2 0xa |
794 | |
795 | static int dw_hdmi_qp_config_audio_infoframe(struct dw_hdmi_qp *hdmi, |
796 | const u8 *buffer, size_t len) |
797 | { |
798 | /* |
799 | * AUDI_CONTENTS0: { RSV, HB2, HB1, RSV } |
800 | * AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 } |
801 | * AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 } |
802 | * |
803 | * PB0: CheckSum |
804 | * PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 | |
805 | * PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 | |
806 | * PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 | |
807 | * PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 | |
808 | * PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 | |
809 | * PB6~PB10: Reserved |
810 | * |
811 | * AUDI_CONTENTS0 default value defined by HDMI specification, |
812 | * and shall only be changed for debug purposes. |
813 | */ |
814 | u32 header_bytes = (DW_HDMI_QP_AUDIO_INFOFRAME_HB1 << 8) | |
815 | (DW_HDMI_QP_AUDIO_INFOFRAME_HB2 << 16); |
816 | |
817 | regmap_bulk_write(map: hdmi->regm, PKT_AUDI_CONTENTS0, val: &header_bytes, val_count: 1); |
818 | regmap_bulk_write(map: hdmi->regm, PKT_AUDI_CONTENTS1, val: &buffer[3], val_count: 1); |
819 | regmap_bulk_write(map: hdmi->regm, PKT_AUDI_CONTENTS2, val: &buffer[4], val_count: 1); |
820 | |
821 | /* Enable ACR, AUDI, AMD */ |
822 | dw_hdmi_qp_mod(hdmi, |
823 | PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, |
824 | PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, |
825 | PKTSCHED_PKT_EN); |
826 | |
827 | /* Enable AUDS */ |
828 | dw_hdmi_qp_mod(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN); |
829 | |
830 | return 0; |
831 | } |
832 | |
833 | static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, |
834 | struct drm_atomic_state *state) |
835 | { |
836 | struct dw_hdmi_qp *hdmi = bridge->driver_private; |
837 | struct drm_connector_state *conn_state; |
838 | struct drm_connector *connector; |
839 | unsigned int op_mode; |
840 | |
841 | connector = drm_atomic_get_new_connector_for_encoder(state, encoder: bridge->encoder); |
842 | if (WARN_ON(!connector)) |
843 | return; |
844 | |
845 | conn_state = drm_atomic_get_new_connector_state(state, connector); |
846 | if (WARN_ON(!conn_state)) |
847 | return; |
848 | |
849 | if (connector->display_info.is_hdmi) { |
850 | dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n", |
851 | __func__, conn_state->hdmi.tmds_char_rate); |
852 | op_mode = 0; |
853 | hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; |
854 | } else { |
855 | dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__); |
856 | op_mode = OPMODE_DVI; |
857 | } |
858 | |
859 | hdmi->phy.ops->init(hdmi, hdmi->phy.data); |
860 | |
861 | dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); |
862 | dw_hdmi_qp_mod(hdmi, data: op_mode, OPMODE_DVI, LINK_CONFIG0); |
863 | |
864 | drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); |
865 | } |
866 | |
867 | static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, |
868 | struct drm_atomic_state *state) |
869 | { |
870 | struct dw_hdmi_qp *hdmi = bridge->driver_private; |
871 | |
872 | hdmi->tmds_char_rate = 0; |
873 | |
874 | hdmi->phy.ops->disable(hdmi, hdmi->phy.data); |
875 | } |
876 | |
877 | static enum drm_connector_status |
878 | dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge) |
879 | { |
880 | struct dw_hdmi_qp *hdmi = bridge->driver_private; |
881 | |
882 | return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); |
883 | } |
884 | |
885 | static const struct drm_edid * |
886 | dw_hdmi_qp_bridge_edid_read(struct drm_bridge *bridge, |
887 | struct drm_connector *connector) |
888 | { |
889 | struct dw_hdmi_qp *hdmi = bridge->driver_private; |
890 | const struct drm_edid *drm_edid; |
891 | |
892 | drm_edid = drm_edid_read_ddc(connector, adapter: bridge->ddc); |
893 | if (!drm_edid) |
894 | dev_dbg(hdmi->dev, "failed to get edid\n"); |
895 | |
896 | return drm_edid; |
897 | } |
898 | |
899 | static enum drm_mode_status |
900 | dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge, |
901 | const struct drm_display_mode *mode, |
902 | unsigned long long rate) |
903 | { |
904 | struct dw_hdmi_qp *hdmi = bridge->driver_private; |
905 | |
906 | if (rate > HDMI14_MAX_TMDSCLK) { |
907 | dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate); |
908 | return MODE_CLOCK_HIGH; |
909 | } |
910 | |
911 | return MODE_OK; |
912 | } |
913 | |
914 | static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge, |
915 | enum hdmi_infoframe_type type) |
916 | { |
917 | struct dw_hdmi_qp *hdmi = bridge->driver_private; |
918 | |
919 | switch (type) { |
920 | case HDMI_INFOFRAME_TYPE_AVI: |
921 | dw_hdmi_qp_mod(hdmi, data: 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, |
922 | PKTSCHED_PKT_EN); |
923 | break; |
924 | |
925 | case HDMI_INFOFRAME_TYPE_DRM: |
926 | dw_hdmi_qp_mod(hdmi, data: 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); |
927 | break; |
928 | |
929 | case HDMI_INFOFRAME_TYPE_AUDIO: |
930 | dw_hdmi_qp_mod(hdmi, data: 0, |
931 | PKTSCHED_ACR_TX_EN | |
932 | PKTSCHED_AUDS_TX_EN | |
933 | PKTSCHED_AUDI_TX_EN, |
934 | PKTSCHED_PKT_EN); |
935 | break; |
936 | default: |
937 | dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); |
938 | } |
939 | |
940 | return 0; |
941 | } |
942 | |
943 | static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge, |
944 | enum hdmi_infoframe_type type, |
945 | const u8 *buffer, size_t len) |
946 | { |
947 | struct dw_hdmi_qp *hdmi = bridge->driver_private; |
948 | |
949 | dw_hdmi_qp_bridge_clear_infoframe(bridge, type); |
950 | |
951 | switch (type) { |
952 | case HDMI_INFOFRAME_TYPE_AVI: |
953 | return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len); |
954 | |
955 | case HDMI_INFOFRAME_TYPE_DRM: |
956 | return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len); |
957 | |
958 | case HDMI_INFOFRAME_TYPE_AUDIO: |
959 | return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len); |
960 | |
961 | default: |
962 | dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); |
963 | return 0; |
964 | } |
965 | } |
966 | |
967 | static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = { |
968 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
969 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
970 | .atomic_reset = drm_atomic_helper_bridge_reset, |
971 | .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, |
972 | .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, |
973 | .detect = dw_hdmi_qp_bridge_detect, |
974 | .edid_read = dw_hdmi_qp_bridge_edid_read, |
975 | .hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid, |
976 | .hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe, |
977 | .hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe, |
978 | .hdmi_audio_startup = dw_hdmi_qp_audio_enable, |
979 | .hdmi_audio_shutdown = dw_hdmi_qp_audio_disable, |
980 | .hdmi_audio_prepare = dw_hdmi_qp_audio_prepare, |
981 | }; |
982 | |
983 | static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id) |
984 | { |
985 | struct dw_hdmi_qp *hdmi = dev_id; |
986 | struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; |
987 | u32 stat; |
988 | |
989 | stat = dw_hdmi_qp_read(hdmi, MAINUNIT_1_INT_STATUS); |
990 | |
991 | i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | |
992 | I2CM_NACK_RCVD_IRQ); |
993 | |
994 | if (i2c->stat) { |
995 | dw_hdmi_qp_write(hdmi, val: i2c->stat, MAINUNIT_1_INT_CLEAR); |
996 | complete(&i2c->cmp); |
997 | } |
998 | |
999 | if (stat) |
1000 | return IRQ_HANDLED; |
1001 | |
1002 | return IRQ_NONE; |
1003 | } |
1004 | |
1005 | static const struct regmap_config dw_hdmi_qp_regmap_config = { |
1006 | .reg_bits = 32, |
1007 | .val_bits = 32, |
1008 | .reg_stride = 4, |
1009 | .max_register = EARCRX_1_INT_FORCE, |
1010 | }; |
1011 | |
1012 | static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi) |
1013 | { |
1014 | dw_hdmi_qp_write(hdmi, val: 0, MAINUNIT_0_INT_MASK_N); |
1015 | dw_hdmi_qp_write(hdmi, val: 0, MAINUNIT_1_INT_MASK_N); |
1016 | dw_hdmi_qp_write(hdmi, val: 428571429, TIMER_BASE_CONFIG0); |
1017 | |
1018 | /* Software reset */ |
1019 | dw_hdmi_qp_write(hdmi, val: 0x01, I2CM_CONTROL0); |
1020 | |
1021 | dw_hdmi_qp_write(hdmi, val: 0x085c085c, I2CM_FM_SCL_CONFIG0); |
1022 | |
1023 | dw_hdmi_qp_mod(hdmi, data: 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); |
1024 | |
1025 | /* Clear DONE and ERROR interrupts */ |
1026 | dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, |
1027 | MAINUNIT_1_INT_CLEAR); |
1028 | |
1029 | if (hdmi->phy.ops->setup_hpd) |
1030 | hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); |
1031 | } |
1032 | |
1033 | struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, |
1034 | struct drm_encoder *encoder, |
1035 | const struct dw_hdmi_qp_plat_data *plat_data) |
1036 | { |
1037 | struct device *dev = &pdev->dev; |
1038 | struct dw_hdmi_qp *hdmi; |
1039 | void __iomem *regs; |
1040 | int ret; |
1041 | |
1042 | if (!plat_data->phy_ops || !plat_data->phy_ops->init || |
1043 | !plat_data->phy_ops->disable || !plat_data->phy_ops->read_hpd) { |
1044 | dev_err(dev, "Missing platform PHY ops\n"); |
1045 | return ERR_PTR(error: -ENODEV); |
1046 | } |
1047 | |
1048 | hdmi = devm_kzalloc(dev, size: sizeof(*hdmi), GFP_KERNEL); |
1049 | if (!hdmi) |
1050 | return ERR_PTR(error: -ENOMEM); |
1051 | |
1052 | hdmi->dev = dev; |
1053 | |
1054 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
1055 | if (IS_ERR(ptr: regs)) |
1056 | return ERR_CAST(ptr: regs); |
1057 | |
1058 | hdmi->regm = devm_regmap_init_mmio(dev, regs, &dw_hdmi_qp_regmap_config); |
1059 | if (IS_ERR(ptr: hdmi->regm)) { |
1060 | dev_err(dev, "Failed to configure regmap\n"); |
1061 | return ERR_CAST(ptr: hdmi->regm); |
1062 | } |
1063 | |
1064 | hdmi->phy.ops = plat_data->phy_ops; |
1065 | hdmi->phy.data = plat_data->phy_data; |
1066 | |
1067 | dw_hdmi_qp_init_hw(hdmi); |
1068 | |
1069 | ret = devm_request_threaded_irq(dev, irq: plat_data->main_irq, |
1070 | handler: dw_hdmi_qp_main_hardirq, NULL, |
1071 | IRQF_SHARED, devname: dev_name(dev), dev_id: hdmi); |
1072 | if (ret) |
1073 | return ERR_PTR(error: ret); |
1074 | |
1075 | hdmi->bridge.driver_private = hdmi; |
1076 | hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs; |
1077 | hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | |
1078 | DRM_BRIDGE_OP_EDID | |
1079 | DRM_BRIDGE_OP_HDMI | |
1080 | DRM_BRIDGE_OP_HDMI_AUDIO | |
1081 | DRM_BRIDGE_OP_HPD; |
1082 | hdmi->bridge.of_node = pdev->dev.of_node; |
1083 | hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; |
1084 | hdmi->bridge.vendor = "Synopsys"; |
1085 | hdmi->bridge.product = "DW HDMI QP TX"; |
1086 | |
1087 | hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi); |
1088 | if (IS_ERR(ptr: hdmi->bridge.ddc)) |
1089 | return ERR_CAST(ptr: hdmi->bridge.ddc); |
1090 | |
1091 | hdmi->bridge.hdmi_audio_max_i2s_playback_channels = 8; |
1092 | hdmi->bridge.hdmi_audio_dev = dev; |
1093 | hdmi->bridge.hdmi_audio_dai_port = 1; |
1094 | |
1095 | ret = devm_drm_bridge_add(dev, bridge: &hdmi->bridge); |
1096 | if (ret) |
1097 | return ERR_PTR(error: ret); |
1098 | |
1099 | ret = drm_bridge_attach(encoder, bridge: &hdmi->bridge, NULL, |
1100 | flags: DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
1101 | if (ret) |
1102 | return ERR_PTR(error: ret); |
1103 | |
1104 | return hdmi; |
1105 | } |
1106 | EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind); |
1107 | |
1108 | void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi) |
1109 | { |
1110 | dw_hdmi_qp_init_hw(hdmi); |
1111 | } |
1112 | EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume); |
1113 | |
1114 | MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>"); |
1115 | MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@collabora.com>"); |
1116 | MODULE_DESCRIPTION("DW HDMI QP transmitter library"); |
1117 | MODULE_LICENSE("GPL"); |
1118 |
Definitions
- dw_hdmi_audio_tmds_n
- common_tmds_n_table
- dw_hdmi_audio_tmds_cts
- common_tmds_cts_table
- dw_hdmi_qp_i2c
- dw_hdmi_qp
- dw_hdmi_qp_write
- dw_hdmi_qp_read
- dw_hdmi_qp_mod
- dw_hdmi_qp_from_bridge
- dw_hdmi_qp_set_cts_n
- dw_hdmi_qp_match_tmds_n_table
- dw_hdmi_qp_audio_math_diff
- dw_hdmi_qp_compute_n
- dw_hdmi_qp_find_n
- dw_hdmi_qp_find_cts
- dw_hdmi_qp_set_audio_interface
- dw_hdmi_qp_set_channel_status
- dw_hdmi_qp_set_sample_rate
- dw_hdmi_qp_audio_enable
- dw_hdmi_qp_audio_prepare
- dw_hdmi_qp_audio_disable_regs
- dw_hdmi_qp_audio_disable
- dw_hdmi_qp_i2c_read
- dw_hdmi_qp_i2c_write
- dw_hdmi_qp_i2c_xfer
- dw_hdmi_qp_i2c_func
- dw_hdmi_qp_algorithm
- dw_hdmi_qp_i2c_adapter
- dw_hdmi_qp_config_avi_infoframe
- dw_hdmi_qp_config_drm_infoframe
- dw_hdmi_qp_config_audio_infoframe
- dw_hdmi_qp_bridge_atomic_enable
- dw_hdmi_qp_bridge_atomic_disable
- dw_hdmi_qp_bridge_detect
- dw_hdmi_qp_bridge_edid_read
- dw_hdmi_qp_bridge_tmds_char_rate_valid
- dw_hdmi_qp_bridge_clear_infoframe
- dw_hdmi_qp_bridge_write_infoframe
- dw_hdmi_qp_bridge_funcs
- dw_hdmi_qp_main_hardirq
- dw_hdmi_qp_regmap_config
- dw_hdmi_qp_init_hw
- dw_hdmi_qp_bind
Improve your Profiling and Debugging skills
Find out more