.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 Avionic Design GmbH |
---|
3 | 4 | * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify |
---|
6 | | - * it under the terms of the GNU General Public License version 2 as |
---|
7 | | - * published by the Free Software Foundation. |
---|
8 | 5 | */ |
---|
9 | 6 | |
---|
10 | 7 | #include <linux/clk.h> |
---|
11 | 8 | #include <linux/debugfs.h> |
---|
12 | | -#include <linux/gpio.h> |
---|
| 9 | +#include <linux/delay.h> |
---|
13 | 10 | #include <linux/hdmi.h> |
---|
| 11 | +#include <linux/math64.h> |
---|
| 12 | +#include <linux/module.h> |
---|
14 | 13 | #include <linux/of_device.h> |
---|
15 | 14 | #include <linux/pm_runtime.h> |
---|
16 | 15 | #include <linux/regulator/consumer.h> |
---|
.. | .. |
---|
18 | 17 | |
---|
19 | 18 | #include <drm/drm_atomic_helper.h> |
---|
20 | 19 | #include <drm/drm_crtc.h> |
---|
21 | | -#include <drm/drm_crtc_helper.h> |
---|
| 20 | +#include <drm/drm_debugfs.h> |
---|
| 21 | +#include <drm/drm_file.h> |
---|
| 22 | +#include <drm/drm_fourcc.h> |
---|
| 23 | +#include <drm/drm_probe_helper.h> |
---|
| 24 | +#include <drm/drm_simple_kms_helper.h> |
---|
22 | 25 | |
---|
23 | | -#include <sound/hda_verbs.h> |
---|
24 | | - |
---|
25 | | -#include <media/cec-notifier.h> |
---|
26 | | - |
---|
| 26 | +#include "hda.h" |
---|
27 | 27 | #include "hdmi.h" |
---|
28 | 28 | #include "drm.h" |
---|
29 | 29 | #include "dc.h" |
---|
.. | .. |
---|
71 | 71 | const struct tegra_hdmi_config *config; |
---|
72 | 72 | |
---|
73 | 73 | unsigned int audio_source; |
---|
74 | | - unsigned int audio_sample_rate; |
---|
75 | | - unsigned int audio_channels; |
---|
| 74 | + struct tegra_hda_format format; |
---|
76 | 75 | |
---|
77 | 76 | unsigned int pixel_clock; |
---|
78 | 77 | bool stereo; |
---|
.. | .. |
---|
119 | 118 | } |
---|
120 | 119 | |
---|
121 | 120 | struct tegra_hdmi_audio_config { |
---|
122 | | - unsigned int pclk; |
---|
123 | 121 | unsigned int n; |
---|
124 | 122 | unsigned int cts; |
---|
125 | 123 | unsigned int aval; |
---|
126 | | -}; |
---|
127 | | - |
---|
128 | | -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = { |
---|
129 | | - { 25200000, 4096, 25200, 24000 }, |
---|
130 | | - { 27000000, 4096, 27000, 24000 }, |
---|
131 | | - { 74250000, 4096, 74250, 24000 }, |
---|
132 | | - { 148500000, 4096, 148500, 24000 }, |
---|
133 | | - { 0, 0, 0, 0 }, |
---|
134 | | -}; |
---|
135 | | - |
---|
136 | | -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = { |
---|
137 | | - { 25200000, 5880, 26250, 25000 }, |
---|
138 | | - { 27000000, 5880, 28125, 25000 }, |
---|
139 | | - { 74250000, 4704, 61875, 20000 }, |
---|
140 | | - { 148500000, 4704, 123750, 20000 }, |
---|
141 | | - { 0, 0, 0, 0 }, |
---|
142 | | -}; |
---|
143 | | - |
---|
144 | | -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_48k[] = { |
---|
145 | | - { 25200000, 6144, 25200, 24000 }, |
---|
146 | | - { 27000000, 6144, 27000, 24000 }, |
---|
147 | | - { 74250000, 6144, 74250, 24000 }, |
---|
148 | | - { 148500000, 6144, 148500, 24000 }, |
---|
149 | | - { 0, 0, 0, 0 }, |
---|
150 | | -}; |
---|
151 | | - |
---|
152 | | -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_88_2k[] = { |
---|
153 | | - { 25200000, 11760, 26250, 25000 }, |
---|
154 | | - { 27000000, 11760, 28125, 25000 }, |
---|
155 | | - { 74250000, 9408, 61875, 20000 }, |
---|
156 | | - { 148500000, 9408, 123750, 20000 }, |
---|
157 | | - { 0, 0, 0, 0 }, |
---|
158 | | -}; |
---|
159 | | - |
---|
160 | | -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_96k[] = { |
---|
161 | | - { 25200000, 12288, 25200, 24000 }, |
---|
162 | | - { 27000000, 12288, 27000, 24000 }, |
---|
163 | | - { 74250000, 12288, 74250, 24000 }, |
---|
164 | | - { 148500000, 12288, 148500, 24000 }, |
---|
165 | | - { 0, 0, 0, 0 }, |
---|
166 | | -}; |
---|
167 | | - |
---|
168 | | -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_176_4k[] = { |
---|
169 | | - { 25200000, 23520, 26250, 25000 }, |
---|
170 | | - { 27000000, 23520, 28125, 25000 }, |
---|
171 | | - { 74250000, 18816, 61875, 20000 }, |
---|
172 | | - { 148500000, 18816, 123750, 20000 }, |
---|
173 | | - { 0, 0, 0, 0 }, |
---|
174 | | -}; |
---|
175 | | - |
---|
176 | | -static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = { |
---|
177 | | - { 25200000, 24576, 25200, 24000 }, |
---|
178 | | - { 27000000, 24576, 27000, 24000 }, |
---|
179 | | - { 74250000, 24576, 74250, 24000 }, |
---|
180 | | - { 148500000, 24576, 148500, 24000 }, |
---|
181 | | - { 0, 0, 0, 0 }, |
---|
182 | 124 | }; |
---|
183 | 125 | |
---|
184 | 126 | static const struct tmds_config tegra20_tmds_config[] = { |
---|
.. | .. |
---|
418 | 360 | }, |
---|
419 | 361 | }; |
---|
420 | 362 | |
---|
421 | | -static const struct tegra_hdmi_audio_config * |
---|
422 | | -tegra_hdmi_get_audio_config(unsigned int sample_rate, unsigned int pclk) |
---|
| 363 | +static int |
---|
| 364 | +tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pix_clock, |
---|
| 365 | + struct tegra_hdmi_audio_config *config) |
---|
423 | 366 | { |
---|
424 | | - const struct tegra_hdmi_audio_config *table; |
---|
| 367 | + const unsigned int afreq = 128 * audio_freq; |
---|
| 368 | + const unsigned int min_n = afreq / 1500; |
---|
| 369 | + const unsigned int max_n = afreq / 300; |
---|
| 370 | + const unsigned int ideal_n = afreq / 1000; |
---|
| 371 | + int64_t min_err = (uint64_t)-1 >> 1; |
---|
| 372 | + unsigned int min_delta = -1; |
---|
| 373 | + int n; |
---|
425 | 374 | |
---|
426 | | - switch (sample_rate) { |
---|
427 | | - case 32000: |
---|
428 | | - table = tegra_hdmi_audio_32k; |
---|
429 | | - break; |
---|
| 375 | + memset(config, 0, sizeof(*config)); |
---|
| 376 | + config->n = -1; |
---|
430 | 377 | |
---|
431 | | - case 44100: |
---|
432 | | - table = tegra_hdmi_audio_44_1k; |
---|
433 | | - break; |
---|
| 378 | + for (n = min_n; n <= max_n; n++) { |
---|
| 379 | + uint64_t cts_f, aval_f; |
---|
| 380 | + unsigned int delta; |
---|
| 381 | + int64_t cts, err; |
---|
434 | 382 | |
---|
435 | | - case 48000: |
---|
436 | | - table = tegra_hdmi_audio_48k; |
---|
437 | | - break; |
---|
| 383 | + /* compute aval in 48.16 fixed point */ |
---|
| 384 | + aval_f = ((int64_t)24000000 << 16) * n; |
---|
| 385 | + do_div(aval_f, afreq); |
---|
| 386 | + /* It should round without any rest */ |
---|
| 387 | + if (aval_f & 0xFFFF) |
---|
| 388 | + continue; |
---|
438 | 389 | |
---|
439 | | - case 88200: |
---|
440 | | - table = tegra_hdmi_audio_88_2k; |
---|
441 | | - break; |
---|
| 390 | + /* Compute cts in 48.16 fixed point */ |
---|
| 391 | + cts_f = ((int64_t)pix_clock << 16) * n; |
---|
| 392 | + do_div(cts_f, afreq); |
---|
| 393 | + /* Round it to the nearest integer */ |
---|
| 394 | + cts = (cts_f & ~0xFFFF) + ((cts_f & BIT(15)) << 1); |
---|
442 | 395 | |
---|
443 | | - case 96000: |
---|
444 | | - table = tegra_hdmi_audio_96k; |
---|
445 | | - break; |
---|
| 396 | + delta = abs(n - ideal_n); |
---|
446 | 397 | |
---|
447 | | - case 176400: |
---|
448 | | - table = tegra_hdmi_audio_176_4k; |
---|
449 | | - break; |
---|
450 | | - |
---|
451 | | - case 192000: |
---|
452 | | - table = tegra_hdmi_audio_192k; |
---|
453 | | - break; |
---|
454 | | - |
---|
455 | | - default: |
---|
456 | | - return NULL; |
---|
| 398 | + /* Compute the absolute error */ |
---|
| 399 | + err = abs((int64_t)cts_f - cts); |
---|
| 400 | + if (err < min_err || (err == min_err && delta < min_delta)) { |
---|
| 401 | + config->n = n; |
---|
| 402 | + config->cts = cts >> 16; |
---|
| 403 | + config->aval = aval_f >> 16; |
---|
| 404 | + min_delta = delta; |
---|
| 405 | + min_err = err; |
---|
| 406 | + } |
---|
457 | 407 | } |
---|
458 | 408 | |
---|
459 | | - while (table->pclk) { |
---|
460 | | - if (table->pclk == pclk) |
---|
461 | | - return table; |
---|
462 | | - |
---|
463 | | - table++; |
---|
464 | | - } |
---|
465 | | - |
---|
466 | | - return NULL; |
---|
| 409 | + return config->n != -1 ? 0 : -EINVAL; |
---|
467 | 410 | } |
---|
468 | 411 | |
---|
469 | 412 | static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi) |
---|
.. | .. |
---|
510 | 453 | unsigned int i; |
---|
511 | 454 | |
---|
512 | 455 | for (i = 0; i < ARRAY_SIZE(regs); i++) { |
---|
513 | | - if (regs[i].sample_rate == hdmi->audio_sample_rate) { |
---|
| 456 | + if (regs[i].sample_rate == hdmi->format.sample_rate) { |
---|
514 | 457 | tegra_hdmi_writel(hdmi, value, regs[i].offset); |
---|
515 | 458 | break; |
---|
516 | 459 | } |
---|
.. | .. |
---|
519 | 462 | |
---|
520 | 463 | static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) |
---|
521 | 464 | { |
---|
522 | | - const struct tegra_hdmi_audio_config *config; |
---|
| 465 | + struct tegra_hdmi_audio_config config; |
---|
523 | 466 | u32 source, value; |
---|
| 467 | + int err; |
---|
524 | 468 | |
---|
525 | 469 | switch (hdmi->audio_source) { |
---|
526 | 470 | case HDA: |
---|
.. | .. |
---|
564 | 508 | * play back system startup sounds early. It is possibly not |
---|
565 | 509 | * needed on Linux at all. |
---|
566 | 510 | */ |
---|
567 | | - if (hdmi->audio_channels == 2) |
---|
| 511 | + if (hdmi->format.channels == 2) |
---|
568 | 512 | value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL; |
---|
569 | 513 | else |
---|
570 | 514 | value = 0; |
---|
.. | .. |
---|
595 | 539 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); |
---|
596 | 540 | } |
---|
597 | 541 | |
---|
598 | | - config = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate, |
---|
599 | | - hdmi->pixel_clock); |
---|
600 | | - if (!config) { |
---|
| 542 | + err = tegra_hdmi_get_audio_config(hdmi->format.sample_rate, |
---|
| 543 | + hdmi->pixel_clock, &config); |
---|
| 544 | + if (err < 0) { |
---|
601 | 545 | dev_err(hdmi->dev, |
---|
602 | 546 | "cannot set audio to %u Hz at %u Hz pixel clock\n", |
---|
603 | | - hdmi->audio_sample_rate, hdmi->pixel_clock); |
---|
604 | | - return -EINVAL; |
---|
| 547 | + hdmi->format.sample_rate, hdmi->pixel_clock); |
---|
| 548 | + return err; |
---|
605 | 549 | } |
---|
| 550 | + |
---|
| 551 | + dev_dbg(hdmi->dev, "audio: pixclk=%u, n=%u, cts=%u, aval=%u\n", |
---|
| 552 | + hdmi->pixel_clock, config.n, config.cts, config.aval); |
---|
606 | 553 | |
---|
607 | 554 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL); |
---|
608 | 555 | |
---|
609 | 556 | value = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE | |
---|
610 | | - AUDIO_N_VALUE(config->n - 1); |
---|
| 557 | + AUDIO_N_VALUE(config.n - 1); |
---|
611 | 558 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); |
---|
612 | 559 | |
---|
613 | | - tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE, |
---|
| 560 | + tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config.n) | ACR_ENABLE, |
---|
614 | 561 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); |
---|
615 | 562 | |
---|
616 | | - tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts), |
---|
| 563 | + tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config.cts), |
---|
617 | 564 | HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); |
---|
618 | 565 | |
---|
619 | 566 | value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1); |
---|
.. | .. |
---|
624 | 571 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); |
---|
625 | 572 | |
---|
626 | 573 | if (hdmi->config->has_hda) |
---|
627 | | - tegra_hdmi_write_aval(hdmi, config->aval); |
---|
| 574 | + tegra_hdmi_write_aval(hdmi, config.aval); |
---|
628 | 575 | |
---|
629 | 576 | tegra_hdmi_setup_audio_fs_tables(hdmi); |
---|
630 | 577 | |
---|
.. | .. |
---|
741 | 688 | u8 buffer[17]; |
---|
742 | 689 | ssize_t err; |
---|
743 | 690 | |
---|
744 | | - err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode, false); |
---|
| 691 | + err = drm_hdmi_avi_infoframe_from_display_mode(&frame, |
---|
| 692 | + &hdmi->output.connector, mode); |
---|
745 | 693 | if (err < 0) { |
---|
746 | 694 | dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err); |
---|
747 | 695 | return; |
---|
.. | .. |
---|
787 | 735 | return; |
---|
788 | 736 | } |
---|
789 | 737 | |
---|
790 | | - frame.channels = hdmi->audio_channels; |
---|
| 738 | + frame.channels = hdmi->format.channels; |
---|
791 | 739 | |
---|
792 | 740 | err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); |
---|
793 | 741 | if (err < 0) { |
---|
.. | .. |
---|
1116 | 1064 | struct drm_minor *minor = connector->dev->primary; |
---|
1117 | 1065 | struct dentry *root = connector->debugfs_entry; |
---|
1118 | 1066 | struct tegra_hdmi *hdmi = to_hdmi(output); |
---|
1119 | | - int err; |
---|
1120 | 1067 | |
---|
1121 | 1068 | hdmi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), |
---|
1122 | 1069 | GFP_KERNEL); |
---|
.. | .. |
---|
1126 | 1073 | for (i = 0; i < count; i++) |
---|
1127 | 1074 | hdmi->debugfs_files[i].data = hdmi; |
---|
1128 | 1075 | |
---|
1129 | | - err = drm_debugfs_create_files(hdmi->debugfs_files, count, root, minor); |
---|
1130 | | - if (err < 0) |
---|
1131 | | - goto free; |
---|
| 1076 | + drm_debugfs_create_files(hdmi->debugfs_files, count, root, minor); |
---|
1132 | 1077 | |
---|
1133 | 1078 | return 0; |
---|
1134 | | - |
---|
1135 | | -free: |
---|
1136 | | - kfree(hdmi->debugfs_files); |
---|
1137 | | - hdmi->debugfs_files = NULL; |
---|
1138 | | - |
---|
1139 | | - return err; |
---|
1140 | 1079 | } |
---|
1141 | 1080 | |
---|
1142 | 1081 | static void tegra_hdmi_early_unregister(struct drm_connector *connector) |
---|
.. | .. |
---|
1188 | 1127 | .mode_valid = tegra_hdmi_connector_mode_valid, |
---|
1189 | 1128 | }; |
---|
1190 | 1129 | |
---|
1191 | | -static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = { |
---|
1192 | | - .destroy = tegra_output_encoder_destroy, |
---|
1193 | | -}; |
---|
1194 | | - |
---|
1195 | 1130 | static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) |
---|
1196 | 1131 | { |
---|
1197 | 1132 | struct tegra_output *output = encoder_to_output(encoder); |
---|
1198 | 1133 | struct tegra_dc *dc = to_tegra_dc(encoder->crtc); |
---|
1199 | 1134 | struct tegra_hdmi *hdmi = to_hdmi(output); |
---|
1200 | 1135 | u32 value; |
---|
| 1136 | + int err; |
---|
1201 | 1137 | |
---|
1202 | 1138 | /* |
---|
1203 | 1139 | * The following accesses registers of the display controller, so make |
---|
.. | .. |
---|
1223 | 1159 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_ENABLE); |
---|
1224 | 1160 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_MASK); |
---|
1225 | 1161 | |
---|
1226 | | - pm_runtime_put(hdmi->dev); |
---|
| 1162 | + err = host1x_client_suspend(&hdmi->client); |
---|
| 1163 | + if (err < 0) |
---|
| 1164 | + dev_err(hdmi->dev, "failed to suspend: %d\n", err); |
---|
1227 | 1165 | } |
---|
1228 | 1166 | |
---|
1229 | 1167 | static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) |
---|
.. | .. |
---|
1238 | 1176 | u32 value; |
---|
1239 | 1177 | int err; |
---|
1240 | 1178 | |
---|
1241 | | - pm_runtime_get_sync(hdmi->dev); |
---|
| 1179 | + err = host1x_client_resume(&hdmi->client); |
---|
| 1180 | + if (err < 0) { |
---|
| 1181 | + dev_err(hdmi->dev, "failed to resume: %d\n", err); |
---|
| 1182 | + return; |
---|
| 1183 | + } |
---|
1242 | 1184 | |
---|
1243 | 1185 | /* |
---|
1244 | 1186 | * Enable and unmask the HDA codec SCRATCH0 register interrupt. This |
---|
.. | .. |
---|
1314 | 1256 | |
---|
1315 | 1257 | hdmi->dvi = !tegra_output_is_hdmi(output); |
---|
1316 | 1258 | if (!hdmi->dvi) { |
---|
1317 | | - err = tegra_hdmi_setup_audio(hdmi); |
---|
1318 | | - if (err < 0) |
---|
1319 | | - hdmi->dvi = true; |
---|
| 1259 | + /* |
---|
| 1260 | + * Make sure that the audio format has been configured before |
---|
| 1261 | + * enabling audio, otherwise we may try to divide by zero. |
---|
| 1262 | + */ |
---|
| 1263 | + if (hdmi->format.sample_rate > 0) { |
---|
| 1264 | + err = tegra_hdmi_setup_audio(hdmi); |
---|
| 1265 | + if (err < 0) |
---|
| 1266 | + hdmi->dvi = true; |
---|
| 1267 | + } |
---|
1320 | 1268 | } |
---|
1321 | 1269 | |
---|
1322 | 1270 | if (hdmi->config->has_hda) |
---|
.. | .. |
---|
1470 | 1418 | |
---|
1471 | 1419 | static int tegra_hdmi_init(struct host1x_client *client) |
---|
1472 | 1420 | { |
---|
1473 | | - struct drm_device *drm = dev_get_drvdata(client->parent); |
---|
1474 | 1421 | struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); |
---|
| 1422 | + struct drm_device *drm = dev_get_drvdata(client->host); |
---|
1475 | 1423 | int err; |
---|
1476 | 1424 | |
---|
1477 | 1425 | hdmi->output.dev = client->dev; |
---|
1478 | 1426 | |
---|
1479 | | - drm_connector_init(drm, &hdmi->output.connector, |
---|
1480 | | - &tegra_hdmi_connector_funcs, |
---|
1481 | | - DRM_MODE_CONNECTOR_HDMIA); |
---|
| 1427 | + drm_connector_init_with_ddc(drm, &hdmi->output.connector, |
---|
| 1428 | + &tegra_hdmi_connector_funcs, |
---|
| 1429 | + DRM_MODE_CONNECTOR_HDMIA, |
---|
| 1430 | + hdmi->output.ddc); |
---|
1482 | 1431 | drm_connector_helper_add(&hdmi->output.connector, |
---|
1483 | 1432 | &tegra_hdmi_connector_helper_funcs); |
---|
1484 | 1433 | hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF; |
---|
1485 | 1434 | |
---|
1486 | | - drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs, |
---|
1487 | | - DRM_MODE_ENCODER_TMDS, NULL); |
---|
| 1435 | + drm_simple_encoder_init(drm, &hdmi->output.encoder, |
---|
| 1436 | + DRM_MODE_ENCODER_TMDS); |
---|
1488 | 1437 | drm_encoder_helper_add(&hdmi->output.encoder, |
---|
1489 | 1438 | &tegra_hdmi_encoder_helper_funcs); |
---|
1490 | 1439 | |
---|
.. | .. |
---|
1535 | 1484 | return 0; |
---|
1536 | 1485 | } |
---|
1537 | 1486 | |
---|
| 1487 | +static int tegra_hdmi_runtime_suspend(struct host1x_client *client) |
---|
| 1488 | +{ |
---|
| 1489 | + struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); |
---|
| 1490 | + struct device *dev = client->dev; |
---|
| 1491 | + int err; |
---|
| 1492 | + |
---|
| 1493 | + err = reset_control_assert(hdmi->rst); |
---|
| 1494 | + if (err < 0) { |
---|
| 1495 | + dev_err(dev, "failed to assert reset: %d\n", err); |
---|
| 1496 | + return err; |
---|
| 1497 | + } |
---|
| 1498 | + |
---|
| 1499 | + usleep_range(1000, 2000); |
---|
| 1500 | + |
---|
| 1501 | + clk_disable_unprepare(hdmi->clk); |
---|
| 1502 | + pm_runtime_put_sync(dev); |
---|
| 1503 | + |
---|
| 1504 | + return 0; |
---|
| 1505 | +} |
---|
| 1506 | + |
---|
| 1507 | +static int tegra_hdmi_runtime_resume(struct host1x_client *client) |
---|
| 1508 | +{ |
---|
| 1509 | + struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); |
---|
| 1510 | + struct device *dev = client->dev; |
---|
| 1511 | + int err; |
---|
| 1512 | + |
---|
| 1513 | + err = pm_runtime_resume_and_get(dev); |
---|
| 1514 | + if (err < 0) { |
---|
| 1515 | + dev_err(dev, "failed to get runtime PM: %d\n", err); |
---|
| 1516 | + return err; |
---|
| 1517 | + } |
---|
| 1518 | + |
---|
| 1519 | + err = clk_prepare_enable(hdmi->clk); |
---|
| 1520 | + if (err < 0) { |
---|
| 1521 | + dev_err(dev, "failed to enable clock: %d\n", err); |
---|
| 1522 | + goto put_rpm; |
---|
| 1523 | + } |
---|
| 1524 | + |
---|
| 1525 | + usleep_range(1000, 2000); |
---|
| 1526 | + |
---|
| 1527 | + err = reset_control_deassert(hdmi->rst); |
---|
| 1528 | + if (err < 0) { |
---|
| 1529 | + dev_err(dev, "failed to deassert reset: %d\n", err); |
---|
| 1530 | + goto disable_clk; |
---|
| 1531 | + } |
---|
| 1532 | + |
---|
| 1533 | + return 0; |
---|
| 1534 | + |
---|
| 1535 | +disable_clk: |
---|
| 1536 | + clk_disable_unprepare(hdmi->clk); |
---|
| 1537 | +put_rpm: |
---|
| 1538 | + pm_runtime_put_sync(dev); |
---|
| 1539 | + return err; |
---|
| 1540 | +} |
---|
| 1541 | + |
---|
1538 | 1542 | static const struct host1x_client_ops hdmi_client_ops = { |
---|
1539 | 1543 | .init = tegra_hdmi_init, |
---|
1540 | 1544 | .exit = tegra_hdmi_exit, |
---|
| 1545 | + .suspend = tegra_hdmi_runtime_suspend, |
---|
| 1546 | + .resume = tegra_hdmi_runtime_resume, |
---|
1541 | 1547 | }; |
---|
1542 | 1548 | |
---|
1543 | 1549 | static const struct tegra_hdmi_config tegra20_hdmi_config = { |
---|
.. | .. |
---|
1589 | 1595 | }; |
---|
1590 | 1596 | MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match); |
---|
1591 | 1597 | |
---|
1592 | | -static void hda_format_parse(unsigned int format, unsigned int *rate, |
---|
1593 | | - unsigned int *channels) |
---|
1594 | | -{ |
---|
1595 | | - unsigned int mul, div; |
---|
1596 | | - |
---|
1597 | | - if (format & AC_FMT_BASE_44K) |
---|
1598 | | - *rate = 44100; |
---|
1599 | | - else |
---|
1600 | | - *rate = 48000; |
---|
1601 | | - |
---|
1602 | | - mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT; |
---|
1603 | | - div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT; |
---|
1604 | | - |
---|
1605 | | - *rate = *rate * (mul + 1) / (div + 1); |
---|
1606 | | - |
---|
1607 | | - *channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT; |
---|
1608 | | -} |
---|
1609 | | - |
---|
1610 | 1598 | static irqreturn_t tegra_hdmi_irq(int irq, void *data) |
---|
1611 | 1599 | { |
---|
1612 | 1600 | struct tegra_hdmi *hdmi = data; |
---|
.. | .. |
---|
1623 | 1611 | value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); |
---|
1624 | 1612 | |
---|
1625 | 1613 | if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) { |
---|
1626 | | - unsigned int sample_rate, channels; |
---|
1627 | | - |
---|
1628 | 1614 | format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK; |
---|
1629 | 1615 | |
---|
1630 | | - hda_format_parse(format, &sample_rate, &channels); |
---|
1631 | | - |
---|
1632 | | - hdmi->audio_sample_rate = sample_rate; |
---|
1633 | | - hdmi->audio_channels = channels; |
---|
| 1616 | + tegra_hda_parse_format(format, &hdmi->format); |
---|
1634 | 1617 | |
---|
1635 | 1618 | err = tegra_hdmi_setup_audio(hdmi); |
---|
1636 | 1619 | if (err < 0) { |
---|
.. | .. |
---|
1652 | 1635 | |
---|
1653 | 1636 | static int tegra_hdmi_probe(struct platform_device *pdev) |
---|
1654 | 1637 | { |
---|
| 1638 | + const char *level = KERN_ERR; |
---|
1655 | 1639 | struct tegra_hdmi *hdmi; |
---|
1656 | 1640 | struct resource *regs; |
---|
1657 | 1641 | int err; |
---|
.. | .. |
---|
1664 | 1648 | hdmi->dev = &pdev->dev; |
---|
1665 | 1649 | |
---|
1666 | 1650 | hdmi->audio_source = AUTO; |
---|
1667 | | - hdmi->audio_sample_rate = 48000; |
---|
1668 | | - hdmi->audio_channels = 2; |
---|
1669 | 1651 | hdmi->stereo = false; |
---|
1670 | 1652 | hdmi->dvi = false; |
---|
1671 | 1653 | |
---|
.. | .. |
---|
1692 | 1674 | } |
---|
1693 | 1675 | |
---|
1694 | 1676 | hdmi->hdmi = devm_regulator_get(&pdev->dev, "hdmi"); |
---|
1695 | | - if (IS_ERR(hdmi->hdmi)) { |
---|
1696 | | - dev_err(&pdev->dev, "failed to get HDMI regulator\n"); |
---|
1697 | | - return PTR_ERR(hdmi->hdmi); |
---|
| 1677 | + err = PTR_ERR_OR_ZERO(hdmi->hdmi); |
---|
| 1678 | + if (err) { |
---|
| 1679 | + if (err == -EPROBE_DEFER) |
---|
| 1680 | + level = KERN_DEBUG; |
---|
| 1681 | + |
---|
| 1682 | + dev_printk(level, &pdev->dev, |
---|
| 1683 | + "failed to get HDMI regulator: %d\n", err); |
---|
| 1684 | + return err; |
---|
1698 | 1685 | } |
---|
1699 | 1686 | |
---|
1700 | 1687 | hdmi->pll = devm_regulator_get(&pdev->dev, "pll"); |
---|
1701 | | - if (IS_ERR(hdmi->pll)) { |
---|
1702 | | - dev_err(&pdev->dev, "failed to get PLL regulator\n"); |
---|
1703 | | - return PTR_ERR(hdmi->pll); |
---|
| 1688 | + err = PTR_ERR_OR_ZERO(hdmi->pll); |
---|
| 1689 | + if (err) { |
---|
| 1690 | + if (err == -EPROBE_DEFER) |
---|
| 1691 | + level = KERN_DEBUG; |
---|
| 1692 | + |
---|
| 1693 | + dev_printk(level, &pdev->dev, |
---|
| 1694 | + "failed to get PLL regulator: %d\n", err); |
---|
| 1695 | + return err; |
---|
1704 | 1696 | } |
---|
1705 | 1697 | |
---|
1706 | 1698 | hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd"); |
---|
1707 | | - if (IS_ERR(hdmi->vdd)) { |
---|
1708 | | - dev_err(&pdev->dev, "failed to get VDD regulator\n"); |
---|
1709 | | - return PTR_ERR(hdmi->vdd); |
---|
1710 | | - } |
---|
| 1699 | + err = PTR_ERR_OR_ZERO(hdmi->vdd); |
---|
| 1700 | + if (err) { |
---|
| 1701 | + if (err == -EPROBE_DEFER) |
---|
| 1702 | + level = KERN_DEBUG; |
---|
1711 | 1703 | |
---|
1712 | | - hdmi->output.notifier = cec_notifier_get(&pdev->dev); |
---|
1713 | | - if (hdmi->output.notifier == NULL) |
---|
1714 | | - return -ENOMEM; |
---|
| 1704 | + dev_printk(level, &pdev->dev, |
---|
| 1705 | + "failed to get VDD regulator: %d\n", err); |
---|
| 1706 | + return err; |
---|
| 1707 | + } |
---|
1715 | 1708 | |
---|
1716 | 1709 | hdmi->output.dev = &pdev->dev; |
---|
1717 | 1710 | |
---|
.. | .. |
---|
1771 | 1764 | |
---|
1772 | 1765 | tegra_output_remove(&hdmi->output); |
---|
1773 | 1766 | |
---|
1774 | | - if (hdmi->output.notifier) |
---|
1775 | | - cec_notifier_put(hdmi->output.notifier); |
---|
1776 | | - |
---|
1777 | 1767 | return 0; |
---|
1778 | 1768 | } |
---|
1779 | | - |
---|
1780 | | -#ifdef CONFIG_PM |
---|
1781 | | -static int tegra_hdmi_suspend(struct device *dev) |
---|
1782 | | -{ |
---|
1783 | | - struct tegra_hdmi *hdmi = dev_get_drvdata(dev); |
---|
1784 | | - int err; |
---|
1785 | | - |
---|
1786 | | - err = reset_control_assert(hdmi->rst); |
---|
1787 | | - if (err < 0) { |
---|
1788 | | - dev_err(dev, "failed to assert reset: %d\n", err); |
---|
1789 | | - return err; |
---|
1790 | | - } |
---|
1791 | | - |
---|
1792 | | - usleep_range(1000, 2000); |
---|
1793 | | - |
---|
1794 | | - clk_disable_unprepare(hdmi->clk); |
---|
1795 | | - |
---|
1796 | | - return 0; |
---|
1797 | | -} |
---|
1798 | | - |
---|
1799 | | -static int tegra_hdmi_resume(struct device *dev) |
---|
1800 | | -{ |
---|
1801 | | - struct tegra_hdmi *hdmi = dev_get_drvdata(dev); |
---|
1802 | | - int err; |
---|
1803 | | - |
---|
1804 | | - err = clk_prepare_enable(hdmi->clk); |
---|
1805 | | - if (err < 0) { |
---|
1806 | | - dev_err(dev, "failed to enable clock: %d\n", err); |
---|
1807 | | - return err; |
---|
1808 | | - } |
---|
1809 | | - |
---|
1810 | | - usleep_range(1000, 2000); |
---|
1811 | | - |
---|
1812 | | - err = reset_control_deassert(hdmi->rst); |
---|
1813 | | - if (err < 0) { |
---|
1814 | | - dev_err(dev, "failed to deassert reset: %d\n", err); |
---|
1815 | | - clk_disable_unprepare(hdmi->clk); |
---|
1816 | | - return err; |
---|
1817 | | - } |
---|
1818 | | - |
---|
1819 | | - return 0; |
---|
1820 | | -} |
---|
1821 | | -#endif |
---|
1822 | | - |
---|
1823 | | -static const struct dev_pm_ops tegra_hdmi_pm_ops = { |
---|
1824 | | - SET_RUNTIME_PM_OPS(tegra_hdmi_suspend, tegra_hdmi_resume, NULL) |
---|
1825 | | -}; |
---|
1826 | 1769 | |
---|
1827 | 1770 | struct platform_driver tegra_hdmi_driver = { |
---|
1828 | 1771 | .driver = { |
---|
1829 | 1772 | .name = "tegra-hdmi", |
---|
1830 | 1773 | .of_match_table = tegra_hdmi_of_match, |
---|
1831 | | - .pm = &tegra_hdmi_pm_ops, |
---|
1832 | 1774 | }, |
---|
1833 | 1775 | .probe = tegra_hdmi_probe, |
---|
1834 | 1776 | .remove = tegra_hdmi_remove, |
---|