1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | |
7 | #define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ |
8 | |
9 | #include <linux/platform_device.h> |
10 | |
11 | #include <drm/display/drm_dp_helper.h> |
12 | #include <drm/drm_edid.h> |
13 | |
14 | #include "dp_catalog.h" |
15 | #include "dp_audio.h" |
16 | #include "dp_panel.h" |
17 | #include "dp_display.h" |
18 | |
19 | #define 0 |
20 | #define PARITY_BYTE_2_BIT 8 |
21 | #define 16 |
22 | #define PARITY_BYTE_1_BIT 24 |
23 | #define 16 |
24 | #define PARITY_BYTE_3_BIT 24 |
25 | |
26 | struct dp_audio_private { |
27 | struct platform_device *audio_pdev; |
28 | struct platform_device *pdev; |
29 | struct drm_device *drm_dev; |
30 | struct dp_catalog *catalog; |
31 | struct dp_panel *panel; |
32 | |
33 | bool engine_on; |
34 | u32 channels; |
35 | |
36 | struct dp_audio dp_audio; |
37 | }; |
38 | |
39 | static u8 dp_audio_get_g0_value(u8 data) |
40 | { |
41 | u8 c[4]; |
42 | u8 g[4]; |
43 | u8 ret_data = 0; |
44 | u8 i; |
45 | |
46 | for (i = 0; i < 4; i++) |
47 | c[i] = (data >> i) & 0x01; |
48 | |
49 | g[0] = c[3]; |
50 | g[1] = c[0] ^ c[3]; |
51 | g[2] = c[1]; |
52 | g[3] = c[2]; |
53 | |
54 | for (i = 0; i < 4; i++) |
55 | ret_data = ((g[i] & 0x01) << i) | ret_data; |
56 | |
57 | return ret_data; |
58 | } |
59 | |
60 | static u8 dp_audio_get_g1_value(u8 data) |
61 | { |
62 | u8 c[4]; |
63 | u8 g[4]; |
64 | u8 ret_data = 0; |
65 | u8 i; |
66 | |
67 | for (i = 0; i < 4; i++) |
68 | c[i] = (data >> i) & 0x01; |
69 | |
70 | g[0] = c[0] ^ c[3]; |
71 | g[1] = c[0] ^ c[1] ^ c[3]; |
72 | g[2] = c[1] ^ c[2]; |
73 | g[3] = c[2] ^ c[3]; |
74 | |
75 | for (i = 0; i < 4; i++) |
76 | ret_data = ((g[i] & 0x01) << i) | ret_data; |
77 | |
78 | return ret_data; |
79 | } |
80 | |
81 | static u8 dp_audio_calculate_parity(u32 data) |
82 | { |
83 | u8 x0 = 0; |
84 | u8 x1 = 0; |
85 | u8 ci = 0; |
86 | u8 iData = 0; |
87 | u8 i = 0; |
88 | u8 parity_byte; |
89 | u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2; |
90 | |
91 | for (i = 0; i < num_byte; i++) { |
92 | iData = (data >> i*4) & 0xF; |
93 | |
94 | ci = iData ^ x1; |
95 | x1 = x0 ^ dp_audio_get_g1_value(data: ci); |
96 | x0 = dp_audio_get_g0_value(data: ci); |
97 | } |
98 | |
99 | parity_byte = x1 | (x0 << 4); |
100 | |
101 | return parity_byte; |
102 | } |
103 | |
104 | static u32 (struct dp_catalog *catalog, |
105 | enum dp_catalog_audio_sdp_type sdp, |
106 | enum dp_catalog_audio_header_type ) |
107 | { |
108 | catalog->sdp_type = sdp; |
109 | catalog->sdp_header = header; |
110 | dp_catalog_audio_get_header(catalog); |
111 | |
112 | return catalog->audio_data; |
113 | } |
114 | |
115 | static void (struct dp_catalog *catalog, |
116 | u32 data, |
117 | enum dp_catalog_audio_sdp_type sdp, |
118 | enum dp_catalog_audio_header_type ) |
119 | { |
120 | catalog->sdp_type = sdp; |
121 | catalog->sdp_header = header; |
122 | catalog->audio_data = data; |
123 | dp_catalog_audio_set_header(catalog); |
124 | } |
125 | |
126 | static void dp_audio_stream_sdp(struct dp_audio_private *audio) |
127 | { |
128 | struct dp_catalog *catalog = audio->catalog; |
129 | u32 value, new_value; |
130 | u8 parity_byte; |
131 | |
132 | /* Config header and parity byte 1 */ |
133 | value = dp_audio_get_header(catalog, |
134 | sdp: DP_AUDIO_SDP_STREAM, header: DP_AUDIO_SDP_HEADER_1); |
135 | |
136 | new_value = 0x02; |
137 | parity_byte = dp_audio_calculate_parity(data: new_value); |
138 | value |= ((new_value << HEADER_BYTE_1_BIT) |
139 | | (parity_byte << PARITY_BYTE_1_BIT)); |
140 | drm_dbg_dp(audio->drm_dev, |
141 | "Header Byte 1: value = 0x%x, parity_byte = 0x%x\n" , |
142 | value, parity_byte); |
143 | dp_audio_set_header(catalog, data: value, |
144 | sdp: DP_AUDIO_SDP_STREAM, header: DP_AUDIO_SDP_HEADER_1); |
145 | |
146 | /* Config header and parity byte 2 */ |
147 | value = dp_audio_get_header(catalog, |
148 | sdp: DP_AUDIO_SDP_STREAM, header: DP_AUDIO_SDP_HEADER_2); |
149 | new_value = value; |
150 | parity_byte = dp_audio_calculate_parity(data: new_value); |
151 | value |= ((new_value << HEADER_BYTE_2_BIT) |
152 | | (parity_byte << PARITY_BYTE_2_BIT)); |
153 | drm_dbg_dp(audio->drm_dev, |
154 | "Header Byte 2: value = 0x%x, parity_byte = 0x%x\n" , |
155 | value, parity_byte); |
156 | |
157 | dp_audio_set_header(catalog, data: value, |
158 | sdp: DP_AUDIO_SDP_STREAM, header: DP_AUDIO_SDP_HEADER_2); |
159 | |
160 | /* Config header and parity byte 3 */ |
161 | value = dp_audio_get_header(catalog, |
162 | sdp: DP_AUDIO_SDP_STREAM, header: DP_AUDIO_SDP_HEADER_3); |
163 | |
164 | new_value = audio->channels - 1; |
165 | parity_byte = dp_audio_calculate_parity(data: new_value); |
166 | value |= ((new_value << HEADER_BYTE_3_BIT) |
167 | | (parity_byte << PARITY_BYTE_3_BIT)); |
168 | drm_dbg_dp(audio->drm_dev, |
169 | "Header Byte 3: value = 0x%x, parity_byte = 0x%x\n" , |
170 | value, parity_byte); |
171 | |
172 | dp_audio_set_header(catalog, data: value, |
173 | sdp: DP_AUDIO_SDP_STREAM, header: DP_AUDIO_SDP_HEADER_3); |
174 | } |
175 | |
176 | static void dp_audio_timestamp_sdp(struct dp_audio_private *audio) |
177 | { |
178 | struct dp_catalog *catalog = audio->catalog; |
179 | u32 value, new_value; |
180 | u8 parity_byte; |
181 | |
182 | /* Config header and parity byte 1 */ |
183 | value = dp_audio_get_header(catalog, |
184 | sdp: DP_AUDIO_SDP_TIMESTAMP, header: DP_AUDIO_SDP_HEADER_1); |
185 | |
186 | new_value = 0x1; |
187 | parity_byte = dp_audio_calculate_parity(data: new_value); |
188 | value |= ((new_value << HEADER_BYTE_1_BIT) |
189 | | (parity_byte << PARITY_BYTE_1_BIT)); |
190 | drm_dbg_dp(audio->drm_dev, |
191 | "Header Byte 1: value = 0x%x, parity_byte = 0x%x\n" , |
192 | value, parity_byte); |
193 | dp_audio_set_header(catalog, data: value, |
194 | sdp: DP_AUDIO_SDP_TIMESTAMP, header: DP_AUDIO_SDP_HEADER_1); |
195 | |
196 | /* Config header and parity byte 2 */ |
197 | value = dp_audio_get_header(catalog, |
198 | sdp: DP_AUDIO_SDP_TIMESTAMP, header: DP_AUDIO_SDP_HEADER_2); |
199 | |
200 | new_value = 0x17; |
201 | parity_byte = dp_audio_calculate_parity(data: new_value); |
202 | value |= ((new_value << HEADER_BYTE_2_BIT) |
203 | | (parity_byte << PARITY_BYTE_2_BIT)); |
204 | drm_dbg_dp(audio->drm_dev, |
205 | "Header Byte 2: value = 0x%x, parity_byte = 0x%x\n" , |
206 | value, parity_byte); |
207 | dp_audio_set_header(catalog, data: value, |
208 | sdp: DP_AUDIO_SDP_TIMESTAMP, header: DP_AUDIO_SDP_HEADER_2); |
209 | |
210 | /* Config header and parity byte 3 */ |
211 | value = dp_audio_get_header(catalog, |
212 | sdp: DP_AUDIO_SDP_TIMESTAMP, header: DP_AUDIO_SDP_HEADER_3); |
213 | |
214 | new_value = (0x0 | (0x11 << 2)); |
215 | parity_byte = dp_audio_calculate_parity(data: new_value); |
216 | value |= ((new_value << HEADER_BYTE_3_BIT) |
217 | | (parity_byte << PARITY_BYTE_3_BIT)); |
218 | drm_dbg_dp(audio->drm_dev, |
219 | "Header Byte 3: value = 0x%x, parity_byte = 0x%x\n" , |
220 | value, parity_byte); |
221 | dp_audio_set_header(catalog, data: value, |
222 | sdp: DP_AUDIO_SDP_TIMESTAMP, header: DP_AUDIO_SDP_HEADER_3); |
223 | } |
224 | |
225 | static void dp_audio_infoframe_sdp(struct dp_audio_private *audio) |
226 | { |
227 | struct dp_catalog *catalog = audio->catalog; |
228 | u32 value, new_value; |
229 | u8 parity_byte; |
230 | |
231 | /* Config header and parity byte 1 */ |
232 | value = dp_audio_get_header(catalog, |
233 | sdp: DP_AUDIO_SDP_INFOFRAME, header: DP_AUDIO_SDP_HEADER_1); |
234 | |
235 | new_value = 0x84; |
236 | parity_byte = dp_audio_calculate_parity(data: new_value); |
237 | value |= ((new_value << HEADER_BYTE_1_BIT) |
238 | | (parity_byte << PARITY_BYTE_1_BIT)); |
239 | drm_dbg_dp(audio->drm_dev, |
240 | "Header Byte 1: value = 0x%x, parity_byte = 0x%x\n" , |
241 | value, parity_byte); |
242 | dp_audio_set_header(catalog, data: value, |
243 | sdp: DP_AUDIO_SDP_INFOFRAME, header: DP_AUDIO_SDP_HEADER_1); |
244 | |
245 | /* Config header and parity byte 2 */ |
246 | value = dp_audio_get_header(catalog, |
247 | sdp: DP_AUDIO_SDP_INFOFRAME, header: DP_AUDIO_SDP_HEADER_2); |
248 | |
249 | new_value = 0x1b; |
250 | parity_byte = dp_audio_calculate_parity(data: new_value); |
251 | value |= ((new_value << HEADER_BYTE_2_BIT) |
252 | | (parity_byte << PARITY_BYTE_2_BIT)); |
253 | drm_dbg_dp(audio->drm_dev, |
254 | "Header Byte 2: value = 0x%x, parity_byte = 0x%x\n" , |
255 | value, parity_byte); |
256 | dp_audio_set_header(catalog, data: value, |
257 | sdp: DP_AUDIO_SDP_INFOFRAME, header: DP_AUDIO_SDP_HEADER_2); |
258 | |
259 | /* Config header and parity byte 3 */ |
260 | value = dp_audio_get_header(catalog, |
261 | sdp: DP_AUDIO_SDP_INFOFRAME, header: DP_AUDIO_SDP_HEADER_3); |
262 | |
263 | new_value = (0x0 | (0x11 << 2)); |
264 | parity_byte = dp_audio_calculate_parity(data: new_value); |
265 | value |= ((new_value << HEADER_BYTE_3_BIT) |
266 | | (parity_byte << PARITY_BYTE_3_BIT)); |
267 | drm_dbg_dp(audio->drm_dev, |
268 | "Header Byte 3: value = 0x%x, parity_byte = 0x%x\n" , |
269 | new_value, parity_byte); |
270 | dp_audio_set_header(catalog, data: value, |
271 | sdp: DP_AUDIO_SDP_INFOFRAME, header: DP_AUDIO_SDP_HEADER_3); |
272 | } |
273 | |
274 | static void dp_audio_copy_management_sdp(struct dp_audio_private *audio) |
275 | { |
276 | struct dp_catalog *catalog = audio->catalog; |
277 | u32 value, new_value; |
278 | u8 parity_byte; |
279 | |
280 | /* Config header and parity byte 1 */ |
281 | value = dp_audio_get_header(catalog, |
282 | sdp: DP_AUDIO_SDP_COPYMANAGEMENT, header: DP_AUDIO_SDP_HEADER_1); |
283 | |
284 | new_value = 0x05; |
285 | parity_byte = dp_audio_calculate_parity(data: new_value); |
286 | value |= ((new_value << HEADER_BYTE_1_BIT) |
287 | | (parity_byte << PARITY_BYTE_1_BIT)); |
288 | drm_dbg_dp(audio->drm_dev, |
289 | "Header Byte 1: value = 0x%x, parity_byte = 0x%x\n" , |
290 | value, parity_byte); |
291 | dp_audio_set_header(catalog, data: value, |
292 | sdp: DP_AUDIO_SDP_COPYMANAGEMENT, header: DP_AUDIO_SDP_HEADER_1); |
293 | |
294 | /* Config header and parity byte 2 */ |
295 | value = dp_audio_get_header(catalog, |
296 | sdp: DP_AUDIO_SDP_COPYMANAGEMENT, header: DP_AUDIO_SDP_HEADER_2); |
297 | |
298 | new_value = 0x0F; |
299 | parity_byte = dp_audio_calculate_parity(data: new_value); |
300 | value |= ((new_value << HEADER_BYTE_2_BIT) |
301 | | (parity_byte << PARITY_BYTE_2_BIT)); |
302 | drm_dbg_dp(audio->drm_dev, |
303 | "Header Byte 2: value = 0x%x, parity_byte = 0x%x\n" , |
304 | value, parity_byte); |
305 | dp_audio_set_header(catalog, data: value, |
306 | sdp: DP_AUDIO_SDP_COPYMANAGEMENT, header: DP_AUDIO_SDP_HEADER_2); |
307 | |
308 | /* Config header and parity byte 3 */ |
309 | value = dp_audio_get_header(catalog, |
310 | sdp: DP_AUDIO_SDP_COPYMANAGEMENT, header: DP_AUDIO_SDP_HEADER_3); |
311 | |
312 | new_value = 0x0; |
313 | parity_byte = dp_audio_calculate_parity(data: new_value); |
314 | value |= ((new_value << HEADER_BYTE_3_BIT) |
315 | | (parity_byte << PARITY_BYTE_3_BIT)); |
316 | drm_dbg_dp(audio->drm_dev, |
317 | "Header Byte 3: value = 0x%x, parity_byte = 0x%x\n" , |
318 | value, parity_byte); |
319 | dp_audio_set_header(catalog, data: value, |
320 | sdp: DP_AUDIO_SDP_COPYMANAGEMENT, header: DP_AUDIO_SDP_HEADER_3); |
321 | } |
322 | |
323 | static void dp_audio_isrc_sdp(struct dp_audio_private *audio) |
324 | { |
325 | struct dp_catalog *catalog = audio->catalog; |
326 | u32 value, new_value; |
327 | u8 parity_byte; |
328 | |
329 | /* Config header and parity byte 1 */ |
330 | value = dp_audio_get_header(catalog, |
331 | sdp: DP_AUDIO_SDP_ISRC, header: DP_AUDIO_SDP_HEADER_1); |
332 | |
333 | new_value = 0x06; |
334 | parity_byte = dp_audio_calculate_parity(data: new_value); |
335 | value |= ((new_value << HEADER_BYTE_1_BIT) |
336 | | (parity_byte << PARITY_BYTE_1_BIT)); |
337 | drm_dbg_dp(audio->drm_dev, |
338 | "Header Byte 1: value = 0x%x, parity_byte = 0x%x\n" , |
339 | value, parity_byte); |
340 | dp_audio_set_header(catalog, data: value, |
341 | sdp: DP_AUDIO_SDP_ISRC, header: DP_AUDIO_SDP_HEADER_1); |
342 | |
343 | /* Config header and parity byte 2 */ |
344 | value = dp_audio_get_header(catalog, |
345 | sdp: DP_AUDIO_SDP_ISRC, header: DP_AUDIO_SDP_HEADER_2); |
346 | |
347 | new_value = 0x0F; |
348 | parity_byte = dp_audio_calculate_parity(data: new_value); |
349 | value |= ((new_value << HEADER_BYTE_2_BIT) |
350 | | (parity_byte << PARITY_BYTE_2_BIT)); |
351 | drm_dbg_dp(audio->drm_dev, |
352 | "Header Byte 2: value = 0x%x, parity_byte = 0x%x\n" , |
353 | value, parity_byte); |
354 | dp_audio_set_header(catalog, data: value, |
355 | sdp: DP_AUDIO_SDP_ISRC, header: DP_AUDIO_SDP_HEADER_2); |
356 | } |
357 | |
358 | static void dp_audio_setup_sdp(struct dp_audio_private *audio) |
359 | { |
360 | dp_catalog_audio_config_sdp(catalog: audio->catalog); |
361 | |
362 | dp_audio_stream_sdp(audio); |
363 | dp_audio_timestamp_sdp(audio); |
364 | dp_audio_infoframe_sdp(audio); |
365 | dp_audio_copy_management_sdp(audio); |
366 | dp_audio_isrc_sdp(audio); |
367 | } |
368 | |
369 | static void dp_audio_setup_acr(struct dp_audio_private *audio) |
370 | { |
371 | u32 select = 0; |
372 | struct dp_catalog *catalog = audio->catalog; |
373 | |
374 | switch (audio->dp_audio.bw_code) { |
375 | case DP_LINK_BW_1_62: |
376 | select = 0; |
377 | break; |
378 | case DP_LINK_BW_2_7: |
379 | select = 1; |
380 | break; |
381 | case DP_LINK_BW_5_4: |
382 | select = 2; |
383 | break; |
384 | case DP_LINK_BW_8_1: |
385 | select = 3; |
386 | break; |
387 | default: |
388 | drm_dbg_dp(audio->drm_dev, "Unknown link rate\n" ); |
389 | select = 0; |
390 | break; |
391 | } |
392 | |
393 | catalog->audio_data = select; |
394 | dp_catalog_audio_config_acr(catalog); |
395 | } |
396 | |
397 | static void dp_audio_safe_to_exit_level(struct dp_audio_private *audio) |
398 | { |
399 | struct dp_catalog *catalog = audio->catalog; |
400 | u32 safe_to_exit_level = 0; |
401 | |
402 | switch (audio->dp_audio.lane_count) { |
403 | case 1: |
404 | safe_to_exit_level = 14; |
405 | break; |
406 | case 2: |
407 | safe_to_exit_level = 8; |
408 | break; |
409 | case 4: |
410 | safe_to_exit_level = 5; |
411 | break; |
412 | default: |
413 | drm_dbg_dp(audio->drm_dev, |
414 | "setting the default safe_to_exit_level = %u\n" , |
415 | safe_to_exit_level); |
416 | safe_to_exit_level = 14; |
417 | break; |
418 | } |
419 | |
420 | catalog->audio_data = safe_to_exit_level; |
421 | dp_catalog_audio_sfe_level(catalog); |
422 | } |
423 | |
424 | static void dp_audio_enable(struct dp_audio_private *audio, bool enable) |
425 | { |
426 | struct dp_catalog *catalog = audio->catalog; |
427 | |
428 | catalog->audio_data = enable; |
429 | dp_catalog_audio_enable(catalog); |
430 | |
431 | audio->engine_on = enable; |
432 | } |
433 | |
434 | static struct dp_audio_private *dp_audio_get_data(struct platform_device *pdev) |
435 | { |
436 | struct dp_audio *dp_audio; |
437 | struct msm_dp *dp_display; |
438 | |
439 | if (!pdev) { |
440 | DRM_ERROR("invalid input\n" ); |
441 | return ERR_PTR(error: -ENODEV); |
442 | } |
443 | |
444 | dp_display = platform_get_drvdata(pdev); |
445 | if (!dp_display) { |
446 | DRM_ERROR("invalid input\n" ); |
447 | return ERR_PTR(error: -ENODEV); |
448 | } |
449 | |
450 | dp_audio = dp_display->dp_audio; |
451 | |
452 | if (!dp_audio) { |
453 | DRM_ERROR("invalid dp_audio data\n" ); |
454 | return ERR_PTR(error: -EINVAL); |
455 | } |
456 | |
457 | return container_of(dp_audio, struct dp_audio_private, dp_audio); |
458 | } |
459 | |
460 | static int dp_audio_hook_plugged_cb(struct device *dev, void *data, |
461 | hdmi_codec_plugged_cb fn, |
462 | struct device *codec_dev) |
463 | { |
464 | |
465 | struct platform_device *pdev; |
466 | struct msm_dp *dp_display; |
467 | |
468 | pdev = to_platform_device(dev); |
469 | if (!pdev) { |
470 | pr_err("invalid input\n" ); |
471 | return -ENODEV; |
472 | } |
473 | |
474 | dp_display = platform_get_drvdata(pdev); |
475 | if (!dp_display) { |
476 | pr_err("invalid input\n" ); |
477 | return -ENODEV; |
478 | } |
479 | |
480 | return dp_display_set_plugged_cb(dp_display, fn, codec_dev); |
481 | } |
482 | |
483 | static int dp_audio_get_eld(struct device *dev, |
484 | void *data, uint8_t *buf, size_t len) |
485 | { |
486 | struct platform_device *pdev; |
487 | struct msm_dp *dp_display; |
488 | |
489 | pdev = to_platform_device(dev); |
490 | |
491 | if (!pdev) { |
492 | DRM_ERROR("invalid input\n" ); |
493 | return -ENODEV; |
494 | } |
495 | |
496 | dp_display = platform_get_drvdata(pdev); |
497 | if (!dp_display) { |
498 | DRM_ERROR("invalid input\n" ); |
499 | return -ENODEV; |
500 | } |
501 | |
502 | memcpy(buf, dp_display->connector->eld, |
503 | min(sizeof(dp_display->connector->eld), len)); |
504 | |
505 | return 0; |
506 | } |
507 | |
508 | int dp_audio_hw_params(struct device *dev, |
509 | void *data, |
510 | struct hdmi_codec_daifmt *daifmt, |
511 | struct hdmi_codec_params *params) |
512 | { |
513 | int rc = 0; |
514 | struct dp_audio_private *audio; |
515 | struct platform_device *pdev; |
516 | struct msm_dp *dp_display; |
517 | |
518 | pdev = to_platform_device(dev); |
519 | dp_display = platform_get_drvdata(pdev); |
520 | |
521 | /* |
522 | * there could be cases where sound card can be opened even |
523 | * before OR even when DP is not connected . This can cause |
524 | * unclocked access as the audio subsystem relies on the DP |
525 | * driver to maintain the correct state of clocks. To protect |
526 | * such cases check for connection status and bail out if not |
527 | * connected. |
528 | */ |
529 | if (!dp_display->power_on) { |
530 | rc = -EINVAL; |
531 | goto end; |
532 | } |
533 | |
534 | audio = dp_audio_get_data(pdev); |
535 | if (IS_ERR(ptr: audio)) { |
536 | rc = PTR_ERR(ptr: audio); |
537 | goto end; |
538 | } |
539 | |
540 | audio->channels = params->channels; |
541 | |
542 | dp_audio_setup_sdp(audio); |
543 | dp_audio_setup_acr(audio); |
544 | dp_audio_safe_to_exit_level(audio); |
545 | dp_audio_enable(audio, enable: true); |
546 | dp_display_signal_audio_start(dp_display); |
547 | dp_display->audio_enabled = true; |
548 | |
549 | end: |
550 | return rc; |
551 | } |
552 | |
553 | static void dp_audio_shutdown(struct device *dev, void *data) |
554 | { |
555 | struct dp_audio_private *audio; |
556 | struct platform_device *pdev; |
557 | struct msm_dp *dp_display; |
558 | |
559 | pdev = to_platform_device(dev); |
560 | dp_display = platform_get_drvdata(pdev); |
561 | audio = dp_audio_get_data(pdev); |
562 | if (IS_ERR(ptr: audio)) { |
563 | DRM_ERROR("failed to get audio data\n" ); |
564 | return; |
565 | } |
566 | |
567 | /* |
568 | * if audio was not enabled there is no need |
569 | * to execute the shutdown and we can bail out early. |
570 | * This also makes sure that we dont cause an unclocked |
571 | * access when audio subsystem calls this without DP being |
572 | * connected. is_connected cannot be used here as its set |
573 | * to false earlier than this call |
574 | */ |
575 | if (!dp_display->audio_enabled) |
576 | return; |
577 | |
578 | dp_audio_enable(audio, enable: false); |
579 | /* signal the dp display to safely shutdown clocks */ |
580 | dp_display_signal_audio_complete(dp_display); |
581 | } |
582 | |
583 | static const struct hdmi_codec_ops dp_audio_codec_ops = { |
584 | .hw_params = dp_audio_hw_params, |
585 | .audio_shutdown = dp_audio_shutdown, |
586 | .get_eld = dp_audio_get_eld, |
587 | .hook_plugged_cb = dp_audio_hook_plugged_cb, |
588 | }; |
589 | |
590 | static struct hdmi_codec_pdata codec_data = { |
591 | .ops = &dp_audio_codec_ops, |
592 | .max_i2s_channels = 8, |
593 | .i2s = 1, |
594 | }; |
595 | |
596 | void dp_unregister_audio_driver(struct device *dev, struct dp_audio *dp_audio) |
597 | { |
598 | struct dp_audio_private *audio_priv; |
599 | |
600 | audio_priv = container_of(dp_audio, struct dp_audio_private, dp_audio); |
601 | |
602 | if (audio_priv->audio_pdev) { |
603 | platform_device_unregister(audio_priv->audio_pdev); |
604 | audio_priv->audio_pdev = NULL; |
605 | } |
606 | } |
607 | |
608 | int dp_register_audio_driver(struct device *dev, |
609 | struct dp_audio *dp_audio) |
610 | { |
611 | struct dp_audio_private *audio_priv; |
612 | |
613 | audio_priv = container_of(dp_audio, |
614 | struct dp_audio_private, dp_audio); |
615 | |
616 | audio_priv->audio_pdev = platform_device_register_data(parent: dev, |
617 | HDMI_CODEC_DRV_NAME, |
618 | PLATFORM_DEVID_AUTO, |
619 | data: &codec_data, |
620 | size: sizeof(codec_data)); |
621 | return PTR_ERR_OR_ZERO(ptr: audio_priv->audio_pdev); |
622 | } |
623 | |
624 | struct dp_audio *dp_audio_get(struct platform_device *pdev, |
625 | struct dp_panel *panel, |
626 | struct dp_catalog *catalog) |
627 | { |
628 | int rc = 0; |
629 | struct dp_audio_private *audio; |
630 | struct dp_audio *dp_audio; |
631 | |
632 | if (!pdev || !panel || !catalog) { |
633 | DRM_ERROR("invalid input\n" ); |
634 | rc = -EINVAL; |
635 | goto error; |
636 | } |
637 | |
638 | audio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*audio), GFP_KERNEL); |
639 | if (!audio) { |
640 | rc = -ENOMEM; |
641 | goto error; |
642 | } |
643 | |
644 | audio->pdev = pdev; |
645 | audio->panel = panel; |
646 | audio->catalog = catalog; |
647 | |
648 | dp_audio = &audio->dp_audio; |
649 | |
650 | dp_catalog_audio_init(catalog); |
651 | |
652 | return dp_audio; |
653 | error: |
654 | return ERR_PTR(error: rc); |
655 | } |
656 | |
657 | void dp_audio_put(struct dp_audio *dp_audio) |
658 | { |
659 | struct dp_audio_private *audio; |
660 | |
661 | if (!dp_audio) |
662 | return; |
663 | |
664 | audio = container_of(dp_audio, struct dp_audio_private, dp_audio); |
665 | |
666 | devm_kfree(dev: &audio->pdev->dev, p: audio); |
667 | } |
668 | |