162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/clk-provider.h>
862306a36Sopenharmony_ci#include <linux/debugfs.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1562306a36Sopenharmony_ci#include <linux/reset.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <soc/tegra/pmc.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <drm/display/drm_dp_helper.h>
2062306a36Sopenharmony_ci#include <drm/display/drm_scdc_helper.h>
2162306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
2262306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
2362306a36Sopenharmony_ci#include <drm/drm_file.h>
2462306a36Sopenharmony_ci#include <drm/drm_panel.h>
2562306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "dc.h"
2862306a36Sopenharmony_ci#include "dp.h"
2962306a36Sopenharmony_ci#include "drm.h"
3062306a36Sopenharmony_ci#include "hda.h"
3162306a36Sopenharmony_ci#include "sor.h"
3262306a36Sopenharmony_ci#include "trace.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define SOR_REKEY 0x38
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct tegra_sor_hdmi_settings {
3762306a36Sopenharmony_ci	unsigned long frequency;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	u8 vcocap;
4062306a36Sopenharmony_ci	u8 filter;
4162306a36Sopenharmony_ci	u8 ichpmp;
4262306a36Sopenharmony_ci	u8 loadadj;
4362306a36Sopenharmony_ci	u8 tmds_termadj;
4462306a36Sopenharmony_ci	u8 tx_pu_value;
4562306a36Sopenharmony_ci	u8 bg_temp_coef;
4662306a36Sopenharmony_ci	u8 bg_vref_level;
4762306a36Sopenharmony_ci	u8 avdd10_level;
4862306a36Sopenharmony_ci	u8 avdd14_level;
4962306a36Sopenharmony_ci	u8 sparepll;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	u8 drive_current[4];
5262306a36Sopenharmony_ci	u8 preemphasis[4];
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#if 1
5662306a36Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = {
5762306a36Sopenharmony_ci	{
5862306a36Sopenharmony_ci		.frequency = 54000000,
5962306a36Sopenharmony_ci		.vcocap = 0x0,
6062306a36Sopenharmony_ci		.filter = 0x0,
6162306a36Sopenharmony_ci		.ichpmp = 0x1,
6262306a36Sopenharmony_ci		.loadadj = 0x3,
6362306a36Sopenharmony_ci		.tmds_termadj = 0x9,
6462306a36Sopenharmony_ci		.tx_pu_value = 0x10,
6562306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
6662306a36Sopenharmony_ci		.bg_vref_level = 0x8,
6762306a36Sopenharmony_ci		.avdd10_level = 0x4,
6862306a36Sopenharmony_ci		.avdd14_level = 0x4,
6962306a36Sopenharmony_ci		.sparepll = 0x0,
7062306a36Sopenharmony_ci		.drive_current = { 0x33, 0x3a, 0x3a, 0x3a },
7162306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
7262306a36Sopenharmony_ci	}, {
7362306a36Sopenharmony_ci		.frequency = 75000000,
7462306a36Sopenharmony_ci		.vcocap = 0x3,
7562306a36Sopenharmony_ci		.filter = 0x0,
7662306a36Sopenharmony_ci		.ichpmp = 0x1,
7762306a36Sopenharmony_ci		.loadadj = 0x3,
7862306a36Sopenharmony_ci		.tmds_termadj = 0x9,
7962306a36Sopenharmony_ci		.tx_pu_value = 0x40,
8062306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
8162306a36Sopenharmony_ci		.bg_vref_level = 0x8,
8262306a36Sopenharmony_ci		.avdd10_level = 0x4,
8362306a36Sopenharmony_ci		.avdd14_level = 0x4,
8462306a36Sopenharmony_ci		.sparepll = 0x0,
8562306a36Sopenharmony_ci		.drive_current = { 0x33, 0x3a, 0x3a, 0x3a },
8662306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
8762306a36Sopenharmony_ci	}, {
8862306a36Sopenharmony_ci		.frequency = 150000000,
8962306a36Sopenharmony_ci		.vcocap = 0x3,
9062306a36Sopenharmony_ci		.filter = 0x0,
9162306a36Sopenharmony_ci		.ichpmp = 0x1,
9262306a36Sopenharmony_ci		.loadadj = 0x3,
9362306a36Sopenharmony_ci		.tmds_termadj = 0x9,
9462306a36Sopenharmony_ci		.tx_pu_value = 0x66,
9562306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
9662306a36Sopenharmony_ci		.bg_vref_level = 0x8,
9762306a36Sopenharmony_ci		.avdd10_level = 0x4,
9862306a36Sopenharmony_ci		.avdd14_level = 0x4,
9962306a36Sopenharmony_ci		.sparepll = 0x0,
10062306a36Sopenharmony_ci		.drive_current = { 0x33, 0x3a, 0x3a, 0x3a },
10162306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
10262306a36Sopenharmony_ci	}, {
10362306a36Sopenharmony_ci		.frequency = 300000000,
10462306a36Sopenharmony_ci		.vcocap = 0x3,
10562306a36Sopenharmony_ci		.filter = 0x0,
10662306a36Sopenharmony_ci		.ichpmp = 0x1,
10762306a36Sopenharmony_ci		.loadadj = 0x3,
10862306a36Sopenharmony_ci		.tmds_termadj = 0x9,
10962306a36Sopenharmony_ci		.tx_pu_value = 0x66,
11062306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
11162306a36Sopenharmony_ci		.bg_vref_level = 0xa,
11262306a36Sopenharmony_ci		.avdd10_level = 0x4,
11362306a36Sopenharmony_ci		.avdd14_level = 0x4,
11462306a36Sopenharmony_ci		.sparepll = 0x0,
11562306a36Sopenharmony_ci		.drive_current = { 0x33, 0x3f, 0x3f, 0x3f },
11662306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x17, 0x17, 0x17 },
11762306a36Sopenharmony_ci	}, {
11862306a36Sopenharmony_ci		.frequency = 600000000,
11962306a36Sopenharmony_ci		.vcocap = 0x3,
12062306a36Sopenharmony_ci		.filter = 0x0,
12162306a36Sopenharmony_ci		.ichpmp = 0x1,
12262306a36Sopenharmony_ci		.loadadj = 0x3,
12362306a36Sopenharmony_ci		.tmds_termadj = 0x9,
12462306a36Sopenharmony_ci		.tx_pu_value = 0x66,
12562306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
12662306a36Sopenharmony_ci		.bg_vref_level = 0x8,
12762306a36Sopenharmony_ci		.avdd10_level = 0x4,
12862306a36Sopenharmony_ci		.avdd14_level = 0x4,
12962306a36Sopenharmony_ci		.sparepll = 0x0,
13062306a36Sopenharmony_ci		.drive_current = { 0x33, 0x3f, 0x3f, 0x3f },
13162306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
13262306a36Sopenharmony_ci	},
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci#else
13562306a36Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = {
13662306a36Sopenharmony_ci	{
13762306a36Sopenharmony_ci		.frequency = 75000000,
13862306a36Sopenharmony_ci		.vcocap = 0x3,
13962306a36Sopenharmony_ci		.filter = 0x0,
14062306a36Sopenharmony_ci		.ichpmp = 0x1,
14162306a36Sopenharmony_ci		.loadadj = 0x3,
14262306a36Sopenharmony_ci		.tmds_termadj = 0x9,
14362306a36Sopenharmony_ci		.tx_pu_value = 0x40,
14462306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
14562306a36Sopenharmony_ci		.bg_vref_level = 0x8,
14662306a36Sopenharmony_ci		.avdd10_level = 0x4,
14762306a36Sopenharmony_ci		.avdd14_level = 0x4,
14862306a36Sopenharmony_ci		.sparepll = 0x0,
14962306a36Sopenharmony_ci		.drive_current = { 0x29, 0x29, 0x29, 0x29 },
15062306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
15162306a36Sopenharmony_ci	}, {
15262306a36Sopenharmony_ci		.frequency = 150000000,
15362306a36Sopenharmony_ci		.vcocap = 0x3,
15462306a36Sopenharmony_ci		.filter = 0x0,
15562306a36Sopenharmony_ci		.ichpmp = 0x1,
15662306a36Sopenharmony_ci		.loadadj = 0x3,
15762306a36Sopenharmony_ci		.tmds_termadj = 0x9,
15862306a36Sopenharmony_ci		.tx_pu_value = 0x66,
15962306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
16062306a36Sopenharmony_ci		.bg_vref_level = 0x8,
16162306a36Sopenharmony_ci		.avdd10_level = 0x4,
16262306a36Sopenharmony_ci		.avdd14_level = 0x4,
16362306a36Sopenharmony_ci		.sparepll = 0x0,
16462306a36Sopenharmony_ci		.drive_current = { 0x30, 0x37, 0x37, 0x37 },
16562306a36Sopenharmony_ci		.preemphasis = { 0x01, 0x02, 0x02, 0x02 },
16662306a36Sopenharmony_ci	}, {
16762306a36Sopenharmony_ci		.frequency = 300000000,
16862306a36Sopenharmony_ci		.vcocap = 0x3,
16962306a36Sopenharmony_ci		.filter = 0x0,
17062306a36Sopenharmony_ci		.ichpmp = 0x6,
17162306a36Sopenharmony_ci		.loadadj = 0x3,
17262306a36Sopenharmony_ci		.tmds_termadj = 0x9,
17362306a36Sopenharmony_ci		.tx_pu_value = 0x66,
17462306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
17562306a36Sopenharmony_ci		.bg_vref_level = 0xf,
17662306a36Sopenharmony_ci		.avdd10_level = 0x4,
17762306a36Sopenharmony_ci		.avdd14_level = 0x4,
17862306a36Sopenharmony_ci		.sparepll = 0x0,
17962306a36Sopenharmony_ci		.drive_current = { 0x30, 0x37, 0x37, 0x37 },
18062306a36Sopenharmony_ci		.preemphasis = { 0x10, 0x3e, 0x3e, 0x3e },
18162306a36Sopenharmony_ci	}, {
18262306a36Sopenharmony_ci		.frequency = 600000000,
18362306a36Sopenharmony_ci		.vcocap = 0x3,
18462306a36Sopenharmony_ci		.filter = 0x0,
18562306a36Sopenharmony_ci		.ichpmp = 0xa,
18662306a36Sopenharmony_ci		.loadadj = 0x3,
18762306a36Sopenharmony_ci		.tmds_termadj = 0xb,
18862306a36Sopenharmony_ci		.tx_pu_value = 0x66,
18962306a36Sopenharmony_ci		.bg_temp_coef = 0x3,
19062306a36Sopenharmony_ci		.bg_vref_level = 0xe,
19162306a36Sopenharmony_ci		.avdd10_level = 0x4,
19262306a36Sopenharmony_ci		.avdd14_level = 0x4,
19362306a36Sopenharmony_ci		.sparepll = 0x0,
19462306a36Sopenharmony_ci		.drive_current = { 0x35, 0x3e, 0x3e, 0x3e },
19562306a36Sopenharmony_ci		.preemphasis = { 0x02, 0x3f, 0x3f, 0x3f },
19662306a36Sopenharmony_ci	},
19762306a36Sopenharmony_ci};
19862306a36Sopenharmony_ci#endif
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra186_sor_hdmi_defaults[] = {
20162306a36Sopenharmony_ci	{
20262306a36Sopenharmony_ci		.frequency = 54000000,
20362306a36Sopenharmony_ci		.vcocap = 0,
20462306a36Sopenharmony_ci		.filter = 5,
20562306a36Sopenharmony_ci		.ichpmp = 5,
20662306a36Sopenharmony_ci		.loadadj = 3,
20762306a36Sopenharmony_ci		.tmds_termadj = 0xf,
20862306a36Sopenharmony_ci		.tx_pu_value = 0,
20962306a36Sopenharmony_ci		.bg_temp_coef = 3,
21062306a36Sopenharmony_ci		.bg_vref_level = 8,
21162306a36Sopenharmony_ci		.avdd10_level = 4,
21262306a36Sopenharmony_ci		.avdd14_level = 4,
21362306a36Sopenharmony_ci		.sparepll = 0x54,
21462306a36Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
21562306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
21662306a36Sopenharmony_ci	}, {
21762306a36Sopenharmony_ci		.frequency = 75000000,
21862306a36Sopenharmony_ci		.vcocap = 1,
21962306a36Sopenharmony_ci		.filter = 5,
22062306a36Sopenharmony_ci		.ichpmp = 5,
22162306a36Sopenharmony_ci		.loadadj = 3,
22262306a36Sopenharmony_ci		.tmds_termadj = 0xf,
22362306a36Sopenharmony_ci		.tx_pu_value = 0,
22462306a36Sopenharmony_ci		.bg_temp_coef = 3,
22562306a36Sopenharmony_ci		.bg_vref_level = 8,
22662306a36Sopenharmony_ci		.avdd10_level = 4,
22762306a36Sopenharmony_ci		.avdd14_level = 4,
22862306a36Sopenharmony_ci		.sparepll = 0x44,
22962306a36Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
23062306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
23162306a36Sopenharmony_ci	}, {
23262306a36Sopenharmony_ci		.frequency = 150000000,
23362306a36Sopenharmony_ci		.vcocap = 3,
23462306a36Sopenharmony_ci		.filter = 5,
23562306a36Sopenharmony_ci		.ichpmp = 5,
23662306a36Sopenharmony_ci		.loadadj = 3,
23762306a36Sopenharmony_ci		.tmds_termadj = 15,
23862306a36Sopenharmony_ci		.tx_pu_value = 0x66 /* 0 */,
23962306a36Sopenharmony_ci		.bg_temp_coef = 3,
24062306a36Sopenharmony_ci		.bg_vref_level = 8,
24162306a36Sopenharmony_ci		.avdd10_level = 4,
24262306a36Sopenharmony_ci		.avdd14_level = 4,
24362306a36Sopenharmony_ci		.sparepll = 0x00, /* 0x34 */
24462306a36Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x37 },
24562306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
24662306a36Sopenharmony_ci	}, {
24762306a36Sopenharmony_ci		.frequency = 300000000,
24862306a36Sopenharmony_ci		.vcocap = 3,
24962306a36Sopenharmony_ci		.filter = 5,
25062306a36Sopenharmony_ci		.ichpmp = 5,
25162306a36Sopenharmony_ci		.loadadj = 3,
25262306a36Sopenharmony_ci		.tmds_termadj = 15,
25362306a36Sopenharmony_ci		.tx_pu_value = 64,
25462306a36Sopenharmony_ci		.bg_temp_coef = 3,
25562306a36Sopenharmony_ci		.bg_vref_level = 8,
25662306a36Sopenharmony_ci		.avdd10_level = 4,
25762306a36Sopenharmony_ci		.avdd14_level = 4,
25862306a36Sopenharmony_ci		.sparepll = 0x34,
25962306a36Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
26062306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
26162306a36Sopenharmony_ci	}, {
26262306a36Sopenharmony_ci		.frequency = 600000000,
26362306a36Sopenharmony_ci		.vcocap = 3,
26462306a36Sopenharmony_ci		.filter = 5,
26562306a36Sopenharmony_ci		.ichpmp = 5,
26662306a36Sopenharmony_ci		.loadadj = 3,
26762306a36Sopenharmony_ci		.tmds_termadj = 12,
26862306a36Sopenharmony_ci		.tx_pu_value = 96,
26962306a36Sopenharmony_ci		.bg_temp_coef = 3,
27062306a36Sopenharmony_ci		.bg_vref_level = 8,
27162306a36Sopenharmony_ci		.avdd10_level = 4,
27262306a36Sopenharmony_ci		.avdd14_level = 4,
27362306a36Sopenharmony_ci		.sparepll = 0x34,
27462306a36Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
27562306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci};
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra194_sor_hdmi_defaults[] = {
28062306a36Sopenharmony_ci	{
28162306a36Sopenharmony_ci		.frequency = 54000000,
28262306a36Sopenharmony_ci		.vcocap = 0,
28362306a36Sopenharmony_ci		.filter = 5,
28462306a36Sopenharmony_ci		.ichpmp = 5,
28562306a36Sopenharmony_ci		.loadadj = 3,
28662306a36Sopenharmony_ci		.tmds_termadj = 0xf,
28762306a36Sopenharmony_ci		.tx_pu_value = 0,
28862306a36Sopenharmony_ci		.bg_temp_coef = 3,
28962306a36Sopenharmony_ci		.bg_vref_level = 8,
29062306a36Sopenharmony_ci		.avdd10_level = 4,
29162306a36Sopenharmony_ci		.avdd14_level = 4,
29262306a36Sopenharmony_ci		.sparepll = 0x54,
29362306a36Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
29462306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
29562306a36Sopenharmony_ci	}, {
29662306a36Sopenharmony_ci		.frequency = 75000000,
29762306a36Sopenharmony_ci		.vcocap = 1,
29862306a36Sopenharmony_ci		.filter = 5,
29962306a36Sopenharmony_ci		.ichpmp = 5,
30062306a36Sopenharmony_ci		.loadadj = 3,
30162306a36Sopenharmony_ci		.tmds_termadj = 0xf,
30262306a36Sopenharmony_ci		.tx_pu_value = 0,
30362306a36Sopenharmony_ci		.bg_temp_coef = 3,
30462306a36Sopenharmony_ci		.bg_vref_level = 8,
30562306a36Sopenharmony_ci		.avdd10_level = 4,
30662306a36Sopenharmony_ci		.avdd14_level = 4,
30762306a36Sopenharmony_ci		.sparepll = 0x44,
30862306a36Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
30962306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
31062306a36Sopenharmony_ci	}, {
31162306a36Sopenharmony_ci		.frequency = 150000000,
31262306a36Sopenharmony_ci		.vcocap = 3,
31362306a36Sopenharmony_ci		.filter = 5,
31462306a36Sopenharmony_ci		.ichpmp = 5,
31562306a36Sopenharmony_ci		.loadadj = 3,
31662306a36Sopenharmony_ci		.tmds_termadj = 15,
31762306a36Sopenharmony_ci		.tx_pu_value = 0x66 /* 0 */,
31862306a36Sopenharmony_ci		.bg_temp_coef = 3,
31962306a36Sopenharmony_ci		.bg_vref_level = 8,
32062306a36Sopenharmony_ci		.avdd10_level = 4,
32162306a36Sopenharmony_ci		.avdd14_level = 4,
32262306a36Sopenharmony_ci		.sparepll = 0x00, /* 0x34 */
32362306a36Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x37 },
32462306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
32562306a36Sopenharmony_ci	}, {
32662306a36Sopenharmony_ci		.frequency = 300000000,
32762306a36Sopenharmony_ci		.vcocap = 3,
32862306a36Sopenharmony_ci		.filter = 5,
32962306a36Sopenharmony_ci		.ichpmp = 5,
33062306a36Sopenharmony_ci		.loadadj = 3,
33162306a36Sopenharmony_ci		.tmds_termadj = 15,
33262306a36Sopenharmony_ci		.tx_pu_value = 64,
33362306a36Sopenharmony_ci		.bg_temp_coef = 3,
33462306a36Sopenharmony_ci		.bg_vref_level = 8,
33562306a36Sopenharmony_ci		.avdd10_level = 4,
33662306a36Sopenharmony_ci		.avdd14_level = 4,
33762306a36Sopenharmony_ci		.sparepll = 0x34,
33862306a36Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
33962306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
34062306a36Sopenharmony_ci	}, {
34162306a36Sopenharmony_ci		.frequency = 600000000,
34262306a36Sopenharmony_ci		.vcocap = 3,
34362306a36Sopenharmony_ci		.filter = 5,
34462306a36Sopenharmony_ci		.ichpmp = 5,
34562306a36Sopenharmony_ci		.loadadj = 3,
34662306a36Sopenharmony_ci		.tmds_termadj = 12,
34762306a36Sopenharmony_ci		.tx_pu_value = 96,
34862306a36Sopenharmony_ci		.bg_temp_coef = 3,
34962306a36Sopenharmony_ci		.bg_vref_level = 8,
35062306a36Sopenharmony_ci		.avdd10_level = 4,
35162306a36Sopenharmony_ci		.avdd14_level = 4,
35262306a36Sopenharmony_ci		.sparepll = 0x34,
35362306a36Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
35462306a36Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistruct tegra_sor_regs {
35962306a36Sopenharmony_ci	unsigned int head_state0;
36062306a36Sopenharmony_ci	unsigned int head_state1;
36162306a36Sopenharmony_ci	unsigned int head_state2;
36262306a36Sopenharmony_ci	unsigned int head_state3;
36362306a36Sopenharmony_ci	unsigned int head_state4;
36462306a36Sopenharmony_ci	unsigned int head_state5;
36562306a36Sopenharmony_ci	unsigned int pll0;
36662306a36Sopenharmony_ci	unsigned int pll1;
36762306a36Sopenharmony_ci	unsigned int pll2;
36862306a36Sopenharmony_ci	unsigned int pll3;
36962306a36Sopenharmony_ci	unsigned int dp_padctl0;
37062306a36Sopenharmony_ci	unsigned int dp_padctl2;
37162306a36Sopenharmony_ci};
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistruct tegra_sor_soc {
37462306a36Sopenharmony_ci	bool supports_lvds;
37562306a36Sopenharmony_ci	bool supports_hdmi;
37662306a36Sopenharmony_ci	bool supports_dp;
37762306a36Sopenharmony_ci	bool supports_audio;
37862306a36Sopenharmony_ci	bool supports_hdcp;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	const struct tegra_sor_regs *regs;
38162306a36Sopenharmony_ci	bool has_nvdisplay;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	const struct tegra_sor_hdmi_settings *settings;
38462306a36Sopenharmony_ci	unsigned int num_settings;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	const u8 *xbar_cfg;
38762306a36Sopenharmony_ci	const u8 *lane_map;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	const u8 (*voltage_swing)[4][4];
39062306a36Sopenharmony_ci	const u8 (*pre_emphasis)[4][4];
39162306a36Sopenharmony_ci	const u8 (*post_cursor)[4][4];
39262306a36Sopenharmony_ci	const u8 (*tx_pu)[4][4];
39362306a36Sopenharmony_ci};
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistruct tegra_sor;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistruct tegra_sor_ops {
39862306a36Sopenharmony_ci	const char *name;
39962306a36Sopenharmony_ci	int (*probe)(struct tegra_sor *sor);
40062306a36Sopenharmony_ci	void (*audio_enable)(struct tegra_sor *sor);
40162306a36Sopenharmony_ci	void (*audio_disable)(struct tegra_sor *sor);
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistruct tegra_sor {
40562306a36Sopenharmony_ci	struct host1x_client client;
40662306a36Sopenharmony_ci	struct tegra_output output;
40762306a36Sopenharmony_ci	struct device *dev;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	const struct tegra_sor_soc *soc;
41062306a36Sopenharmony_ci	void __iomem *regs;
41162306a36Sopenharmony_ci	unsigned int index;
41262306a36Sopenharmony_ci	unsigned int irq;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	struct reset_control *rst;
41562306a36Sopenharmony_ci	struct clk *clk_parent;
41662306a36Sopenharmony_ci	struct clk *clk_safe;
41762306a36Sopenharmony_ci	struct clk *clk_out;
41862306a36Sopenharmony_ci	struct clk *clk_pad;
41962306a36Sopenharmony_ci	struct clk *clk_dp;
42062306a36Sopenharmony_ci	struct clk *clk;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	u8 xbar_cfg[5];
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	struct drm_dp_link link;
42562306a36Sopenharmony_ci	struct drm_dp_aux *aux;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	struct drm_info_list *debugfs_files;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	const struct tegra_sor_ops *ops;
43062306a36Sopenharmony_ci	enum tegra_io_pad pad;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* for HDMI 2.0 */
43362306a36Sopenharmony_ci	struct tegra_sor_hdmi_settings *settings;
43462306a36Sopenharmony_ci	unsigned int num_settings;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	struct regulator *avdd_io_supply;
43762306a36Sopenharmony_ci	struct regulator *vdd_pll_supply;
43862306a36Sopenharmony_ci	struct regulator *hdmi_supply;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	struct delayed_work scdc;
44162306a36Sopenharmony_ci	bool scdc_enabled;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	struct tegra_hda_format format;
44462306a36Sopenharmony_ci};
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistruct tegra_sor_state {
44762306a36Sopenharmony_ci	struct drm_connector_state base;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	unsigned int link_speed;
45062306a36Sopenharmony_ci	unsigned long pclk;
45162306a36Sopenharmony_ci	unsigned int bpc;
45262306a36Sopenharmony_ci};
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic inline struct tegra_sor_state *
45562306a36Sopenharmony_cito_sor_state(struct drm_connector_state *state)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	return container_of(state, struct tegra_sor_state, base);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistruct tegra_sor_config {
46162306a36Sopenharmony_ci	u32 bits_per_pixel;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	u32 active_polarity;
46462306a36Sopenharmony_ci	u32 active_count;
46562306a36Sopenharmony_ci	u32 tu_size;
46662306a36Sopenharmony_ci	u32 active_frac;
46762306a36Sopenharmony_ci	u32 watermark;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	u32 hblank_symbols;
47062306a36Sopenharmony_ci	u32 vblank_symbols;
47162306a36Sopenharmony_ci};
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic inline struct tegra_sor *
47462306a36Sopenharmony_cihost1x_client_to_sor(struct host1x_client *client)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	return container_of(client, struct tegra_sor, client);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic inline struct tegra_sor *to_sor(struct tegra_output *output)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	return container_of(output, struct tegra_sor, output);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic inline u32 tegra_sor_readl(struct tegra_sor *sor, unsigned int offset)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	u32 value = readl(sor->regs + (offset << 2));
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	trace_sor_readl(sor->dev, offset, value);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return value;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic inline void tegra_sor_writel(struct tegra_sor *sor, u32 value,
49462306a36Sopenharmony_ci				    unsigned int offset)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	trace_sor_writel(sor->dev, offset, value);
49762306a36Sopenharmony_ci	writel(value, sor->regs + (offset << 2));
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int tegra_sor_set_parent_clock(struct tegra_sor *sor, struct clk *parent)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	int err;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	clk_disable_unprepare(sor->clk);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	err = clk_set_parent(sor->clk_out, parent);
50762306a36Sopenharmony_ci	if (err < 0)
50862306a36Sopenharmony_ci		return err;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	err = clk_prepare_enable(sor->clk);
51162306a36Sopenharmony_ci	if (err < 0)
51262306a36Sopenharmony_ci		return err;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return 0;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistruct tegra_clk_sor_pad {
51862306a36Sopenharmony_ci	struct clk_hw hw;
51962306a36Sopenharmony_ci	struct tegra_sor *sor;
52062306a36Sopenharmony_ci};
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic inline struct tegra_clk_sor_pad *to_pad(struct clk_hw *hw)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	return container_of(hw, struct tegra_clk_sor_pad, hw);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic const char * const tegra_clk_sor_pad_parents[2][2] = {
52862306a36Sopenharmony_ci	{ "pll_d_out0", "pll_dp" },
52962306a36Sopenharmony_ci	{ "pll_d2_out0", "pll_dp" },
53062306a36Sopenharmony_ci};
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci/*
53362306a36Sopenharmony_ci * Implementing ->set_parent() here isn't really required because the parent
53462306a36Sopenharmony_ci * will be explicitly selected in the driver code via the DP_CLK_SEL mux in
53562306a36Sopenharmony_ci * the SOR_CLK_CNTRL register. This is primarily for compatibility with the
53662306a36Sopenharmony_ci * Tegra186 and later SoC generations where the BPMP implements this clock
53762306a36Sopenharmony_ci * and doesn't expose the mux via the common clock framework.
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic int tegra_clk_sor_pad_set_parent(struct clk_hw *hw, u8 index)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct tegra_clk_sor_pad *pad = to_pad(hw);
54362306a36Sopenharmony_ci	struct tegra_sor *sor = pad->sor;
54462306a36Sopenharmony_ci	u32 value;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
54762306a36Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	switch (index) {
55062306a36Sopenharmony_ci	case 0:
55162306a36Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK;
55262306a36Sopenharmony_ci		break;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	case 1:
55562306a36Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
55662306a36Sopenharmony_ci		break;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return 0;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic u8 tegra_clk_sor_pad_get_parent(struct clk_hw *hw)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct tegra_clk_sor_pad *pad = to_pad(hw);
56762306a36Sopenharmony_ci	struct tegra_sor *sor = pad->sor;
56862306a36Sopenharmony_ci	u8 parent = U8_MAX;
56962306a36Sopenharmony_ci	u32 value;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	switch (value & SOR_CLK_CNTRL_DP_CLK_SEL_MASK) {
57462306a36Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK:
57562306a36Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK:
57662306a36Sopenharmony_ci		parent = 0;
57762306a36Sopenharmony_ci		break;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK:
58062306a36Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK:
58162306a36Sopenharmony_ci		parent = 1;
58262306a36Sopenharmony_ci		break;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return parent;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic const struct clk_ops tegra_clk_sor_pad_ops = {
58962306a36Sopenharmony_ci	.determine_rate = clk_hw_determine_rate_no_reparent,
59062306a36Sopenharmony_ci	.set_parent = tegra_clk_sor_pad_set_parent,
59162306a36Sopenharmony_ci	.get_parent = tegra_clk_sor_pad_get_parent,
59262306a36Sopenharmony_ci};
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic struct clk *tegra_clk_sor_pad_register(struct tegra_sor *sor,
59562306a36Sopenharmony_ci					      const char *name)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct tegra_clk_sor_pad *pad;
59862306a36Sopenharmony_ci	struct clk_init_data init;
59962306a36Sopenharmony_ci	struct clk *clk;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	pad = devm_kzalloc(sor->dev, sizeof(*pad), GFP_KERNEL);
60262306a36Sopenharmony_ci	if (!pad)
60362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	pad->sor = sor;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	init.name = name;
60862306a36Sopenharmony_ci	init.flags = 0;
60962306a36Sopenharmony_ci	init.parent_names = tegra_clk_sor_pad_parents[sor->index];
61062306a36Sopenharmony_ci	init.num_parents = ARRAY_SIZE(tegra_clk_sor_pad_parents[sor->index]);
61162306a36Sopenharmony_ci	init.ops = &tegra_clk_sor_pad_ops;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	pad->hw.init = &init;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	clk = devm_clk_register(sor->dev, &pad->hw);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return clk;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic void tegra_sor_filter_rates(struct tegra_sor *sor)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct drm_dp_link *link = &sor->link;
62362306a36Sopenharmony_ci	unsigned int i;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* Tegra only supports RBR, HBR and HBR2 */
62662306a36Sopenharmony_ci	for (i = 0; i < link->num_rates; i++) {
62762306a36Sopenharmony_ci		switch (link->rates[i]) {
62862306a36Sopenharmony_ci		case 1620000:
62962306a36Sopenharmony_ci		case 2700000:
63062306a36Sopenharmony_ci		case 5400000:
63162306a36Sopenharmony_ci			break;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		default:
63462306a36Sopenharmony_ci			DRM_DEBUG_KMS("link rate %lu kHz not supported\n",
63562306a36Sopenharmony_ci				      link->rates[i]);
63662306a36Sopenharmony_ci			link->rates[i] = 0;
63762306a36Sopenharmony_ci			break;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	drm_dp_link_update_rates(link);
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int tegra_sor_power_up_lanes(struct tegra_sor *sor, unsigned int lanes)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	unsigned long timeout;
64762306a36Sopenharmony_ci	u32 value;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/*
65062306a36Sopenharmony_ci	 * Clear or set the PD_TXD bit corresponding to each lane, depending
65162306a36Sopenharmony_ci	 * on whether it is used or not.
65262306a36Sopenharmony_ci	 */
65362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (lanes <= 2)
65662306a36Sopenharmony_ci		value &= ~(SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[3]) |
65762306a36Sopenharmony_ci			   SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[2]));
65862306a36Sopenharmony_ci	else
65962306a36Sopenharmony_ci		value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[3]) |
66062306a36Sopenharmony_ci			 SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[2]);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (lanes <= 1)
66362306a36Sopenharmony_ci		value &= ~SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[1]);
66462306a36Sopenharmony_ci	else
66562306a36Sopenharmony_ci		value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[1]);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (lanes == 0)
66862306a36Sopenharmony_ci		value &= ~SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[0]);
66962306a36Sopenharmony_ci	else
67062306a36Sopenharmony_ci		value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[0]);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/* start lane sequencer */
67562306a36Sopenharmony_ci	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
67662306a36Sopenharmony_ci		SOR_LANE_SEQ_CTL_POWER_STATE_UP;
67762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
68262306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
68362306a36Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
68462306a36Sopenharmony_ci			break;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		usleep_range(250, 1000);
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
69062306a36Sopenharmony_ci		return -ETIMEDOUT;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	return 0;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic int tegra_sor_power_down_lanes(struct tegra_sor *sor)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	unsigned long timeout;
69862306a36Sopenharmony_ci	u32 value;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/* power down all lanes */
70162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
70262306a36Sopenharmony_ci	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
70362306a36Sopenharmony_ci		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
70462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* start lane sequencer */
70762306a36Sopenharmony_ci	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
70862306a36Sopenharmony_ci		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
70962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
71462306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
71562306a36Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
71662306a36Sopenharmony_ci			break;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		usleep_range(25, 100);
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
72262306a36Sopenharmony_ci		return -ETIMEDOUT;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return 0;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic void tegra_sor_dp_precharge(struct tegra_sor *sor, unsigned int lanes)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	u32 value;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/* pre-charge all used lanes */
73262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (lanes <= 2)
73562306a36Sopenharmony_ci		value &= ~(SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[3]) |
73662306a36Sopenharmony_ci			   SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[2]));
73762306a36Sopenharmony_ci	else
73862306a36Sopenharmony_ci		value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[3]) |
73962306a36Sopenharmony_ci			 SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[2]);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (lanes <= 1)
74262306a36Sopenharmony_ci		value &= ~SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[1]);
74362306a36Sopenharmony_ci	else
74462306a36Sopenharmony_ci		value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[1]);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (lanes == 0)
74762306a36Sopenharmony_ci		value &= ~SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[0]);
74862306a36Sopenharmony_ci	else
74962306a36Sopenharmony_ci		value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[0]);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	usleep_range(15, 100);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
75662306a36Sopenharmony_ci	value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
75762306a36Sopenharmony_ci		   SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0);
75862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic void tegra_sor_dp_term_calibrate(struct tegra_sor *sor)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	u32 mask = 0x08, adj = 0, value;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	/* enable pad calibration logic */
76662306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
76762306a36Sopenharmony_ci	value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
76862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
77162306a36Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERM;
77262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	while (mask) {
77562306a36Sopenharmony_ci		adj |= mask;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		value = tegra_sor_readl(sor, sor->soc->regs->pll1);
77862306a36Sopenharmony_ci		value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
77962306a36Sopenharmony_ci		value |= SOR_PLL1_TMDS_TERMADJ(adj);
78062306a36Sopenharmony_ci		tegra_sor_writel(sor, value, sor->soc->regs->pll1);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		usleep_range(100, 200);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		value = tegra_sor_readl(sor, sor->soc->regs->pll1);
78562306a36Sopenharmony_ci		if (value & SOR_PLL1_TERM_COMPOUT)
78662306a36Sopenharmony_ci			adj &= ~mask;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		mask >>= 1;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
79262306a36Sopenharmony_ci	value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
79362306a36Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERMADJ(adj);
79462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* disable pad calibration logic */
79762306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
79862306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_PAD_CAL_PD;
79962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int tegra_sor_dp_link_apply_training(struct drm_dp_link *link)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct tegra_sor *sor = container_of(link, struct tegra_sor, link);
80562306a36Sopenharmony_ci	u32 voltage_swing = 0, pre_emphasis = 0, post_cursor = 0;
80662306a36Sopenharmony_ci	const struct tegra_sor_soc *soc = sor->soc;
80762306a36Sopenharmony_ci	u32 pattern = 0, tx_pu = 0, value;
80862306a36Sopenharmony_ci	unsigned int i;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	for (value = 0, i = 0; i < link->lanes; i++) {
81162306a36Sopenharmony_ci		u8 vs = link->train.request.voltage_swing[i];
81262306a36Sopenharmony_ci		u8 pe = link->train.request.pre_emphasis[i];
81362306a36Sopenharmony_ci		u8 pc = link->train.request.post_cursor[i];
81462306a36Sopenharmony_ci		u8 shift = sor->soc->lane_map[i] << 3;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		voltage_swing |= soc->voltage_swing[pc][vs][pe] << shift;
81762306a36Sopenharmony_ci		pre_emphasis |= soc->pre_emphasis[pc][vs][pe] << shift;
81862306a36Sopenharmony_ci		post_cursor |= soc->post_cursor[pc][vs][pe] << shift;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		if (sor->soc->tx_pu[pc][vs][pe] > tx_pu)
82162306a36Sopenharmony_ci			tx_pu = sor->soc->tx_pu[pc][vs][pe];
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci		switch (link->train.pattern) {
82462306a36Sopenharmony_ci		case DP_TRAINING_PATTERN_DISABLE:
82562306a36Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_GALIOS |
82662306a36Sopenharmony_ci				SOR_DP_TPG_PATTERN_NONE;
82762306a36Sopenharmony_ci			break;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		case DP_TRAINING_PATTERN_1:
83062306a36Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_NONE |
83162306a36Sopenharmony_ci				SOR_DP_TPG_PATTERN_TRAIN1;
83262306a36Sopenharmony_ci			break;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		case DP_TRAINING_PATTERN_2:
83562306a36Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_NONE |
83662306a36Sopenharmony_ci				SOR_DP_TPG_PATTERN_TRAIN2;
83762306a36Sopenharmony_ci			break;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		case DP_TRAINING_PATTERN_3:
84062306a36Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_NONE |
84162306a36Sopenharmony_ci				SOR_DP_TPG_PATTERN_TRAIN3;
84262306a36Sopenharmony_ci			break;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		default:
84562306a36Sopenharmony_ci			return -EINVAL;
84662306a36Sopenharmony_ci		}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci		if (link->caps.channel_coding)
84962306a36Sopenharmony_ci			value |= SOR_DP_TPG_CHANNEL_CODING;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		pattern = pattern << 8 | value;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	tegra_sor_writel(sor, voltage_swing, SOR_LANE_DRIVE_CURRENT0);
85562306a36Sopenharmony_ci	tegra_sor_writel(sor, pre_emphasis, SOR_LANE_PREEMPHASIS0);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (link->caps.tps3_supported)
85862306a36Sopenharmony_ci		tegra_sor_writel(sor, post_cursor, SOR_LANE_POSTCURSOR0);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	tegra_sor_writel(sor, pattern, SOR_DP_TPG);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
86362306a36Sopenharmony_ci	value &= ~SOR_DP_PADCTL_TX_PU_MASK;
86462306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU_ENABLE;
86562306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU(tx_pu);
86662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	usleep_range(20, 100);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	return 0;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic int tegra_sor_dp_link_configure(struct drm_dp_link *link)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	struct tegra_sor *sor = container_of(link, struct tegra_sor, link);
87662306a36Sopenharmony_ci	unsigned int rate, lanes;
87762306a36Sopenharmony_ci	u32 value;
87862306a36Sopenharmony_ci	int err;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	rate = drm_dp_link_rate_to_bw_code(link->rate);
88162306a36Sopenharmony_ci	lanes = link->lanes;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* configure link speed and lane count */
88462306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
88562306a36Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
88662306a36Sopenharmony_ci	value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate);
88762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
89062306a36Sopenharmony_ci	value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
89162306a36Sopenharmony_ci	value |= SOR_DP_LINKCTL_LANE_COUNT(lanes);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	if (link->caps.enhanced_framing)
89462306a36Sopenharmony_ci		value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	usleep_range(400, 1000);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* configure load pulse position adjustment */
90162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
90262306a36Sopenharmony_ci	value &= ~SOR_PLL1_LOADADJ_MASK;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	switch (rate) {
90562306a36Sopenharmony_ci	case DP_LINK_BW_1_62:
90662306a36Sopenharmony_ci		value |= SOR_PLL1_LOADADJ(0x3);
90762306a36Sopenharmony_ci		break;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	case DP_LINK_BW_2_7:
91062306a36Sopenharmony_ci		value |= SOR_PLL1_LOADADJ(0x4);
91162306a36Sopenharmony_ci		break;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	case DP_LINK_BW_5_4:
91462306a36Sopenharmony_ci		value |= SOR_PLL1_LOADADJ(0x6);
91562306a36Sopenharmony_ci		break;
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* use alternate scrambler reset for eDP */
92162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (link->edp == 0)
92462306a36Sopenharmony_ci		value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
92562306a36Sopenharmony_ci	else
92662306a36Sopenharmony_ci		value |= SOR_DP_SPARE_PANEL_INTERNAL;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	err = tegra_sor_power_down_lanes(sor);
93162306a36Sopenharmony_ci	if (err < 0) {
93262306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power down lanes: %d\n", err);
93362306a36Sopenharmony_ci		return err;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* power up and pre-charge lanes */
93762306a36Sopenharmony_ci	err = tegra_sor_power_up_lanes(sor, lanes);
93862306a36Sopenharmony_ci	if (err < 0) {
93962306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power up %u lane%s: %d\n",
94062306a36Sopenharmony_ci			lanes, (lanes != 1) ? "s" : "", err);
94162306a36Sopenharmony_ci		return err;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	tegra_sor_dp_precharge(sor, lanes);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	return 0;
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic const struct drm_dp_link_ops tegra_sor_dp_link_ops = {
95062306a36Sopenharmony_ci	.apply_training = tegra_sor_dp_link_apply_training,
95162306a36Sopenharmony_ci	.configure = tegra_sor_dp_link_configure,
95262306a36Sopenharmony_ci};
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic void tegra_sor_super_update(struct tegra_sor *sor)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_SUPER_STATE0);
95762306a36Sopenharmony_ci	tegra_sor_writel(sor, 1, SOR_SUPER_STATE0);
95862306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_SUPER_STATE0);
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic void tegra_sor_update(struct tegra_sor *sor)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE0);
96462306a36Sopenharmony_ci	tegra_sor_writel(sor, 1, SOR_STATE0);
96562306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE0);
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	u32 value;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWM_DIV);
97362306a36Sopenharmony_ci	value &= ~SOR_PWM_DIV_MASK;
97462306a36Sopenharmony_ci	value |= 0x400; /* period */
97562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWM_DIV);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWM_CTL);
97862306a36Sopenharmony_ci	value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK;
97962306a36Sopenharmony_ci	value |= 0x400; /* duty cycle */
98062306a36Sopenharmony_ci	value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */
98162306a36Sopenharmony_ci	value |= SOR_PWM_CTL_TRIGGER;
98262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWM_CTL);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
98762306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWM_CTL);
98862306a36Sopenharmony_ci		if ((value & SOR_PWM_CTL_TRIGGER) == 0)
98962306a36Sopenharmony_ci			return 0;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		usleep_range(25, 100);
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	return -ETIMEDOUT;
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic int tegra_sor_attach(struct tegra_sor *sor)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	unsigned long value, timeout;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	/* wake up in normal mode */
100262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
100362306a36Sopenharmony_ci	value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE;
100462306a36Sopenharmony_ci	value |= SOR_SUPER_STATE_MODE_NORMAL;
100562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
100662306a36Sopenharmony_ci	tegra_sor_super_update(sor);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	/* attach */
100962306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
101062306a36Sopenharmony_ci	value |= SOR_SUPER_STATE_ATTACHED;
101162306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
101262306a36Sopenharmony_ci	tegra_sor_super_update(sor);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
101762306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_TEST);
101862306a36Sopenharmony_ci		if ((value & SOR_TEST_ATTACHED) != 0)
101962306a36Sopenharmony_ci			return 0;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci		usleep_range(25, 100);
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return -ETIMEDOUT;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic int tegra_sor_wakeup(struct tegra_sor *sor)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	unsigned long value, timeout;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* wait for head to wake up */
103462306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
103562306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_TEST);
103662306a36Sopenharmony_ci		value &= SOR_TEST_HEAD_MODE_MASK;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		if (value == SOR_TEST_HEAD_MODE_AWAKE)
103962306a36Sopenharmony_ci			return 0;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci		usleep_range(25, 100);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	return -ETIMEDOUT;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	u32 value;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWR);
105262306a36Sopenharmony_ci	value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU;
105362306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWR);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
105862306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWR);
105962306a36Sopenharmony_ci		if ((value & SOR_PWR_TRIGGER) == 0)
106062306a36Sopenharmony_ci			return 0;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		usleep_range(25, 100);
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	return -ETIMEDOUT;
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistruct tegra_sor_params {
106962306a36Sopenharmony_ci	/* number of link clocks per line */
107062306a36Sopenharmony_ci	unsigned int num_clocks;
107162306a36Sopenharmony_ci	/* ratio between input and output */
107262306a36Sopenharmony_ci	u64 ratio;
107362306a36Sopenharmony_ci	/* precision factor */
107462306a36Sopenharmony_ci	u64 precision;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	unsigned int active_polarity;
107762306a36Sopenharmony_ci	unsigned int active_count;
107862306a36Sopenharmony_ci	unsigned int active_frac;
107962306a36Sopenharmony_ci	unsigned int tu_size;
108062306a36Sopenharmony_ci	unsigned int error;
108162306a36Sopenharmony_ci};
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic int tegra_sor_compute_params(struct tegra_sor *sor,
108462306a36Sopenharmony_ci				    struct tegra_sor_params *params,
108562306a36Sopenharmony_ci				    unsigned int tu_size)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	u64 active_sym, active_count, frac, approx;
108862306a36Sopenharmony_ci	u32 active_polarity, active_frac = 0;
108962306a36Sopenharmony_ci	const u64 f = params->precision;
109062306a36Sopenharmony_ci	s64 error;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	active_sym = params->ratio * tu_size;
109362306a36Sopenharmony_ci	active_count = div_u64(active_sym, f) * f;
109462306a36Sopenharmony_ci	frac = active_sym - active_count;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	/* fraction < 0.5 */
109762306a36Sopenharmony_ci	if (frac >= (f / 2)) {
109862306a36Sopenharmony_ci		active_polarity = 1;
109962306a36Sopenharmony_ci		frac = f - frac;
110062306a36Sopenharmony_ci	} else {
110162306a36Sopenharmony_ci		active_polarity = 0;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (frac != 0) {
110562306a36Sopenharmony_ci		frac = div_u64(f * f,  frac); /* 1/fraction */
110662306a36Sopenharmony_ci		if (frac <= (15 * f)) {
110762306a36Sopenharmony_ci			active_frac = div_u64(frac, f);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci			/* round up */
111062306a36Sopenharmony_ci			if (active_polarity)
111162306a36Sopenharmony_ci				active_frac++;
111262306a36Sopenharmony_ci		} else {
111362306a36Sopenharmony_ci			active_frac = active_polarity ? 1 : 15;
111462306a36Sopenharmony_ci		}
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (active_frac == 1)
111862306a36Sopenharmony_ci		active_polarity = 0;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (active_polarity == 1) {
112162306a36Sopenharmony_ci		if (active_frac) {
112262306a36Sopenharmony_ci			approx = active_count + (active_frac * (f - 1)) * f;
112362306a36Sopenharmony_ci			approx = div_u64(approx, active_frac * f);
112462306a36Sopenharmony_ci		} else {
112562306a36Sopenharmony_ci			approx = active_count + f;
112662306a36Sopenharmony_ci		}
112762306a36Sopenharmony_ci	} else {
112862306a36Sopenharmony_ci		if (active_frac)
112962306a36Sopenharmony_ci			approx = active_count + div_u64(f, active_frac);
113062306a36Sopenharmony_ci		else
113162306a36Sopenharmony_ci			approx = active_count;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	error = div_s64(active_sym - approx, tu_size);
113562306a36Sopenharmony_ci	error *= params->num_clocks;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (error <= 0 && abs(error) < params->error) {
113862306a36Sopenharmony_ci		params->active_count = div_u64(active_count, f);
113962306a36Sopenharmony_ci		params->active_polarity = active_polarity;
114062306a36Sopenharmony_ci		params->active_frac = active_frac;
114162306a36Sopenharmony_ci		params->error = abs(error);
114262306a36Sopenharmony_ci		params->tu_size = tu_size;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		if (error == 0)
114562306a36Sopenharmony_ci			return true;
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	return false;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cistatic int tegra_sor_compute_config(struct tegra_sor *sor,
115262306a36Sopenharmony_ci				    const struct drm_display_mode *mode,
115362306a36Sopenharmony_ci				    struct tegra_sor_config *config,
115462306a36Sopenharmony_ci				    struct drm_dp_link *link)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	const u64 f = 100000, link_rate = link->rate * 1000;
115762306a36Sopenharmony_ci	const u64 pclk = (u64)mode->clock * 1000;
115862306a36Sopenharmony_ci	u64 input, output, watermark, num;
115962306a36Sopenharmony_ci	struct tegra_sor_params params;
116062306a36Sopenharmony_ci	u32 num_syms_per_line;
116162306a36Sopenharmony_ci	unsigned int i;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (!link_rate || !link->lanes || !pclk || !config->bits_per_pixel)
116462306a36Sopenharmony_ci		return -EINVAL;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	input = pclk * config->bits_per_pixel;
116762306a36Sopenharmony_ci	output = link_rate * 8 * link->lanes;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (input >= output)
117062306a36Sopenharmony_ci		return -ERANGE;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	memset(&params, 0, sizeof(params));
117362306a36Sopenharmony_ci	params.ratio = div64_u64(input * f, output);
117462306a36Sopenharmony_ci	params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk);
117562306a36Sopenharmony_ci	params.precision = f;
117662306a36Sopenharmony_ci	params.error = 64 * f;
117762306a36Sopenharmony_ci	params.tu_size = 64;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	for (i = params.tu_size; i >= 32; i--)
118062306a36Sopenharmony_ci		if (tegra_sor_compute_params(sor, &params, i))
118162306a36Sopenharmony_ci			break;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (params.active_frac == 0) {
118462306a36Sopenharmony_ci		config->active_polarity = 0;
118562306a36Sopenharmony_ci		config->active_count = params.active_count;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		if (!params.active_polarity)
118862306a36Sopenharmony_ci			config->active_count--;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		config->tu_size = params.tu_size;
119162306a36Sopenharmony_ci		config->active_frac = 1;
119262306a36Sopenharmony_ci	} else {
119362306a36Sopenharmony_ci		config->active_polarity = params.active_polarity;
119462306a36Sopenharmony_ci		config->active_count = params.active_count;
119562306a36Sopenharmony_ci		config->active_frac = params.active_frac;
119662306a36Sopenharmony_ci		config->tu_size = params.tu_size;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	dev_dbg(sor->dev,
120062306a36Sopenharmony_ci		"polarity: %d active count: %d tu size: %d active frac: %d\n",
120162306a36Sopenharmony_ci		config->active_polarity, config->active_count,
120262306a36Sopenharmony_ci		config->tu_size, config->active_frac);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	watermark = params.ratio * config->tu_size * (f - params.ratio);
120562306a36Sopenharmony_ci	watermark = div_u64(watermark, f);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	watermark = div_u64(watermark + params.error, f);
120862306a36Sopenharmony_ci	config->watermark = watermark + (config->bits_per_pixel / 8) + 2;
120962306a36Sopenharmony_ci	num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) *
121062306a36Sopenharmony_ci			    (link->lanes * 8);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (config->watermark > 30) {
121362306a36Sopenharmony_ci		config->watermark = 30;
121462306a36Sopenharmony_ci		dev_err(sor->dev,
121562306a36Sopenharmony_ci			"unable to compute TU size, forcing watermark to %u\n",
121662306a36Sopenharmony_ci			config->watermark);
121762306a36Sopenharmony_ci	} else if (config->watermark > num_syms_per_line) {
121862306a36Sopenharmony_ci		config->watermark = num_syms_per_line;
121962306a36Sopenharmony_ci		dev_err(sor->dev, "watermark too high, forcing to %u\n",
122062306a36Sopenharmony_ci			config->watermark);
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	/* compute the number of symbols per horizontal blanking interval */
122462306a36Sopenharmony_ci	num = ((mode->htotal - mode->hdisplay) - 7) * link_rate;
122562306a36Sopenharmony_ci	config->hblank_symbols = div_u64(num, pclk);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (link->caps.enhanced_framing)
122862306a36Sopenharmony_ci		config->hblank_symbols -= 3;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	config->hblank_symbols -= 12 / link->lanes;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/* compute the number of symbols per vertical blanking interval */
123362306a36Sopenharmony_ci	num = (mode->hdisplay - 25) * link_rate;
123462306a36Sopenharmony_ci	config->vblank_symbols = div_u64(num, pclk);
123562306a36Sopenharmony_ci	config->vblank_symbols -= 36 / link->lanes + 4;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols,
123862306a36Sopenharmony_ci		config->vblank_symbols);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	return 0;
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic void tegra_sor_apply_config(struct tegra_sor *sor,
124462306a36Sopenharmony_ci				   const struct tegra_sor_config *config)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	u32 value;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
124962306a36Sopenharmony_ci	value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
125062306a36Sopenharmony_ci	value |= SOR_DP_LINKCTL_TU_SIZE(config->tu_size);
125162306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_CONFIG0);
125462306a36Sopenharmony_ci	value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
125562306a36Sopenharmony_ci	value |= SOR_DP_CONFIG_WATERMARK(config->watermark);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
125862306a36Sopenharmony_ci	value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config->active_count);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
126162306a36Sopenharmony_ci	value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config->active_frac);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (config->active_polarity)
126462306a36Sopenharmony_ci		value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
126562306a36Sopenharmony_ci	else
126662306a36Sopenharmony_ci		value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
126962306a36Sopenharmony_ci	value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE;
127062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_CONFIG0);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
127362306a36Sopenharmony_ci	value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
127462306a36Sopenharmony_ci	value |= config->hblank_symbols & 0xffff;
127562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
127862306a36Sopenharmony_ci	value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
127962306a36Sopenharmony_ci	value |= config->vblank_symbols & 0xffff;
128062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic void tegra_sor_mode_set(struct tegra_sor *sor,
128462306a36Sopenharmony_ci			       const struct drm_display_mode *mode,
128562306a36Sopenharmony_ci			       struct tegra_sor_state *state)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc);
128862306a36Sopenharmony_ci	unsigned int vbe, vse, hbe, hse, vbs, hbs;
128962306a36Sopenharmony_ci	u32 value;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
129262306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_PIXELDEPTH_MASK;
129362306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
129462306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_OWNER_MASK;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	value |= SOR_STATE_ASY_CRC_MODE_COMPLETE |
129762306a36Sopenharmony_ci		 SOR_STATE_ASY_OWNER(dc->pipe + 1);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
130062306a36Sopenharmony_ci		value &= ~SOR_STATE_ASY_HSYNCPOL;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
130362306a36Sopenharmony_ci		value |= SOR_STATE_ASY_HSYNCPOL;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
130662306a36Sopenharmony_ci		value &= ~SOR_STATE_ASY_VSYNCPOL;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
130962306a36Sopenharmony_ci		value |= SOR_STATE_ASY_VSYNCPOL;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	switch (state->bpc) {
131262306a36Sopenharmony_ci	case 16:
131362306a36Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_48_444;
131462306a36Sopenharmony_ci		break;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	case 12:
131762306a36Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_36_444;
131862306a36Sopenharmony_ci		break;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	case 10:
132162306a36Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_30_444;
132262306a36Sopenharmony_ci		break;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	case 8:
132562306a36Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
132662306a36Sopenharmony_ci		break;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	case 6:
132962306a36Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444;
133062306a36Sopenharmony_ci		break;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	default:
133362306a36Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
133462306a36Sopenharmony_ci		break;
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	/*
134062306a36Sopenharmony_ci	 * TODO: The video timing programming below doesn't seem to match the
134162306a36Sopenharmony_ci	 * register definitions.
134262306a36Sopenharmony_ci	 */
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
134562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state1 + dc->pipe);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	/* sync end = sync width - 1 */
134862306a36Sopenharmony_ci	vse = mode->vsync_end - mode->vsync_start - 1;
134962306a36Sopenharmony_ci	hse = mode->hsync_end - mode->hsync_start - 1;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
135262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state2 + dc->pipe);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/* blank end = sync end + back porch */
135562306a36Sopenharmony_ci	vbe = vse + (mode->vtotal - mode->vsync_end);
135662306a36Sopenharmony_ci	hbe = hse + (mode->htotal - mode->hsync_end);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
135962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state3 + dc->pipe);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/* blank start = blank end + active */
136262306a36Sopenharmony_ci	vbs = vbe + mode->vdisplay;
136362306a36Sopenharmony_ci	hbs = hbe + mode->hdisplay;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
136662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state4 + dc->pipe);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	/* XXX interlacing support */
136962306a36Sopenharmony_ci	tegra_sor_writel(sor, 0x001, sor->soc->regs->head_state5 + dc->pipe);
137062306a36Sopenharmony_ci}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_cistatic int tegra_sor_detach(struct tegra_sor *sor)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	unsigned long value, timeout;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	/* switch to safe mode */
137762306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
137862306a36Sopenharmony_ci	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
137962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
138062306a36Sopenharmony_ci	tegra_sor_super_update(sor);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
138562306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWR);
138662306a36Sopenharmony_ci		if (value & SOR_PWR_MODE_SAFE)
138762306a36Sopenharmony_ci			break;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	if ((value & SOR_PWR_MODE_SAFE) == 0)
139162306a36Sopenharmony_ci		return -ETIMEDOUT;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	/* go to sleep */
139462306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
139562306a36Sopenharmony_ci	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
139662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
139762306a36Sopenharmony_ci	tegra_sor_super_update(sor);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	/* detach */
140062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
140162306a36Sopenharmony_ci	value &= ~SOR_SUPER_STATE_ATTACHED;
140262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
140362306a36Sopenharmony_ci	tegra_sor_super_update(sor);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
140862306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_TEST);
140962306a36Sopenharmony_ci		if ((value & SOR_TEST_ATTACHED) == 0)
141062306a36Sopenharmony_ci			break;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		usleep_range(25, 100);
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	if ((value & SOR_TEST_ATTACHED) != 0)
141662306a36Sopenharmony_ci		return -ETIMEDOUT;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	return 0;
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_cistatic int tegra_sor_power_down(struct tegra_sor *sor)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	unsigned long value, timeout;
142462306a36Sopenharmony_ci	int err;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWR);
142762306a36Sopenharmony_ci	value &= ~SOR_PWR_NORMAL_STATE_PU;
142862306a36Sopenharmony_ci	value |= SOR_PWR_TRIGGER;
142962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWR);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
143462306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWR);
143562306a36Sopenharmony_ci		if ((value & SOR_PWR_TRIGGER) == 0)
143662306a36Sopenharmony_ci			return 0;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		usleep_range(25, 100);
143962306a36Sopenharmony_ci	}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	if ((value & SOR_PWR_TRIGGER) != 0)
144262306a36Sopenharmony_ci		return -ETIMEDOUT;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	/* switch to safe parent clock */
144562306a36Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
144662306a36Sopenharmony_ci	if (err < 0) {
144762306a36Sopenharmony_ci		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
144862306a36Sopenharmony_ci		return err;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
145262306a36Sopenharmony_ci	value |= SOR_PLL2_PORT_POWERDOWN;
145362306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	usleep_range(20, 100);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
145862306a36Sopenharmony_ci	value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR;
145962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
146262306a36Sopenharmony_ci	value |= SOR_PLL2_SEQ_PLLCAPPD;
146362306a36Sopenharmony_ci	value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
146462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	usleep_range(20, 100);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	return 0;
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_cistatic int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
147262306a36Sopenharmony_ci{
147362306a36Sopenharmony_ci	u32 value;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
147862306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_CRCA);
147962306a36Sopenharmony_ci		if (value & SOR_CRCA_VALID)
148062306a36Sopenharmony_ci			return 0;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci		usleep_range(100, 200);
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	return -ETIMEDOUT;
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cistatic int tegra_sor_show_crc(struct seq_file *s, void *data)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	struct drm_info_node *node = s->private;
149162306a36Sopenharmony_ci	struct tegra_sor *sor = node->info_ent->data;
149262306a36Sopenharmony_ci	struct drm_crtc *crtc = sor->output.encoder.crtc;
149362306a36Sopenharmony_ci	struct drm_device *drm = node->minor->dev;
149462306a36Sopenharmony_ci	int err = 0;
149562306a36Sopenharmony_ci	u32 value;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	drm_modeset_lock_all(drm);
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	if (!crtc || !crtc->state->active) {
150062306a36Sopenharmony_ci		err = -EBUSY;
150162306a36Sopenharmony_ci		goto unlock;
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
150562306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
150662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
150962306a36Sopenharmony_ci	value |= SOR_CRC_CNTRL_ENABLE;
151062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_TEST);
151362306a36Sopenharmony_ci	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
151462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_TEST);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	err = tegra_sor_crc_wait(sor, 100);
151762306a36Sopenharmony_ci	if (err < 0)
151862306a36Sopenharmony_ci		goto unlock;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	tegra_sor_writel(sor, SOR_CRCA_RESET, SOR_CRCA);
152162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CRCB);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	seq_printf(s, "%08x\n", value);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ciunlock:
152662306a36Sopenharmony_ci	drm_modeset_unlock_all(drm);
152762306a36Sopenharmony_ci	return err;
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic const struct debugfs_reg32 tegra_sor_regs[] = {
153362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CTXSW),
153462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SUPER_STATE0),
153562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SUPER_STATE1),
153662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_STATE0),
153762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_STATE1),
153862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE0(0)),
153962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE0(1)),
154062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE1(0)),
154162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE1(1)),
154262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE2(0)),
154362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE2(1)),
154462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE3(0)),
154562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE3(1)),
154662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE4(0)),
154762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE4(1)),
154862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE5(0)),
154962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE5(1)),
155062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CRC_CNTRL),
155162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_DEBUG_MVID),
155262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CLK_CNTRL),
155362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CAP),
155462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_PWR),
155562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_TEST),
155662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL0),
155762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL1),
155862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL2),
155962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL3),
156062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CSTM),
156162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LVDS),
156262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CRCA),
156362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CRCB),
156462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_BLANK),
156562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_CTL),
156662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_SEQ_CTL),
156762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(0)),
156862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(1)),
156962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(2)),
157062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(3)),
157162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(4)),
157262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(5)),
157362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(6)),
157462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(7)),
157562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(8)),
157662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(9)),
157762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(10)),
157862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(11)),
157962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(12)),
158062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(13)),
158162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(14)),
158262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(15)),
158362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_PWM_DIV),
158462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_PWM_CTL),
158562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_A0),
158662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_A1),
158762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_B0),
158862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_B1),
158962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_A0),
159062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_A1),
159162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_B0),
159262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_B1),
159362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_A0),
159462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_A1),
159562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_B0),
159662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_B1),
159762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_A0),
159862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_A1),
159962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_B0),
160062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_B1),
160162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_A0),
160262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_A1),
160362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_B0),
160462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_B1),
160562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_TRIG),
160662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_MSCHECK),
160762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_XBAR_CTRL),
160862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_XBAR_POL),
160962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LINKCTL0),
161062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LINKCTL1),
161162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT0),
161262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT1),
161362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT0),
161462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT1),
161562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_PREEMPHASIS0),
161662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_PREEMPHASIS1),
161762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS0),
161862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS1),
161962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_POSTCURSOR0),
162062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_POSTCURSOR1),
162162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_CONFIG0),
162262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_CONFIG1),
162362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_MN0),
162462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_MN1),
162562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_PADCTL0),
162662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_PADCTL1),
162762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_PADCTL2),
162862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_DEBUG0),
162962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_DEBUG1),
163062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_SPARE0),
163162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_SPARE1),
163262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_AUDIO_CTRL),
163362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_AUDIO_HBLANK_SYMBOLS),
163462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_AUDIO_VBLANK_SYMBOLS),
163562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_HEADER),
163662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK0),
163762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK1),
163862306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK2),
163962306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK3),
164062306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK4),
164162306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK5),
164262306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK6),
164362306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_TPG),
164462306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_TPG_CONFIG),
164562306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LQ_CSTM0),
164662306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LQ_CSTM1),
164762306a36Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LQ_CSTM2),
164862306a36Sopenharmony_ci};
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic int tegra_sor_show_regs(struct seq_file *s, void *data)
165162306a36Sopenharmony_ci{
165262306a36Sopenharmony_ci	struct drm_info_node *node = s->private;
165362306a36Sopenharmony_ci	struct tegra_sor *sor = node->info_ent->data;
165462306a36Sopenharmony_ci	struct drm_crtc *crtc = sor->output.encoder.crtc;
165562306a36Sopenharmony_ci	struct drm_device *drm = node->minor->dev;
165662306a36Sopenharmony_ci	unsigned int i;
165762306a36Sopenharmony_ci	int err = 0;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	drm_modeset_lock_all(drm);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	if (!crtc || !crtc->state->active) {
166262306a36Sopenharmony_ci		err = -EBUSY;
166362306a36Sopenharmony_ci		goto unlock;
166462306a36Sopenharmony_ci	}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tegra_sor_regs); i++) {
166762306a36Sopenharmony_ci		unsigned int offset = tegra_sor_regs[i].offset;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci		seq_printf(s, "%-38s %#05x %08x\n", tegra_sor_regs[i].name,
167062306a36Sopenharmony_ci			   offset, tegra_sor_readl(sor, offset));
167162306a36Sopenharmony_ci	}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ciunlock:
167462306a36Sopenharmony_ci	drm_modeset_unlock_all(drm);
167562306a36Sopenharmony_ci	return err;
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cistatic const struct drm_info_list debugfs_files[] = {
167962306a36Sopenharmony_ci	{ "crc", tegra_sor_show_crc, 0, NULL },
168062306a36Sopenharmony_ci	{ "regs", tegra_sor_show_regs, 0, NULL },
168162306a36Sopenharmony_ci};
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cistatic int tegra_sor_late_register(struct drm_connector *connector)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
168662306a36Sopenharmony_ci	unsigned int i, count = ARRAY_SIZE(debugfs_files);
168762306a36Sopenharmony_ci	struct drm_minor *minor = connector->dev->primary;
168862306a36Sopenharmony_ci	struct dentry *root = connector->debugfs_entry;
168962306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
169262306a36Sopenharmony_ci				     GFP_KERNEL);
169362306a36Sopenharmony_ci	if (!sor->debugfs_files)
169462306a36Sopenharmony_ci		return -ENOMEM;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	for (i = 0; i < count; i++)
169762306a36Sopenharmony_ci		sor->debugfs_files[i].data = sor;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	drm_debugfs_create_files(sor->debugfs_files, count, root, minor);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	return 0;
170262306a36Sopenharmony_ci}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_cistatic void tegra_sor_early_unregister(struct drm_connector *connector)
170562306a36Sopenharmony_ci{
170662306a36Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
170762306a36Sopenharmony_ci	unsigned int count = ARRAY_SIZE(debugfs_files);
170862306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	drm_debugfs_remove_files(sor->debugfs_files, count,
171162306a36Sopenharmony_ci				 connector->dev->primary);
171262306a36Sopenharmony_ci	kfree(sor->debugfs_files);
171362306a36Sopenharmony_ci	sor->debugfs_files = NULL;
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_cistatic void tegra_sor_connector_reset(struct drm_connector *connector)
171762306a36Sopenharmony_ci{
171862306a36Sopenharmony_ci	struct tegra_sor_state *state;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
172162306a36Sopenharmony_ci	if (!state)
172262306a36Sopenharmony_ci		return;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	if (connector->state) {
172562306a36Sopenharmony_ci		__drm_atomic_helper_connector_destroy_state(connector->state);
172662306a36Sopenharmony_ci		kfree(connector->state);
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	__drm_atomic_helper_connector_reset(connector, &state->base);
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic enum drm_connector_status
173362306a36Sopenharmony_citegra_sor_connector_detect(struct drm_connector *connector, bool force)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
173662306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	if (sor->aux)
173962306a36Sopenharmony_ci		return drm_dp_aux_detect(sor->aux);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	return tegra_output_connector_detect(connector, force);
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistatic struct drm_connector_state *
174562306a36Sopenharmony_citegra_sor_connector_duplicate_state(struct drm_connector *connector)
174662306a36Sopenharmony_ci{
174762306a36Sopenharmony_ci	struct tegra_sor_state *state = to_sor_state(connector->state);
174862306a36Sopenharmony_ci	struct tegra_sor_state *copy;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
175162306a36Sopenharmony_ci	if (!copy)
175262306a36Sopenharmony_ci		return NULL;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	__drm_atomic_helper_connector_duplicate_state(connector, &copy->base);
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	return &copy->base;
175762306a36Sopenharmony_ci}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic const struct drm_connector_funcs tegra_sor_connector_funcs = {
176062306a36Sopenharmony_ci	.reset = tegra_sor_connector_reset,
176162306a36Sopenharmony_ci	.detect = tegra_sor_connector_detect,
176262306a36Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
176362306a36Sopenharmony_ci	.destroy = tegra_output_connector_destroy,
176462306a36Sopenharmony_ci	.atomic_duplicate_state = tegra_sor_connector_duplicate_state,
176562306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
176662306a36Sopenharmony_ci	.late_register = tegra_sor_late_register,
176762306a36Sopenharmony_ci	.early_unregister = tegra_sor_early_unregister,
176862306a36Sopenharmony_ci};
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_cistatic int tegra_sor_connector_get_modes(struct drm_connector *connector)
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
177362306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
177462306a36Sopenharmony_ci	int err;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	if (sor->aux)
177762306a36Sopenharmony_ci		drm_dp_aux_enable(sor->aux);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	err = tegra_output_connector_get_modes(connector);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	if (sor->aux)
178262306a36Sopenharmony_ci		drm_dp_aux_disable(sor->aux);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	return err;
178562306a36Sopenharmony_ci}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_cistatic enum drm_mode_status
178862306a36Sopenharmony_citegra_sor_connector_mode_valid(struct drm_connector *connector,
178962306a36Sopenharmony_ci			       struct drm_display_mode *mode)
179062306a36Sopenharmony_ci{
179162306a36Sopenharmony_ci	return MODE_OK;
179262306a36Sopenharmony_ci}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
179562306a36Sopenharmony_ci	.get_modes = tegra_sor_connector_get_modes,
179662306a36Sopenharmony_ci	.mode_valid = tegra_sor_connector_mode_valid,
179762306a36Sopenharmony_ci};
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_cistatic int
180062306a36Sopenharmony_citegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
180162306a36Sopenharmony_ci			       struct drm_crtc_state *crtc_state,
180262306a36Sopenharmony_ci			       struct drm_connector_state *conn_state)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
180562306a36Sopenharmony_ci	struct tegra_sor_state *state = to_sor_state(conn_state);
180662306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
180762306a36Sopenharmony_ci	unsigned long pclk = crtc_state->mode.clock * 1000;
180862306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
180962306a36Sopenharmony_ci	struct drm_display_info *info;
181062306a36Sopenharmony_ci	int err;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	info = &output->connector.display_info;
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	/*
181562306a36Sopenharmony_ci	 * For HBR2 modes, the SOR brick needs to use the x20 multiplier, so
181662306a36Sopenharmony_ci	 * the pixel clock must be corrected accordingly.
181762306a36Sopenharmony_ci	 */
181862306a36Sopenharmony_ci	if (pclk >= 340000000) {
181962306a36Sopenharmony_ci		state->link_speed = 20;
182062306a36Sopenharmony_ci		state->pclk = pclk / 2;
182162306a36Sopenharmony_ci	} else {
182262306a36Sopenharmony_ci		state->link_speed = 10;
182362306a36Sopenharmony_ci		state->pclk = pclk;
182462306a36Sopenharmony_ci	}
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent,
182762306a36Sopenharmony_ci					 pclk, 0);
182862306a36Sopenharmony_ci	if (err < 0) {
182962306a36Sopenharmony_ci		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
183062306a36Sopenharmony_ci		return err;
183162306a36Sopenharmony_ci	}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	switch (info->bpc) {
183462306a36Sopenharmony_ci	case 8:
183562306a36Sopenharmony_ci	case 6:
183662306a36Sopenharmony_ci		state->bpc = info->bpc;
183762306a36Sopenharmony_ci		break;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	default:
184062306a36Sopenharmony_ci		DRM_DEBUG_KMS("%u bits-per-color not supported\n", info->bpc);
184162306a36Sopenharmony_ci		state->bpc = 8;
184262306a36Sopenharmony_ci		break;
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	return 0;
184662306a36Sopenharmony_ci}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_cistatic inline u32 tegra_sor_hdmi_subpack(const u8 *ptr, size_t size)
184962306a36Sopenharmony_ci{
185062306a36Sopenharmony_ci	u32 value = 0;
185162306a36Sopenharmony_ci	size_t i;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	for (i = size; i > 0; i--)
185462306a36Sopenharmony_ci		value = (value << 8) | ptr[i - 1];
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	return value;
185762306a36Sopenharmony_ci}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_cistatic void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor,
186062306a36Sopenharmony_ci					  const void *data, size_t size)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	const u8 *ptr = data;
186362306a36Sopenharmony_ci	unsigned long offset;
186462306a36Sopenharmony_ci	size_t i, j;
186562306a36Sopenharmony_ci	u32 value;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	switch (ptr[0]) {
186862306a36Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_AVI:
186962306a36Sopenharmony_ci		offset = SOR_HDMI_AVI_INFOFRAME_HEADER;
187062306a36Sopenharmony_ci		break;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_AUDIO:
187362306a36Sopenharmony_ci		offset = SOR_HDMI_AUDIO_INFOFRAME_HEADER;
187462306a36Sopenharmony_ci		break;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_VENDOR:
187762306a36Sopenharmony_ci		offset = SOR_HDMI_VSI_INFOFRAME_HEADER;
187862306a36Sopenharmony_ci		break;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	default:
188162306a36Sopenharmony_ci		dev_err(sor->dev, "unsupported infoframe type: %02x\n",
188262306a36Sopenharmony_ci			ptr[0]);
188362306a36Sopenharmony_ci		return;
188462306a36Sopenharmony_ci	}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	value = INFOFRAME_HEADER_TYPE(ptr[0]) |
188762306a36Sopenharmony_ci		INFOFRAME_HEADER_VERSION(ptr[1]) |
188862306a36Sopenharmony_ci		INFOFRAME_HEADER_LEN(ptr[2]);
188962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, offset);
189062306a36Sopenharmony_ci	offset++;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	/*
189362306a36Sopenharmony_ci	 * Each subpack contains 7 bytes, divided into:
189462306a36Sopenharmony_ci	 * - subpack_low: bytes 0 - 3
189562306a36Sopenharmony_ci	 * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00)
189662306a36Sopenharmony_ci	 */
189762306a36Sopenharmony_ci	for (i = 3, j = 0; i < size; i += 7, j += 8) {
189862306a36Sopenharmony_ci		size_t rem = size - i, num = min_t(size_t, rem, 4);
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci		value = tegra_sor_hdmi_subpack(&ptr[i], num);
190162306a36Sopenharmony_ci		tegra_sor_writel(sor, value, offset++);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci		num = min_t(size_t, rem - num, 3);
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci		value = tegra_sor_hdmi_subpack(&ptr[i + 4], num);
190662306a36Sopenharmony_ci		tegra_sor_writel(sor, value, offset++);
190762306a36Sopenharmony_ci	}
190862306a36Sopenharmony_ci}
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_cistatic int
191162306a36Sopenharmony_citegra_sor_hdmi_setup_avi_infoframe(struct tegra_sor *sor,
191262306a36Sopenharmony_ci				   const struct drm_display_mode *mode)
191362306a36Sopenharmony_ci{
191462306a36Sopenharmony_ci	u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
191562306a36Sopenharmony_ci	struct hdmi_avi_infoframe frame;
191662306a36Sopenharmony_ci	u32 value;
191762306a36Sopenharmony_ci	int err;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	/* disable AVI infoframe */
192062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AVI_INFOFRAME_CTRL);
192162306a36Sopenharmony_ci	value &= ~INFOFRAME_CTRL_SINGLE;
192262306a36Sopenharmony_ci	value &= ~INFOFRAME_CTRL_OTHER;
192362306a36Sopenharmony_ci	value &= ~INFOFRAME_CTRL_ENABLE;
192462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AVI_INFOFRAME_CTRL);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	err = drm_hdmi_avi_infoframe_from_display_mode(&frame,
192762306a36Sopenharmony_ci						       &sor->output.connector, mode);
192862306a36Sopenharmony_ci	if (err < 0) {
192962306a36Sopenharmony_ci		dev_err(sor->dev, "failed to setup AVI infoframe: %d\n", err);
193062306a36Sopenharmony_ci		return err;
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
193462306a36Sopenharmony_ci	if (err < 0) {
193562306a36Sopenharmony_ci		dev_err(sor->dev, "failed to pack AVI infoframe: %d\n", err);
193662306a36Sopenharmony_ci		return err;
193762306a36Sopenharmony_ci	}
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	tegra_sor_hdmi_write_infopack(sor, buffer, err);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	/* enable AVI infoframe */
194262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AVI_INFOFRAME_CTRL);
194362306a36Sopenharmony_ci	value |= INFOFRAME_CTRL_CHECKSUM_ENABLE;
194462306a36Sopenharmony_ci	value |= INFOFRAME_CTRL_ENABLE;
194562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AVI_INFOFRAME_CTRL);
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	return 0;
194862306a36Sopenharmony_ci}
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_cistatic void tegra_sor_write_eld(struct tegra_sor *sor)
195162306a36Sopenharmony_ci{
195262306a36Sopenharmony_ci	size_t length = drm_eld_size(sor->output.connector.eld), i;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	for (i = 0; i < length; i++)
195562306a36Sopenharmony_ci		tegra_sor_writel(sor, i << 8 | sor->output.connector.eld[i],
195662306a36Sopenharmony_ci				 SOR_AUDIO_HDA_ELD_BUFWR);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	/*
195962306a36Sopenharmony_ci	 * The HDA codec will always report an ELD buffer size of 96 bytes and
196062306a36Sopenharmony_ci	 * the HDA codec driver will check that each byte read from the buffer
196162306a36Sopenharmony_ci	 * is valid. Therefore every byte must be written, even if no 96 bytes
196262306a36Sopenharmony_ci	 * were parsed from EDID.
196362306a36Sopenharmony_ci	 */
196462306a36Sopenharmony_ci	for (i = length; i < 96; i++)
196562306a36Sopenharmony_ci		tegra_sor_writel(sor, i << 8 | 0, SOR_AUDIO_HDA_ELD_BUFWR);
196662306a36Sopenharmony_ci}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_cistatic void tegra_sor_audio_prepare(struct tegra_sor *sor)
196962306a36Sopenharmony_ci{
197062306a36Sopenharmony_ci	u32 value;
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	/*
197362306a36Sopenharmony_ci	 * Enable and unmask the HDA codec SCRATCH0 register interrupt. This
197462306a36Sopenharmony_ci	 * is used for interoperability between the HDA codec driver and the
197562306a36Sopenharmony_ci	 * HDMI/DP driver.
197662306a36Sopenharmony_ci	 */
197762306a36Sopenharmony_ci	value = SOR_INT_CODEC_SCRATCH1 | SOR_INT_CODEC_SCRATCH0;
197862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_INT_ENABLE);
197962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_INT_MASK);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	tegra_sor_write_eld(sor);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	value = SOR_AUDIO_HDA_PRESENSE_ELDV | SOR_AUDIO_HDA_PRESENSE_PD;
198462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_HDA_PRESENSE);
198562306a36Sopenharmony_ci}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_cistatic void tegra_sor_audio_unprepare(struct tegra_sor *sor)
198862306a36Sopenharmony_ci{
198962306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_AUDIO_HDA_PRESENSE);
199062306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_INT_MASK);
199162306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_INT_ENABLE);
199262306a36Sopenharmony_ci}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_cistatic void tegra_sor_audio_enable(struct tegra_sor *sor)
199562306a36Sopenharmony_ci{
199662306a36Sopenharmony_ci	u32 value;
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_AUDIO_CNTRL);
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	/* select HDA audio input */
200162306a36Sopenharmony_ci	value &= ~SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_MASK);
200262306a36Sopenharmony_ci	value |= SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_HDA);
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	/* inject null samples */
200562306a36Sopenharmony_ci	if (sor->format.channels != 2)
200662306a36Sopenharmony_ci		value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
200762306a36Sopenharmony_ci	else
200862306a36Sopenharmony_ci		value |= SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	value |= SOR_AUDIO_CNTRL_AFIFO_FLUSH;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_CNTRL);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	/* enable advertising HBR capability */
201562306a36Sopenharmony_ci	tegra_sor_writel(sor, SOR_AUDIO_SPARE_HBR_ENABLE, SOR_AUDIO_SPARE);
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_cistatic int tegra_sor_hdmi_enable_audio_infoframe(struct tegra_sor *sor)
201962306a36Sopenharmony_ci{
202062306a36Sopenharmony_ci	u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)];
202162306a36Sopenharmony_ci	struct hdmi_audio_infoframe frame;
202262306a36Sopenharmony_ci	u32 value;
202362306a36Sopenharmony_ci	int err;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	err = hdmi_audio_infoframe_init(&frame);
202662306a36Sopenharmony_ci	if (err < 0) {
202762306a36Sopenharmony_ci		dev_err(sor->dev, "failed to setup audio infoframe: %d\n", err);
202862306a36Sopenharmony_ci		return err;
202962306a36Sopenharmony_ci	}
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	frame.channels = sor->format.channels;
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
203462306a36Sopenharmony_ci	if (err < 0) {
203562306a36Sopenharmony_ci		dev_err(sor->dev, "failed to pack audio infoframe: %d\n", err);
203662306a36Sopenharmony_ci		return err;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	tegra_sor_hdmi_write_infopack(sor, buffer, err);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
204262306a36Sopenharmony_ci	value |= INFOFRAME_CTRL_CHECKSUM_ENABLE;
204362306a36Sopenharmony_ci	value |= INFOFRAME_CTRL_ENABLE;
204462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	return 0;
204762306a36Sopenharmony_ci}
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_cistatic void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
205062306a36Sopenharmony_ci{
205162306a36Sopenharmony_ci	u32 value;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	tegra_sor_audio_enable(sor);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_HDMI_ACR_CTRL);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	value = SOR_HDMI_SPARE_ACR_PRIORITY_HIGH |
205862306a36Sopenharmony_ci		SOR_HDMI_SPARE_CTS_RESET(1) |
205962306a36Sopenharmony_ci		SOR_HDMI_SPARE_HW_CTS_ENABLE;
206062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_SPARE);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	/* enable HW CTS */
206362306a36Sopenharmony_ci	value = SOR_HDMI_ACR_SUBPACK_LOW_SB1(0);
206462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_ACR_0441_SUBPACK_LOW);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	/* allow packet to be sent */
206762306a36Sopenharmony_ci	value = SOR_HDMI_ACR_SUBPACK_HIGH_ENABLE;
206862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_ACR_0441_SUBPACK_HIGH);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	/* reset N counter and enable lookup */
207162306a36Sopenharmony_ci	value = SOR_HDMI_AUDIO_N_RESET | SOR_HDMI_AUDIO_N_LOOKUP;
207262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N);
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	value = (24000 * 4096) / (128 * sor->format.sample_rate / 1000);
207562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0320);
207662306a36Sopenharmony_ci	tegra_sor_writel(sor, 4096, SOR_AUDIO_NVAL_0320);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_0441);
207962306a36Sopenharmony_ci	tegra_sor_writel(sor, 4704, SOR_AUDIO_NVAL_0441);
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_0882);
208262306a36Sopenharmony_ci	tegra_sor_writel(sor, 9408, SOR_AUDIO_NVAL_0882);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_1764);
208562306a36Sopenharmony_ci	tegra_sor_writel(sor, 18816, SOR_AUDIO_NVAL_1764);
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	value = (24000 * 6144) / (128 * sor->format.sample_rate / 1000);
208862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0480);
208962306a36Sopenharmony_ci	tegra_sor_writel(sor, 6144, SOR_AUDIO_NVAL_0480);
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	value = (24000 * 12288) / (128 * sor->format.sample_rate / 1000);
209262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0960);
209362306a36Sopenharmony_ci	tegra_sor_writel(sor, 12288, SOR_AUDIO_NVAL_0960);
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	value = (24000 * 24576) / (128 * sor->format.sample_rate / 1000);
209662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_1920);
209762306a36Sopenharmony_ci	tegra_sor_writel(sor, 24576, SOR_AUDIO_NVAL_1920);
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AUDIO_N);
210062306a36Sopenharmony_ci	value &= ~SOR_HDMI_AUDIO_N_RESET;
210162306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	tegra_sor_hdmi_enable_audio_infoframe(sor);
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_cistatic void tegra_sor_hdmi_disable_audio_infoframe(struct tegra_sor *sor)
210762306a36Sopenharmony_ci{
210862306a36Sopenharmony_ci	u32 value;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
211162306a36Sopenharmony_ci	value &= ~INFOFRAME_CTRL_ENABLE;
211262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
211362306a36Sopenharmony_ci}
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_cistatic void tegra_sor_hdmi_audio_disable(struct tegra_sor *sor)
211662306a36Sopenharmony_ci{
211762306a36Sopenharmony_ci	tegra_sor_hdmi_disable_audio_infoframe(sor);
211862306a36Sopenharmony_ci}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_cistatic struct tegra_sor_hdmi_settings *
212162306a36Sopenharmony_citegra_sor_hdmi_find_settings(struct tegra_sor *sor, unsigned long frequency)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	unsigned int i;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	for (i = 0; i < sor->num_settings; i++)
212662306a36Sopenharmony_ci		if (frequency <= sor->settings[i].frequency)
212762306a36Sopenharmony_ci			return &sor->settings[i];
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	return NULL;
213062306a36Sopenharmony_ci}
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_cistatic void tegra_sor_hdmi_disable_scrambling(struct tegra_sor *sor)
213362306a36Sopenharmony_ci{
213462306a36Sopenharmony_ci	u32 value;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI2_CTRL);
213762306a36Sopenharmony_ci	value &= ~SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4;
213862306a36Sopenharmony_ci	value &= ~SOR_HDMI2_CTRL_SCRAMBLE;
213962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI2_CTRL);
214062306a36Sopenharmony_ci}
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_cistatic void tegra_sor_hdmi_scdc_disable(struct tegra_sor *sor)
214362306a36Sopenharmony_ci{
214462306a36Sopenharmony_ci	drm_scdc_set_high_tmds_clock_ratio(&sor->output.connector, false);
214562306a36Sopenharmony_ci	drm_scdc_set_scrambling(&sor->output.connector, false);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	tegra_sor_hdmi_disable_scrambling(sor);
214862306a36Sopenharmony_ci}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_cistatic void tegra_sor_hdmi_scdc_stop(struct tegra_sor *sor)
215162306a36Sopenharmony_ci{
215262306a36Sopenharmony_ci	if (sor->scdc_enabled) {
215362306a36Sopenharmony_ci		cancel_delayed_work_sync(&sor->scdc);
215462306a36Sopenharmony_ci		tegra_sor_hdmi_scdc_disable(sor);
215562306a36Sopenharmony_ci	}
215662306a36Sopenharmony_ci}
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_cistatic void tegra_sor_hdmi_enable_scrambling(struct tegra_sor *sor)
215962306a36Sopenharmony_ci{
216062306a36Sopenharmony_ci	u32 value;
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI2_CTRL);
216362306a36Sopenharmony_ci	value |= SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4;
216462306a36Sopenharmony_ci	value |= SOR_HDMI2_CTRL_SCRAMBLE;
216562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI2_CTRL);
216662306a36Sopenharmony_ci}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_cistatic void tegra_sor_hdmi_scdc_enable(struct tegra_sor *sor)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	drm_scdc_set_high_tmds_clock_ratio(&sor->output.connector, true);
217162306a36Sopenharmony_ci	drm_scdc_set_scrambling(&sor->output.connector, true);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	tegra_sor_hdmi_enable_scrambling(sor);
217462306a36Sopenharmony_ci}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_cistatic void tegra_sor_hdmi_scdc_work(struct work_struct *work)
217762306a36Sopenharmony_ci{
217862306a36Sopenharmony_ci	struct tegra_sor *sor = container_of(work, struct tegra_sor, scdc.work);
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	if (!drm_scdc_get_scrambling_status(&sor->output.connector)) {
218162306a36Sopenharmony_ci		DRM_DEBUG_KMS("SCDC not scrambled\n");
218262306a36Sopenharmony_ci		tegra_sor_hdmi_scdc_enable(sor);
218362306a36Sopenharmony_ci	}
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000));
218662306a36Sopenharmony_ci}
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_cistatic void tegra_sor_hdmi_scdc_start(struct tegra_sor *sor)
218962306a36Sopenharmony_ci{
219062306a36Sopenharmony_ci	struct drm_scdc *scdc = &sor->output.connector.display_info.hdmi.scdc;
219162306a36Sopenharmony_ci	struct drm_display_mode *mode;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	mode = &sor->output.encoder.crtc->state->adjusted_mode;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	if (mode->clock >= 340000 && scdc->supported) {
219662306a36Sopenharmony_ci		schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000));
219762306a36Sopenharmony_ci		tegra_sor_hdmi_scdc_enable(sor);
219862306a36Sopenharmony_ci		sor->scdc_enabled = true;
219962306a36Sopenharmony_ci	}
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_cistatic void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
220362306a36Sopenharmony_ci{
220462306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
220562306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
220662306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
220762306a36Sopenharmony_ci	u32 value;
220862306a36Sopenharmony_ci	int err;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	tegra_sor_audio_unprepare(sor);
221162306a36Sopenharmony_ci	tegra_sor_hdmi_scdc_stop(sor);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	err = tegra_sor_detach(sor);
221462306a36Sopenharmony_ci	if (err < 0)
221562306a36Sopenharmony_ci		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE1);
221862306a36Sopenharmony_ci	tegra_sor_update(sor);
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	/* disable display to SOR clock */
222162306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	if (!sor->soc->has_nvdisplay)
222462306a36Sopenharmony_ci		value &= ~SOR1_TIMING_CYA;
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	value &= ~SOR_ENABLE(sor->index);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	tegra_dc_commit(dc);
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	err = tegra_sor_power_down(sor);
223362306a36Sopenharmony_ci	if (err < 0)
223462306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power down SOR: %d\n", err);
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	err = tegra_io_pad_power_disable(sor->pad);
223762306a36Sopenharmony_ci	if (err < 0)
223862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	host1x_client_suspend(&sor->client);
224162306a36Sopenharmony_ci}
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_cistatic void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
224462306a36Sopenharmony_ci{
224562306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
224662306a36Sopenharmony_ci	unsigned int h_ref_to_sync = 1, pulse_start, max_ac;
224762306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
224862306a36Sopenharmony_ci	struct tegra_sor_hdmi_settings *settings;
224962306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
225062306a36Sopenharmony_ci	struct tegra_sor_state *state;
225162306a36Sopenharmony_ci	struct drm_display_mode *mode;
225262306a36Sopenharmony_ci	unsigned long rate, pclk;
225362306a36Sopenharmony_ci	unsigned int div, i;
225462306a36Sopenharmony_ci	u32 value;
225562306a36Sopenharmony_ci	int err;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	state = to_sor_state(output->connector.state);
225862306a36Sopenharmony_ci	mode = &encoder->crtc->state->adjusted_mode;
225962306a36Sopenharmony_ci	pclk = mode->clock * 1000;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	err = host1x_client_resume(&sor->client);
226262306a36Sopenharmony_ci	if (err < 0) {
226362306a36Sopenharmony_ci		dev_err(sor->dev, "failed to resume: %d\n", err);
226462306a36Sopenharmony_ci		return;
226562306a36Sopenharmony_ci	}
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	/* switch to safe parent clock */
226862306a36Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
226962306a36Sopenharmony_ci	if (err < 0) {
227062306a36Sopenharmony_ci		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
227162306a36Sopenharmony_ci		return;
227262306a36Sopenharmony_ci	}
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	div = clk_get_rate(sor->clk) / 1000000 * 4;
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	err = tegra_io_pad_power_enable(sor->pad);
227762306a36Sopenharmony_ci	if (err < 0)
227862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power on I/O pad: %d\n", err);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	usleep_range(20, 100);
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
228362306a36Sopenharmony_ci	value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
228462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	usleep_range(20, 100);
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
228962306a36Sopenharmony_ci	value &= ~SOR_PLL3_PLL_VDD_MODE_3V3;
229062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
229362306a36Sopenharmony_ci	value &= ~SOR_PLL0_VCOPD;
229462306a36Sopenharmony_ci	value &= ~SOR_PLL0_PWR;
229562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
229862306a36Sopenharmony_ci	value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
229962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	usleep_range(200, 400);
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
230462306a36Sopenharmony_ci	value &= ~SOR_PLL2_POWERDOWN_OVERRIDE;
230562306a36Sopenharmony_ci	value &= ~SOR_PLL2_PORT_POWERDOWN;
230662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	usleep_range(20, 100);
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
231162306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
231262306a36Sopenharmony_ci		 SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2;
231362306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	while (true) {
231662306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
231762306a36Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_STATE_BUSY) == 0)
231862306a36Sopenharmony_ci			break;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci		usleep_range(250, 1000);
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
232462306a36Sopenharmony_ci		SOR_LANE_SEQ_CTL_POWER_STATE_UP | SOR_LANE_SEQ_CTL_DELAY(5);
232562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	while (true) {
232862306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
232962306a36Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
233062306a36Sopenharmony_ci			break;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci		usleep_range(250, 1000);
233362306a36Sopenharmony_ci	}
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
233662306a36Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
233762306a36Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	if (mode->clock < 340000) {
234062306a36Sopenharmony_ci		DRM_DEBUG_KMS("setting 2.7 GHz link speed\n");
234162306a36Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
234262306a36Sopenharmony_ci	} else {
234362306a36Sopenharmony_ci		DRM_DEBUG_KMS("setting 5.4 GHz link speed\n");
234462306a36Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40;
234562306a36Sopenharmony_ci	}
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK;
234862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	/* SOR pad PLL stabilization time */
235162306a36Sopenharmony_ci	usleep_range(250, 1000);
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
235462306a36Sopenharmony_ci	value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
235562306a36Sopenharmony_ci	value |= SOR_DP_LINKCTL_LANE_COUNT(4);
235662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
235962306a36Sopenharmony_ci	value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE;
236062306a36Sopenharmony_ci	value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
236162306a36Sopenharmony_ci	value &= ~SOR_DP_SPARE_SEQ_ENABLE;
236262306a36Sopenharmony_ci	value &= ~SOR_DP_SPARE_MACRO_SOR_CLK;
236362306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	value = SOR_SEQ_CTL_PU_PC(0) | SOR_SEQ_CTL_PU_PC_ALT(0) |
236662306a36Sopenharmony_ci		SOR_SEQ_CTL_PD_PC(8) | SOR_SEQ_CTL_PD_PC_ALT(8);
236762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SEQ_CTL);
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	value = SOR_SEQ_INST_DRIVE_PWM_OUT_LO | SOR_SEQ_INST_HALT |
237062306a36Sopenharmony_ci		SOR_SEQ_INST_WAIT_VSYNC | SOR_SEQ_INST_WAIT(1);
237162306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SEQ_INST(0));
237262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SEQ_INST(8));
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	if (!sor->soc->has_nvdisplay) {
237562306a36Sopenharmony_ci		/* program the reference clock */
237662306a36Sopenharmony_ci		value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div);
237762306a36Sopenharmony_ci		tegra_sor_writel(sor, value, SOR_REFCLK);
237862306a36Sopenharmony_ci	}
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	/* XXX not in TRM */
238162306a36Sopenharmony_ci	for (value = 0, i = 0; i < 5; i++)
238262306a36Sopenharmony_ci		value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) |
238362306a36Sopenharmony_ci			 SOR_XBAR_CTRL_LINK1_XSEL(i, i);
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
238662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	/*
238962306a36Sopenharmony_ci	 * Switch the pad clock to the DP clock. Note that we cannot actually
239062306a36Sopenharmony_ci	 * do this because Tegra186 and later don't support clk_set_parent()
239162306a36Sopenharmony_ci	 * on the sorX_pad_clkout clocks. We already do the equivalent above
239262306a36Sopenharmony_ci	 * using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register.
239362306a36Sopenharmony_ci	 */
239462306a36Sopenharmony_ci#if 0
239562306a36Sopenharmony_ci	err = clk_set_parent(sor->clk_pad, sor->clk_dp);
239662306a36Sopenharmony_ci	if (err < 0) {
239762306a36Sopenharmony_ci		dev_err(sor->dev, "failed to select pad parent clock: %d\n",
239862306a36Sopenharmony_ci			err);
239962306a36Sopenharmony_ci		return;
240062306a36Sopenharmony_ci	}
240162306a36Sopenharmony_ci#endif
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	/* switch the SOR clock to the pad clock */
240462306a36Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_pad);
240562306a36Sopenharmony_ci	if (err < 0) {
240662306a36Sopenharmony_ci		dev_err(sor->dev, "failed to select SOR parent clock: %d\n",
240762306a36Sopenharmony_ci			err);
240862306a36Sopenharmony_ci		return;
240962306a36Sopenharmony_ci	}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	/* switch the output clock to the parent pixel clock */
241262306a36Sopenharmony_ci	err = clk_set_parent(sor->clk, sor->clk_parent);
241362306a36Sopenharmony_ci	if (err < 0) {
241462306a36Sopenharmony_ci		dev_err(sor->dev, "failed to select output parent clock: %d\n",
241562306a36Sopenharmony_ci			err);
241662306a36Sopenharmony_ci		return;
241762306a36Sopenharmony_ci	}
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	/* adjust clock rate for HDMI 2.0 modes */
242062306a36Sopenharmony_ci	rate = clk_get_rate(sor->clk_parent);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	if (mode->clock >= 340000)
242362306a36Sopenharmony_ci		rate /= 2;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	DRM_DEBUG_KMS("setting clock to %lu Hz, mode: %lu Hz\n", rate, pclk);
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	clk_set_rate(sor->clk, rate);
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_ci	if (!sor->soc->has_nvdisplay) {
243062306a36Sopenharmony_ci		value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe);
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci		/* XXX is this the proper check? */
243362306a36Sopenharmony_ci		if (mode->clock < 75000)
243462306a36Sopenharmony_ci			value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci		tegra_sor_writel(sor, value, SOR_INPUT_CONTROL);
243762306a36Sopenharmony_ci	}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	max_ac = ((mode->htotal - mode->hdisplay) - SOR_REKEY - 18) / 32;
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	value = SOR_HDMI_CTRL_ENABLE | SOR_HDMI_CTRL_MAX_AC_PACKET(max_ac) |
244262306a36Sopenharmony_ci		SOR_HDMI_CTRL_AUDIO_LAYOUT | SOR_HDMI_CTRL_REKEY(SOR_REKEY);
244362306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_CTRL);
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
244662306a36Sopenharmony_ci		/* H_PULSE2 setup */
244762306a36Sopenharmony_ci		pulse_start = h_ref_to_sync +
244862306a36Sopenharmony_ci			      (mode->hsync_end - mode->hsync_start) +
244962306a36Sopenharmony_ci			      (mode->htotal - mode->hsync_end) - 10;
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci		value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE |
245262306a36Sopenharmony_ci			PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL;
245362306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL);
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci		value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start);
245662306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A);
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci		value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0);
245962306a36Sopenharmony_ci		value |= H_PULSE2_ENABLE;
246062306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0);
246162306a36Sopenharmony_ci	}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	/* infoframe setup */
246462306a36Sopenharmony_ci	err = tegra_sor_hdmi_setup_avi_infoframe(sor, mode);
246562306a36Sopenharmony_ci	if (err < 0)
246662306a36Sopenharmony_ci		dev_err(sor->dev, "failed to setup AVI infoframe: %d\n", err);
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	/* XXX HDMI audio support not implemented yet */
246962306a36Sopenharmony_ci	tegra_sor_hdmi_disable_audio_infoframe(sor);
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	/* use single TMDS protocol */
247262306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
247362306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
247462306a36Sopenharmony_ci	value |= SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A;
247562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	/* power up pad calibration */
247862306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
247962306a36Sopenharmony_ci	value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
248062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	/* production settings */
248362306a36Sopenharmony_ci	settings = tegra_sor_hdmi_find_settings(sor, mode->clock * 1000);
248462306a36Sopenharmony_ci	if (!settings) {
248562306a36Sopenharmony_ci		dev_err(sor->dev, "no settings for pixel clock %d Hz\n",
248662306a36Sopenharmony_ci			mode->clock * 1000);
248762306a36Sopenharmony_ci		return;
248862306a36Sopenharmony_ci	}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
249162306a36Sopenharmony_ci	value &= ~SOR_PLL0_ICHPMP_MASK;
249262306a36Sopenharmony_ci	value &= ~SOR_PLL0_FILTER_MASK;
249362306a36Sopenharmony_ci	value &= ~SOR_PLL0_VCOCAP_MASK;
249462306a36Sopenharmony_ci	value |= SOR_PLL0_ICHPMP(settings->ichpmp);
249562306a36Sopenharmony_ci	value |= SOR_PLL0_FILTER(settings->filter);
249662306a36Sopenharmony_ci	value |= SOR_PLL0_VCOCAP(settings->vcocap);
249762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	/* XXX not in TRM */
250062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
250162306a36Sopenharmony_ci	value &= ~SOR_PLL1_LOADADJ_MASK;
250262306a36Sopenharmony_ci	value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
250362306a36Sopenharmony_ci	value |= SOR_PLL1_LOADADJ(settings->loadadj);
250462306a36Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERMADJ(settings->tmds_termadj);
250562306a36Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERM;
250662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
250962306a36Sopenharmony_ci	value &= ~SOR_PLL3_BG_TEMP_COEF_MASK;
251062306a36Sopenharmony_ci	value &= ~SOR_PLL3_BG_VREF_LEVEL_MASK;
251162306a36Sopenharmony_ci	value &= ~SOR_PLL3_AVDD10_LEVEL_MASK;
251262306a36Sopenharmony_ci	value &= ~SOR_PLL3_AVDD14_LEVEL_MASK;
251362306a36Sopenharmony_ci	value |= SOR_PLL3_BG_TEMP_COEF(settings->bg_temp_coef);
251462306a36Sopenharmony_ci	value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref_level);
251562306a36Sopenharmony_ci	value |= SOR_PLL3_AVDD10_LEVEL(settings->avdd10_level);
251662306a36Sopenharmony_ci	value |= SOR_PLL3_AVDD14_LEVEL(settings->avdd14_level);
251762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	value = settings->drive_current[3] << 24 |
252062306a36Sopenharmony_ci		settings->drive_current[2] << 16 |
252162306a36Sopenharmony_ci		settings->drive_current[1] <<  8 |
252262306a36Sopenharmony_ci		settings->drive_current[0] <<  0;
252362306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT0);
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	value = settings->preemphasis[3] << 24 |
252662306a36Sopenharmony_ci		settings->preemphasis[2] << 16 |
252762306a36Sopenharmony_ci		settings->preemphasis[1] <<  8 |
252862306a36Sopenharmony_ci		settings->preemphasis[0] <<  0;
252962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS0);
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
253262306a36Sopenharmony_ci	value &= ~SOR_DP_PADCTL_TX_PU_MASK;
253362306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU_ENABLE;
253462306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu_value);
253562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl2);
253862306a36Sopenharmony_ci	value &= ~SOR_DP_PADCTL_SPAREPLL_MASK;
253962306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_SPAREPLL(settings->sparepll);
254062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl2);
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	/* power down pad calibration */
254362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
254462306a36Sopenharmony_ci	value |= SOR_DP_PADCTL_PAD_CAL_PD;
254562306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
254862306a36Sopenharmony_ci		/* miscellaneous display controller settings */
254962306a36Sopenharmony_ci		value = VSYNC_H_POSITION(1);
255062306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS);
255162306a36Sopenharmony_ci	}
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
255462306a36Sopenharmony_ci	value &= ~DITHER_CONTROL_MASK;
255562306a36Sopenharmony_ci	value &= ~BASE_COLOR_SIZE_MASK;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	switch (state->bpc) {
255862306a36Sopenharmony_ci	case 6:
255962306a36Sopenharmony_ci		value |= BASE_COLOR_SIZE_666;
256062306a36Sopenharmony_ci		break;
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci	case 8:
256362306a36Sopenharmony_ci		value |= BASE_COLOR_SIZE_888;
256462306a36Sopenharmony_ci		break;
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	case 10:
256762306a36Sopenharmony_ci		value |= BASE_COLOR_SIZE_101010;
256862306a36Sopenharmony_ci		break;
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	case 12:
257162306a36Sopenharmony_ci		value |= BASE_COLOR_SIZE_121212;
257262306a36Sopenharmony_ci		break;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	default:
257562306a36Sopenharmony_ci		WARN(1, "%u bits-per-color not supported\n", state->bpc);
257662306a36Sopenharmony_ci		value |= BASE_COLOR_SIZE_888;
257762306a36Sopenharmony_ci		break;
257862306a36Sopenharmony_ci	}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL);
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	/* XXX set display head owner */
258362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
258462306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_OWNER_MASK;
258562306a36Sopenharmony_ci	value |= SOR_STATE_ASY_OWNER(1 + dc->pipe);
258662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	err = tegra_sor_power_up(sor, 250);
258962306a36Sopenharmony_ci	if (err < 0)
259062306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power up SOR: %d\n", err);
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	/* configure dynamic range of output */
259362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe);
259462306a36Sopenharmony_ci	value &= ~SOR_HEAD_STATE_RANGECOMPRESS_MASK;
259562306a36Sopenharmony_ci	value &= ~SOR_HEAD_STATE_DYNRANGE_MASK;
259662306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe);
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	/* configure colorspace */
259962306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe);
260062306a36Sopenharmony_ci	value &= ~SOR_HEAD_STATE_COLORSPACE_MASK;
260162306a36Sopenharmony_ci	value |= SOR_HEAD_STATE_COLORSPACE_RGB;
260262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe);
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_ci	tegra_sor_mode_set(sor, mode, state);
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	tegra_sor_update(sor);
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	/* program preamble timing in SOR (XXX) */
260962306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
261062306a36Sopenharmony_ci	value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE;
261162306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	err = tegra_sor_attach(sor);
261462306a36Sopenharmony_ci	if (err < 0)
261562306a36Sopenharmony_ci		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	/* enable display to SOR clock and generate HDMI preamble */
261862306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	if (!sor->soc->has_nvdisplay)
262162306a36Sopenharmony_ci		value |= SOR1_TIMING_CYA;
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	value |= SOR_ENABLE(sor->index);
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay) {
262862306a36Sopenharmony_ci		value = tegra_dc_readl(dc, DC_DISP_CORE_SOR_SET_CONTROL(sor->index));
262962306a36Sopenharmony_ci		value &= ~PROTOCOL_MASK;
263062306a36Sopenharmony_ci		value |= PROTOCOL_SINGLE_TMDS_A;
263162306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_CORE_SOR_SET_CONTROL(sor->index));
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	tegra_dc_commit(dc);
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	err = tegra_sor_wakeup(sor);
263762306a36Sopenharmony_ci	if (err < 0)
263862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to wakeup SOR: %d\n", err);
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	tegra_sor_hdmi_scdc_start(sor);
264162306a36Sopenharmony_ci	tegra_sor_audio_prepare(sor);
264262306a36Sopenharmony_ci}
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs tegra_sor_hdmi_helpers = {
264562306a36Sopenharmony_ci	.disable = tegra_sor_hdmi_disable,
264662306a36Sopenharmony_ci	.enable = tegra_sor_hdmi_enable,
264762306a36Sopenharmony_ci	.atomic_check = tegra_sor_encoder_atomic_check,
264862306a36Sopenharmony_ci};
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_cistatic void tegra_sor_dp_disable(struct drm_encoder *encoder)
265162306a36Sopenharmony_ci{
265262306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
265362306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
265462306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
265562306a36Sopenharmony_ci	u32 value;
265662306a36Sopenharmony_ci	int err;
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	if (output->panel)
265962306a36Sopenharmony_ci		drm_panel_disable(output->panel);
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	/*
266262306a36Sopenharmony_ci	 * Do not attempt to power down a DP link if we're not connected since
266362306a36Sopenharmony_ci	 * the AUX transactions would just be timing out.
266462306a36Sopenharmony_ci	 */
266562306a36Sopenharmony_ci	if (output->connector.status != connector_status_disconnected) {
266662306a36Sopenharmony_ci		err = drm_dp_link_power_down(sor->aux, &sor->link);
266762306a36Sopenharmony_ci		if (err < 0)
266862306a36Sopenharmony_ci			dev_err(sor->dev, "failed to power down link: %d\n",
266962306a36Sopenharmony_ci				err);
267062306a36Sopenharmony_ci	}
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	err = tegra_sor_detach(sor);
267362306a36Sopenharmony_ci	if (err < 0)
267462306a36Sopenharmony_ci		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE1);
267762306a36Sopenharmony_ci	tegra_sor_update(sor);
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
268062306a36Sopenharmony_ci	value &= ~SOR_ENABLE(sor->index);
268162306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
268262306a36Sopenharmony_ci	tegra_dc_commit(dc);
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
268562306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
268662306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_SUBOWNER_MASK;
268762306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_OWNER_MASK;
268862306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
268962306a36Sopenharmony_ci	tegra_sor_update(sor);
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	/* switch to safe parent clock */
269262306a36Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
269362306a36Sopenharmony_ci	if (err < 0)
269462306a36Sopenharmony_ci		dev_err(sor->dev, "failed to set safe clock: %d\n", err);
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	err = tegra_sor_power_down(sor);
269762306a36Sopenharmony_ci	if (err < 0)
269862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power down SOR: %d\n", err);
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci	err = tegra_io_pad_power_disable(sor->pad);
270162306a36Sopenharmony_ci	if (err < 0)
270262306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	err = drm_dp_aux_disable(sor->aux);
270562306a36Sopenharmony_ci	if (err < 0)
270662306a36Sopenharmony_ci		dev_err(sor->dev, "failed disable DPAUX: %d\n", err);
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci	if (output->panel)
270962306a36Sopenharmony_ci		drm_panel_unprepare(output->panel);
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	host1x_client_suspend(&sor->client);
271262306a36Sopenharmony_ci}
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_cistatic void tegra_sor_dp_enable(struct drm_encoder *encoder)
271562306a36Sopenharmony_ci{
271662306a36Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
271762306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
271862306a36Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
271962306a36Sopenharmony_ci	struct tegra_sor_config config;
272062306a36Sopenharmony_ci	struct tegra_sor_state *state;
272162306a36Sopenharmony_ci	struct drm_display_mode *mode;
272262306a36Sopenharmony_ci	struct drm_display_info *info;
272362306a36Sopenharmony_ci	unsigned int i;
272462306a36Sopenharmony_ci	u32 value;
272562306a36Sopenharmony_ci	int err;
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	state = to_sor_state(output->connector.state);
272862306a36Sopenharmony_ci	mode = &encoder->crtc->state->adjusted_mode;
272962306a36Sopenharmony_ci	info = &output->connector.display_info;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	err = host1x_client_resume(&sor->client);
273262306a36Sopenharmony_ci	if (err < 0) {
273362306a36Sopenharmony_ci		dev_err(sor->dev, "failed to resume: %d\n", err);
273462306a36Sopenharmony_ci		return;
273562306a36Sopenharmony_ci	}
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	/* switch to safe parent clock */
273862306a36Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
273962306a36Sopenharmony_ci	if (err < 0)
274062306a36Sopenharmony_ci		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	err = tegra_io_pad_power_enable(sor->pad);
274362306a36Sopenharmony_ci	if (err < 0)
274462306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power on LVDS rail: %d\n", err);
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	usleep_range(20, 100);
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	err = drm_dp_aux_enable(sor->aux);
274962306a36Sopenharmony_ci	if (err < 0)
275062306a36Sopenharmony_ci		dev_err(sor->dev, "failed to enable DPAUX: %d\n", err);
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	err = drm_dp_link_probe(sor->aux, &sor->link);
275362306a36Sopenharmony_ci	if (err < 0)
275462306a36Sopenharmony_ci		dev_err(sor->dev, "failed to probe DP link: %d\n", err);
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	tegra_sor_filter_rates(sor);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	err = drm_dp_link_choose(&sor->link, mode, info);
275962306a36Sopenharmony_ci	if (err < 0)
276062306a36Sopenharmony_ci		dev_err(sor->dev, "failed to choose link: %d\n", err);
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	if (output->panel)
276362306a36Sopenharmony_ci		drm_panel_prepare(output->panel);
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
276662306a36Sopenharmony_ci	value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
276762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	usleep_range(20, 40);
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
277262306a36Sopenharmony_ci	value |= SOR_PLL3_PLL_VDD_MODE_3V3;
277362306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
277662306a36Sopenharmony_ci	value &= ~(SOR_PLL0_VCOPD | SOR_PLL0_PWR);
277762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
278062306a36Sopenharmony_ci	value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
278162306a36Sopenharmony_ci	value |= SOR_PLL2_SEQ_PLLCAPPD;
278262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	usleep_range(200, 400);
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
278762306a36Sopenharmony_ci	value &= ~SOR_PLL2_POWERDOWN_OVERRIDE;
278862306a36Sopenharmony_ci	value &= ~SOR_PLL2_PORT_POWERDOWN;
278962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
279262306a36Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	if (output->panel)
279562306a36Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
279662306a36Sopenharmony_ci	else
279762306a36Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK;
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	usleep_range(200, 400);
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
280462306a36Sopenharmony_ci	/* XXX not in TRM */
280562306a36Sopenharmony_ci	if (output->panel)
280662306a36Sopenharmony_ci		value |= SOR_DP_SPARE_PANEL_INTERNAL;
280762306a36Sopenharmony_ci	else
280862306a36Sopenharmony_ci		value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	value |= SOR_DP_SPARE_SEQ_ENABLE;
281162306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	/* XXX not in TRM */
281462306a36Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_LVDS);
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
281762306a36Sopenharmony_ci	value &= ~SOR_PLL0_ICHPMP_MASK;
281862306a36Sopenharmony_ci	value &= ~SOR_PLL0_VCOCAP_MASK;
281962306a36Sopenharmony_ci	value |= SOR_PLL0_ICHPMP(0x1);
282062306a36Sopenharmony_ci	value |= SOR_PLL0_VCOCAP(0x3);
282162306a36Sopenharmony_ci	value |= SOR_PLL0_RESISTOR_EXT;
282262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	/* XXX not in TRM */
282562306a36Sopenharmony_ci	for (value = 0, i = 0; i < 5; i++)
282662306a36Sopenharmony_ci		value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) |
282762306a36Sopenharmony_ci			 SOR_XBAR_CTRL_LINK1_XSEL(i, i);
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
283062306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	/*
283362306a36Sopenharmony_ci	 * Switch the pad clock to the DP clock. Note that we cannot actually
283462306a36Sopenharmony_ci	 * do this because Tegra186 and later don't support clk_set_parent()
283562306a36Sopenharmony_ci	 * on the sorX_pad_clkout clocks. We already do the equivalent above
283662306a36Sopenharmony_ci	 * using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register.
283762306a36Sopenharmony_ci	 */
283862306a36Sopenharmony_ci#if 0
283962306a36Sopenharmony_ci	err = clk_set_parent(sor->clk_pad, sor->clk_parent);
284062306a36Sopenharmony_ci	if (err < 0) {
284162306a36Sopenharmony_ci		dev_err(sor->dev, "failed to select pad parent clock: %d\n",
284262306a36Sopenharmony_ci			err);
284362306a36Sopenharmony_ci		return;
284462306a36Sopenharmony_ci	}
284562306a36Sopenharmony_ci#endif
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	/* switch the SOR clock to the pad clock */
284862306a36Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_pad);
284962306a36Sopenharmony_ci	if (err < 0) {
285062306a36Sopenharmony_ci		dev_err(sor->dev, "failed to select SOR parent clock: %d\n",
285162306a36Sopenharmony_ci			err);
285262306a36Sopenharmony_ci		return;
285362306a36Sopenharmony_ci	}
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci	/* switch the output clock to the parent pixel clock */
285662306a36Sopenharmony_ci	err = clk_set_parent(sor->clk, sor->clk_parent);
285762306a36Sopenharmony_ci	if (err < 0) {
285862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to select output parent clock: %d\n",
285962306a36Sopenharmony_ci			err);
286062306a36Sopenharmony_ci		return;
286162306a36Sopenharmony_ci	}
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci	/* use DP-A protocol */
286462306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
286562306a36Sopenharmony_ci	value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
286662306a36Sopenharmony_ci	value |= SOR_STATE_ASY_PROTOCOL_DP_A;
286762306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	/* enable port */
287062306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
287162306a36Sopenharmony_ci	value |= SOR_DP_LINKCTL_ENABLE;
287262306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	tegra_sor_dp_term_calibrate(sor);
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	err = drm_dp_link_train(&sor->link);
287762306a36Sopenharmony_ci	if (err < 0)
287862306a36Sopenharmony_ci		dev_err(sor->dev, "link training failed: %d\n", err);
287962306a36Sopenharmony_ci	else
288062306a36Sopenharmony_ci		dev_dbg(sor->dev, "link training succeeded\n");
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	err = drm_dp_link_power_up(sor->aux, &sor->link);
288362306a36Sopenharmony_ci	if (err < 0)
288462306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power up DP link: %d\n", err);
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci	/* compute configuration */
288762306a36Sopenharmony_ci	memset(&config, 0, sizeof(config));
288862306a36Sopenharmony_ci	config.bits_per_pixel = state->bpc * 3;
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	err = tegra_sor_compute_config(sor, mode, &config, &sor->link);
289162306a36Sopenharmony_ci	if (err < 0)
289262306a36Sopenharmony_ci		dev_err(sor->dev, "failed to compute configuration: %d\n", err);
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	tegra_sor_apply_config(sor, &config);
289562306a36Sopenharmony_ci	tegra_sor_mode_set(sor, mode, state);
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	if (output->panel) {
289862306a36Sopenharmony_ci		/* CSTM (LVDS, link A/B, upper) */
289962306a36Sopenharmony_ci		value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
290062306a36Sopenharmony_ci			SOR_CSTM_UPPER;
290162306a36Sopenharmony_ci		tegra_sor_writel(sor, value, SOR_CSTM);
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci		/* PWM setup */
290462306a36Sopenharmony_ci		err = tegra_sor_setup_pwm(sor, 250);
290562306a36Sopenharmony_ci		if (err < 0)
290662306a36Sopenharmony_ci			dev_err(sor->dev, "failed to setup PWM: %d\n", err);
290762306a36Sopenharmony_ci	}
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci	tegra_sor_update(sor);
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	err = tegra_sor_power_up(sor, 250);
291262306a36Sopenharmony_ci	if (err < 0)
291362306a36Sopenharmony_ci		dev_err(sor->dev, "failed to power up SOR: %d\n", err);
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	/* attach and wake up */
291662306a36Sopenharmony_ci	err = tegra_sor_attach(sor);
291762306a36Sopenharmony_ci	if (err < 0)
291862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
292162306a36Sopenharmony_ci	value |= SOR_ENABLE(sor->index);
292262306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	tegra_dc_commit(dc);
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	err = tegra_sor_wakeup(sor);
292762306a36Sopenharmony_ci	if (err < 0)
292862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to wakeup SOR: %d\n", err);
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	if (output->panel)
293162306a36Sopenharmony_ci		drm_panel_enable(output->panel);
293262306a36Sopenharmony_ci}
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = {
293562306a36Sopenharmony_ci	.disable = tegra_sor_dp_disable,
293662306a36Sopenharmony_ci	.enable = tegra_sor_dp_enable,
293762306a36Sopenharmony_ci	.atomic_check = tegra_sor_encoder_atomic_check,
293862306a36Sopenharmony_ci};
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_cistatic void tegra_sor_disable_regulator(void *data)
294162306a36Sopenharmony_ci{
294262306a36Sopenharmony_ci	struct regulator *reg = data;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	regulator_disable(reg);
294562306a36Sopenharmony_ci}
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_cistatic int tegra_sor_enable_regulator(struct tegra_sor *sor, struct regulator *reg)
294862306a36Sopenharmony_ci{
294962306a36Sopenharmony_ci	int err;
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	err = regulator_enable(reg);
295262306a36Sopenharmony_ci	if (err)
295362306a36Sopenharmony_ci		return err;
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci	return devm_add_action_or_reset(sor->dev, tegra_sor_disable_regulator, reg);
295662306a36Sopenharmony_ci}
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_cistatic int tegra_sor_hdmi_probe(struct tegra_sor *sor)
295962306a36Sopenharmony_ci{
296062306a36Sopenharmony_ci	int err;
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci	sor->avdd_io_supply = devm_regulator_get(sor->dev, "avdd-io-hdmi-dp");
296362306a36Sopenharmony_ci	if (IS_ERR(sor->avdd_io_supply))
296462306a36Sopenharmony_ci		return dev_err_probe(sor->dev, PTR_ERR(sor->avdd_io_supply),
296562306a36Sopenharmony_ci				     "cannot get AVDD I/O supply\n");
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply);
296862306a36Sopenharmony_ci	if (err < 0) {
296962306a36Sopenharmony_ci		dev_err(sor->dev, "failed to enable AVDD I/O supply: %d\n",
297062306a36Sopenharmony_ci			err);
297162306a36Sopenharmony_ci		return err;
297262306a36Sopenharmony_ci	}
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	sor->vdd_pll_supply = devm_regulator_get(sor->dev, "vdd-hdmi-dp-pll");
297562306a36Sopenharmony_ci	if (IS_ERR(sor->vdd_pll_supply))
297662306a36Sopenharmony_ci		return dev_err_probe(sor->dev, PTR_ERR(sor->vdd_pll_supply),
297762306a36Sopenharmony_ci				     "cannot get VDD PLL supply\n");
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply);
298062306a36Sopenharmony_ci	if (err < 0) {
298162306a36Sopenharmony_ci		dev_err(sor->dev, "failed to enable VDD PLL supply: %d\n",
298262306a36Sopenharmony_ci			err);
298362306a36Sopenharmony_ci		return err;
298462306a36Sopenharmony_ci	}
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	sor->hdmi_supply = devm_regulator_get(sor->dev, "hdmi");
298762306a36Sopenharmony_ci	if (IS_ERR(sor->hdmi_supply))
298862306a36Sopenharmony_ci		return dev_err_probe(sor->dev, PTR_ERR(sor->hdmi_supply),
298962306a36Sopenharmony_ci				     "cannot get HDMI supply\n");
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->hdmi_supply);
299262306a36Sopenharmony_ci	if (err < 0) {
299362306a36Sopenharmony_ci		dev_err(sor->dev, "failed to enable HDMI supply: %d\n", err);
299462306a36Sopenharmony_ci		return err;
299562306a36Sopenharmony_ci	}
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	INIT_DELAYED_WORK(&sor->scdc, tegra_sor_hdmi_scdc_work);
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	return 0;
300062306a36Sopenharmony_ci}
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_cistatic const struct tegra_sor_ops tegra_sor_hdmi_ops = {
300362306a36Sopenharmony_ci	.name = "HDMI",
300462306a36Sopenharmony_ci	.probe = tegra_sor_hdmi_probe,
300562306a36Sopenharmony_ci	.audio_enable = tegra_sor_hdmi_audio_enable,
300662306a36Sopenharmony_ci	.audio_disable = tegra_sor_hdmi_audio_disable,
300762306a36Sopenharmony_ci};
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_cistatic int tegra_sor_dp_probe(struct tegra_sor *sor)
301062306a36Sopenharmony_ci{
301162306a36Sopenharmony_ci	int err;
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci	sor->avdd_io_supply = devm_regulator_get(sor->dev, "avdd-io-hdmi-dp");
301462306a36Sopenharmony_ci	if (IS_ERR(sor->avdd_io_supply))
301562306a36Sopenharmony_ci		return PTR_ERR(sor->avdd_io_supply);
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply);
301862306a36Sopenharmony_ci	if (err < 0)
301962306a36Sopenharmony_ci		return err;
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_ci	sor->vdd_pll_supply = devm_regulator_get(sor->dev, "vdd-hdmi-dp-pll");
302262306a36Sopenharmony_ci	if (IS_ERR(sor->vdd_pll_supply))
302362306a36Sopenharmony_ci		return PTR_ERR(sor->vdd_pll_supply);
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply);
302662306a36Sopenharmony_ci	if (err < 0)
302762306a36Sopenharmony_ci		return err;
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci	return 0;
303062306a36Sopenharmony_ci}
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_cistatic const struct tegra_sor_ops tegra_sor_dp_ops = {
303362306a36Sopenharmony_ci	.name = "DP",
303462306a36Sopenharmony_ci	.probe = tegra_sor_dp_probe,
303562306a36Sopenharmony_ci};
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_cistatic int tegra_sor_init(struct host1x_client *client)
303862306a36Sopenharmony_ci{
303962306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
304062306a36Sopenharmony_ci	const struct drm_encoder_helper_funcs *helpers = NULL;
304162306a36Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
304262306a36Sopenharmony_ci	int connector = DRM_MODE_CONNECTOR_Unknown;
304362306a36Sopenharmony_ci	int encoder = DRM_MODE_ENCODER_NONE;
304462306a36Sopenharmony_ci	int err;
304562306a36Sopenharmony_ci
304662306a36Sopenharmony_ci	if (!sor->aux) {
304762306a36Sopenharmony_ci		if (sor->ops == &tegra_sor_hdmi_ops) {
304862306a36Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_HDMIA;
304962306a36Sopenharmony_ci			encoder = DRM_MODE_ENCODER_TMDS;
305062306a36Sopenharmony_ci			helpers = &tegra_sor_hdmi_helpers;
305162306a36Sopenharmony_ci		} else if (sor->soc->supports_lvds) {
305262306a36Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_LVDS;
305362306a36Sopenharmony_ci			encoder = DRM_MODE_ENCODER_LVDS;
305462306a36Sopenharmony_ci		}
305562306a36Sopenharmony_ci	} else {
305662306a36Sopenharmony_ci		if (sor->output.panel) {
305762306a36Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_eDP;
305862306a36Sopenharmony_ci			encoder = DRM_MODE_ENCODER_TMDS;
305962306a36Sopenharmony_ci			helpers = &tegra_sor_dp_helpers;
306062306a36Sopenharmony_ci		} else {
306162306a36Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_DisplayPort;
306262306a36Sopenharmony_ci			encoder = DRM_MODE_ENCODER_TMDS;
306362306a36Sopenharmony_ci			helpers = &tegra_sor_dp_helpers;
306462306a36Sopenharmony_ci		}
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci		sor->link.ops = &tegra_sor_dp_link_ops;
306762306a36Sopenharmony_ci		sor->link.aux = sor->aux;
306862306a36Sopenharmony_ci	}
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_ci	sor->output.dev = sor->dev;
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci	drm_connector_init_with_ddc(drm, &sor->output.connector,
307362306a36Sopenharmony_ci				    &tegra_sor_connector_funcs,
307462306a36Sopenharmony_ci				    connector,
307562306a36Sopenharmony_ci				    sor->output.ddc);
307662306a36Sopenharmony_ci	drm_connector_helper_add(&sor->output.connector,
307762306a36Sopenharmony_ci				 &tegra_sor_connector_helper_funcs);
307862306a36Sopenharmony_ci	sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	drm_simple_encoder_init(drm, &sor->output.encoder, encoder);
308162306a36Sopenharmony_ci	drm_encoder_helper_add(&sor->output.encoder, helpers);
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci	drm_connector_attach_encoder(&sor->output.connector,
308462306a36Sopenharmony_ci					  &sor->output.encoder);
308562306a36Sopenharmony_ci	drm_connector_register(&sor->output.connector);
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci	err = tegra_output_init(drm, &sor->output);
308862306a36Sopenharmony_ci	if (err < 0) {
308962306a36Sopenharmony_ci		dev_err(client->dev, "failed to initialize output: %d\n", err);
309062306a36Sopenharmony_ci		return err;
309162306a36Sopenharmony_ci	}
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	tegra_output_find_possible_crtcs(&sor->output, drm);
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	if (sor->aux) {
309662306a36Sopenharmony_ci		err = drm_dp_aux_attach(sor->aux, &sor->output);
309762306a36Sopenharmony_ci		if (err < 0) {
309862306a36Sopenharmony_ci			dev_err(sor->dev, "failed to attach DP: %d\n", err);
309962306a36Sopenharmony_ci			return err;
310062306a36Sopenharmony_ci		}
310162306a36Sopenharmony_ci	}
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci	/*
310462306a36Sopenharmony_ci	 * XXX: Remove this reset once proper hand-over from firmware to
310562306a36Sopenharmony_ci	 * kernel is possible.
310662306a36Sopenharmony_ci	 */
310762306a36Sopenharmony_ci	if (sor->rst) {
310862306a36Sopenharmony_ci		err = pm_runtime_resume_and_get(sor->dev);
310962306a36Sopenharmony_ci		if (err < 0) {
311062306a36Sopenharmony_ci			dev_err(sor->dev, "failed to get runtime PM: %d\n", err);
311162306a36Sopenharmony_ci			return err;
311262306a36Sopenharmony_ci		}
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci		err = reset_control_acquire(sor->rst);
311562306a36Sopenharmony_ci		if (err < 0) {
311662306a36Sopenharmony_ci			dev_err(sor->dev, "failed to acquire SOR reset: %d\n",
311762306a36Sopenharmony_ci				err);
311862306a36Sopenharmony_ci			goto rpm_put;
311962306a36Sopenharmony_ci		}
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci		err = reset_control_assert(sor->rst);
312262306a36Sopenharmony_ci		if (err < 0) {
312362306a36Sopenharmony_ci			dev_err(sor->dev, "failed to assert SOR reset: %d\n",
312462306a36Sopenharmony_ci				err);
312562306a36Sopenharmony_ci			goto rpm_put;
312662306a36Sopenharmony_ci		}
312762306a36Sopenharmony_ci	}
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci	err = clk_prepare_enable(sor->clk);
313062306a36Sopenharmony_ci	if (err < 0) {
313162306a36Sopenharmony_ci		dev_err(sor->dev, "failed to enable clock: %d\n", err);
313262306a36Sopenharmony_ci		goto rpm_put;
313362306a36Sopenharmony_ci	}
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	usleep_range(1000, 3000);
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci	if (sor->rst) {
313862306a36Sopenharmony_ci		err = reset_control_deassert(sor->rst);
313962306a36Sopenharmony_ci		if (err < 0) {
314062306a36Sopenharmony_ci			dev_err(sor->dev, "failed to deassert SOR reset: %d\n",
314162306a36Sopenharmony_ci				err);
314262306a36Sopenharmony_ci			clk_disable_unprepare(sor->clk);
314362306a36Sopenharmony_ci			goto rpm_put;
314462306a36Sopenharmony_ci		}
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci		reset_control_release(sor->rst);
314762306a36Sopenharmony_ci		pm_runtime_put(sor->dev);
314862306a36Sopenharmony_ci	}
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci	err = clk_prepare_enable(sor->clk_safe);
315162306a36Sopenharmony_ci	if (err < 0) {
315262306a36Sopenharmony_ci		clk_disable_unprepare(sor->clk);
315362306a36Sopenharmony_ci		return err;
315462306a36Sopenharmony_ci	}
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	err = clk_prepare_enable(sor->clk_dp);
315762306a36Sopenharmony_ci	if (err < 0) {
315862306a36Sopenharmony_ci		clk_disable_unprepare(sor->clk_safe);
315962306a36Sopenharmony_ci		clk_disable_unprepare(sor->clk);
316062306a36Sopenharmony_ci		return err;
316162306a36Sopenharmony_ci	}
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	return 0;
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_cirpm_put:
316662306a36Sopenharmony_ci	if (sor->rst)
316762306a36Sopenharmony_ci		pm_runtime_put(sor->dev);
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	return err;
317062306a36Sopenharmony_ci}
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_cistatic int tegra_sor_exit(struct host1x_client *client)
317362306a36Sopenharmony_ci{
317462306a36Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
317562306a36Sopenharmony_ci	int err;
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	tegra_output_exit(&sor->output);
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci	if (sor->aux) {
318062306a36Sopenharmony_ci		err = drm_dp_aux_detach(sor->aux);
318162306a36Sopenharmony_ci		if (err < 0) {
318262306a36Sopenharmony_ci			dev_err(sor->dev, "failed to detach DP: %d\n", err);
318362306a36Sopenharmony_ci			return err;
318462306a36Sopenharmony_ci		}
318562306a36Sopenharmony_ci	}
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	clk_disable_unprepare(sor->clk_safe);
318862306a36Sopenharmony_ci	clk_disable_unprepare(sor->clk_dp);
318962306a36Sopenharmony_ci	clk_disable_unprepare(sor->clk);
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	return 0;
319262306a36Sopenharmony_ci}
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_cistatic int tegra_sor_runtime_suspend(struct host1x_client *client)
319562306a36Sopenharmony_ci{
319662306a36Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
319762306a36Sopenharmony_ci	struct device *dev = client->dev;
319862306a36Sopenharmony_ci	int err;
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci	if (sor->rst) {
320162306a36Sopenharmony_ci		err = reset_control_assert(sor->rst);
320262306a36Sopenharmony_ci		if (err < 0) {
320362306a36Sopenharmony_ci			dev_err(dev, "failed to assert reset: %d\n", err);
320462306a36Sopenharmony_ci			return err;
320562306a36Sopenharmony_ci		}
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci		reset_control_release(sor->rst);
320862306a36Sopenharmony_ci	}
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci	usleep_range(1000, 2000);
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	clk_disable_unprepare(sor->clk);
321362306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci	return 0;
321662306a36Sopenharmony_ci}
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_cistatic int tegra_sor_runtime_resume(struct host1x_client *client)
321962306a36Sopenharmony_ci{
322062306a36Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
322162306a36Sopenharmony_ci	struct device *dev = client->dev;
322262306a36Sopenharmony_ci	int err;
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci	err = pm_runtime_resume_and_get(dev);
322562306a36Sopenharmony_ci	if (err < 0) {
322662306a36Sopenharmony_ci		dev_err(dev, "failed to get runtime PM: %d\n", err);
322762306a36Sopenharmony_ci		return err;
322862306a36Sopenharmony_ci	}
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci	err = clk_prepare_enable(sor->clk);
323162306a36Sopenharmony_ci	if (err < 0) {
323262306a36Sopenharmony_ci		dev_err(dev, "failed to enable clock: %d\n", err);
323362306a36Sopenharmony_ci		goto put_rpm;
323462306a36Sopenharmony_ci	}
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci	usleep_range(1000, 2000);
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	if (sor->rst) {
323962306a36Sopenharmony_ci		err = reset_control_acquire(sor->rst);
324062306a36Sopenharmony_ci		if (err < 0) {
324162306a36Sopenharmony_ci			dev_err(dev, "failed to acquire reset: %d\n", err);
324262306a36Sopenharmony_ci			goto disable_clk;
324362306a36Sopenharmony_ci		}
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci		err = reset_control_deassert(sor->rst);
324662306a36Sopenharmony_ci		if (err < 0) {
324762306a36Sopenharmony_ci			dev_err(dev, "failed to deassert reset: %d\n", err);
324862306a36Sopenharmony_ci			goto release_reset;
324962306a36Sopenharmony_ci		}
325062306a36Sopenharmony_ci	}
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	return 0;
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_cirelease_reset:
325562306a36Sopenharmony_ci	reset_control_release(sor->rst);
325662306a36Sopenharmony_cidisable_clk:
325762306a36Sopenharmony_ci	clk_disable_unprepare(sor->clk);
325862306a36Sopenharmony_ciput_rpm:
325962306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
326062306a36Sopenharmony_ci	return err;
326162306a36Sopenharmony_ci}
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_cistatic const struct host1x_client_ops sor_client_ops = {
326462306a36Sopenharmony_ci	.init = tegra_sor_init,
326562306a36Sopenharmony_ci	.exit = tegra_sor_exit,
326662306a36Sopenharmony_ci	.suspend = tegra_sor_runtime_suspend,
326762306a36Sopenharmony_ci	.resume = tegra_sor_runtime_resume,
326862306a36Sopenharmony_ci};
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_cistatic const u8 tegra124_sor_xbar_cfg[5] = {
327162306a36Sopenharmony_ci	0, 1, 2, 3, 4
327262306a36Sopenharmony_ci};
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_cistatic const struct tegra_sor_regs tegra124_sor_regs = {
327562306a36Sopenharmony_ci	.head_state0 = 0x05,
327662306a36Sopenharmony_ci	.head_state1 = 0x07,
327762306a36Sopenharmony_ci	.head_state2 = 0x09,
327862306a36Sopenharmony_ci	.head_state3 = 0x0b,
327962306a36Sopenharmony_ci	.head_state4 = 0x0d,
328062306a36Sopenharmony_ci	.head_state5 = 0x0f,
328162306a36Sopenharmony_ci	.pll0 = 0x17,
328262306a36Sopenharmony_ci	.pll1 = 0x18,
328362306a36Sopenharmony_ci	.pll2 = 0x19,
328462306a36Sopenharmony_ci	.pll3 = 0x1a,
328562306a36Sopenharmony_ci	.dp_padctl0 = 0x5c,
328662306a36Sopenharmony_ci	.dp_padctl2 = 0x73,
328762306a36Sopenharmony_ci};
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ci/* Tegra124 and Tegra132 have lanes 0 and 2 swapped. */
329062306a36Sopenharmony_cistatic const u8 tegra124_sor_lane_map[4] = {
329162306a36Sopenharmony_ci	2, 1, 0, 3,
329262306a36Sopenharmony_ci};
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_cistatic const u8 tegra124_sor_voltage_swing[4][4][4] = {
329562306a36Sopenharmony_ci	{
329662306a36Sopenharmony_ci		{ 0x13, 0x19, 0x1e, 0x28 },
329762306a36Sopenharmony_ci		{ 0x1e, 0x25, 0x2d, },
329862306a36Sopenharmony_ci		{ 0x28, 0x32, },
329962306a36Sopenharmony_ci		{ 0x3c, },
330062306a36Sopenharmony_ci	}, {
330162306a36Sopenharmony_ci		{ 0x12, 0x17, 0x1b, 0x25 },
330262306a36Sopenharmony_ci		{ 0x1c, 0x23, 0x2a, },
330362306a36Sopenharmony_ci		{ 0x25, 0x2f, },
330462306a36Sopenharmony_ci		{ 0x39, }
330562306a36Sopenharmony_ci	}, {
330662306a36Sopenharmony_ci		{ 0x12, 0x16, 0x1a, 0x22 },
330762306a36Sopenharmony_ci		{ 0x1b, 0x20, 0x27, },
330862306a36Sopenharmony_ci		{ 0x24, 0x2d, },
330962306a36Sopenharmony_ci		{ 0x36, },
331062306a36Sopenharmony_ci	}, {
331162306a36Sopenharmony_ci		{ 0x11, 0x14, 0x17, 0x1f },
331262306a36Sopenharmony_ci		{ 0x19, 0x1e, 0x24, },
331362306a36Sopenharmony_ci		{ 0x22, 0x2a, },
331462306a36Sopenharmony_ci		{ 0x32, },
331562306a36Sopenharmony_ci	},
331662306a36Sopenharmony_ci};
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_cistatic const u8 tegra124_sor_pre_emphasis[4][4][4] = {
331962306a36Sopenharmony_ci	{
332062306a36Sopenharmony_ci		{ 0x00, 0x09, 0x13, 0x25 },
332162306a36Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
332262306a36Sopenharmony_ci		{ 0x00, 0x14, },
332362306a36Sopenharmony_ci		{ 0x00, },
332462306a36Sopenharmony_ci	}, {
332562306a36Sopenharmony_ci		{ 0x00, 0x0a, 0x14, 0x28 },
332662306a36Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
332762306a36Sopenharmony_ci		{ 0x00, 0x14, },
332862306a36Sopenharmony_ci		{ 0x00 },
332962306a36Sopenharmony_ci	}, {
333062306a36Sopenharmony_ci		{ 0x00, 0x0a, 0x14, 0x28 },
333162306a36Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
333262306a36Sopenharmony_ci		{ 0x00, 0x14, },
333362306a36Sopenharmony_ci		{ 0x00, },
333462306a36Sopenharmony_ci	}, {
333562306a36Sopenharmony_ci		{ 0x00, 0x0a, 0x14, 0x28 },
333662306a36Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
333762306a36Sopenharmony_ci		{ 0x00, 0x14, },
333862306a36Sopenharmony_ci		{ 0x00, },
333962306a36Sopenharmony_ci	},
334062306a36Sopenharmony_ci};
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_cistatic const u8 tegra124_sor_post_cursor[4][4][4] = {
334362306a36Sopenharmony_ci	{
334462306a36Sopenharmony_ci		{ 0x00, 0x00, 0x00, 0x00 },
334562306a36Sopenharmony_ci		{ 0x00, 0x00, 0x00, },
334662306a36Sopenharmony_ci		{ 0x00, 0x00, },
334762306a36Sopenharmony_ci		{ 0x00, },
334862306a36Sopenharmony_ci	}, {
334962306a36Sopenharmony_ci		{ 0x02, 0x02, 0x04, 0x05 },
335062306a36Sopenharmony_ci		{ 0x02, 0x04, 0x05, },
335162306a36Sopenharmony_ci		{ 0x04, 0x05, },
335262306a36Sopenharmony_ci		{ 0x05, },
335362306a36Sopenharmony_ci	}, {
335462306a36Sopenharmony_ci		{ 0x04, 0x05, 0x08, 0x0b },
335562306a36Sopenharmony_ci		{ 0x05, 0x09, 0x0b, },
335662306a36Sopenharmony_ci		{ 0x08, 0x0a, },
335762306a36Sopenharmony_ci		{ 0x0b, },
335862306a36Sopenharmony_ci	}, {
335962306a36Sopenharmony_ci		{ 0x05, 0x09, 0x0b, 0x12 },
336062306a36Sopenharmony_ci		{ 0x09, 0x0d, 0x12, },
336162306a36Sopenharmony_ci		{ 0x0b, 0x0f, },
336262306a36Sopenharmony_ci		{ 0x12, },
336362306a36Sopenharmony_ci	},
336462306a36Sopenharmony_ci};
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_cistatic const u8 tegra124_sor_tx_pu[4][4][4] = {
336762306a36Sopenharmony_ci	{
336862306a36Sopenharmony_ci		{ 0x20, 0x30, 0x40, 0x60 },
336962306a36Sopenharmony_ci		{ 0x30, 0x40, 0x60, },
337062306a36Sopenharmony_ci		{ 0x40, 0x60, },
337162306a36Sopenharmony_ci		{ 0x60, },
337262306a36Sopenharmony_ci	}, {
337362306a36Sopenharmony_ci		{ 0x20, 0x20, 0x30, 0x50 },
337462306a36Sopenharmony_ci		{ 0x30, 0x40, 0x50, },
337562306a36Sopenharmony_ci		{ 0x40, 0x50, },
337662306a36Sopenharmony_ci		{ 0x60, },
337762306a36Sopenharmony_ci	}, {
337862306a36Sopenharmony_ci		{ 0x20, 0x20, 0x30, 0x40, },
337962306a36Sopenharmony_ci		{ 0x30, 0x30, 0x40, },
338062306a36Sopenharmony_ci		{ 0x40, 0x50, },
338162306a36Sopenharmony_ci		{ 0x60, },
338262306a36Sopenharmony_ci	}, {
338362306a36Sopenharmony_ci		{ 0x20, 0x20, 0x20, 0x40, },
338462306a36Sopenharmony_ci		{ 0x30, 0x30, 0x40, },
338562306a36Sopenharmony_ci		{ 0x40, 0x40, },
338662306a36Sopenharmony_ci		{ 0x60, },
338762306a36Sopenharmony_ci	},
338862306a36Sopenharmony_ci};
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_cistatic const struct tegra_sor_soc tegra124_sor = {
339162306a36Sopenharmony_ci	.supports_lvds = true,
339262306a36Sopenharmony_ci	.supports_hdmi = false,
339362306a36Sopenharmony_ci	.supports_dp = true,
339462306a36Sopenharmony_ci	.supports_audio = false,
339562306a36Sopenharmony_ci	.supports_hdcp = false,
339662306a36Sopenharmony_ci	.regs = &tegra124_sor_regs,
339762306a36Sopenharmony_ci	.has_nvdisplay = false,
339862306a36Sopenharmony_ci	.xbar_cfg = tegra124_sor_xbar_cfg,
339962306a36Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
340062306a36Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
340162306a36Sopenharmony_ci	.pre_emphasis = tegra124_sor_pre_emphasis,
340262306a36Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
340362306a36Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
340462306a36Sopenharmony_ci};
340562306a36Sopenharmony_ci
340662306a36Sopenharmony_cistatic const u8 tegra132_sor_pre_emphasis[4][4][4] = {
340762306a36Sopenharmony_ci	{
340862306a36Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
340962306a36Sopenharmony_ci		{ 0x01, 0x0e, 0x1d, },
341062306a36Sopenharmony_ci		{ 0x01, 0x13, },
341162306a36Sopenharmony_ci		{ 0x00, },
341262306a36Sopenharmony_ci	}, {
341362306a36Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
341462306a36Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
341562306a36Sopenharmony_ci		{ 0x00, 0x13, },
341662306a36Sopenharmony_ci		{ 0x00 },
341762306a36Sopenharmony_ci	}, {
341862306a36Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
341962306a36Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
342062306a36Sopenharmony_ci		{ 0x00, 0x13, },
342162306a36Sopenharmony_ci		{ 0x00, },
342262306a36Sopenharmony_ci	}, {
342362306a36Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
342462306a36Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
342562306a36Sopenharmony_ci		{ 0x00, 0x13, },
342662306a36Sopenharmony_ci		{ 0x00, },
342762306a36Sopenharmony_ci	},
342862306a36Sopenharmony_ci};
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_cistatic const struct tegra_sor_soc tegra132_sor = {
343162306a36Sopenharmony_ci	.supports_lvds = true,
343262306a36Sopenharmony_ci	.supports_hdmi = false,
343362306a36Sopenharmony_ci	.supports_dp = true,
343462306a36Sopenharmony_ci	.supports_audio = false,
343562306a36Sopenharmony_ci	.supports_hdcp = false,
343662306a36Sopenharmony_ci	.regs = &tegra124_sor_regs,
343762306a36Sopenharmony_ci	.has_nvdisplay = false,
343862306a36Sopenharmony_ci	.xbar_cfg = tegra124_sor_xbar_cfg,
343962306a36Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
344062306a36Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
344162306a36Sopenharmony_ci	.pre_emphasis = tegra132_sor_pre_emphasis,
344262306a36Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
344362306a36Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
344462306a36Sopenharmony_ci};
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_cistatic const struct tegra_sor_regs tegra210_sor_regs = {
344762306a36Sopenharmony_ci	.head_state0 = 0x05,
344862306a36Sopenharmony_ci	.head_state1 = 0x07,
344962306a36Sopenharmony_ci	.head_state2 = 0x09,
345062306a36Sopenharmony_ci	.head_state3 = 0x0b,
345162306a36Sopenharmony_ci	.head_state4 = 0x0d,
345262306a36Sopenharmony_ci	.head_state5 = 0x0f,
345362306a36Sopenharmony_ci	.pll0 = 0x17,
345462306a36Sopenharmony_ci	.pll1 = 0x18,
345562306a36Sopenharmony_ci	.pll2 = 0x19,
345662306a36Sopenharmony_ci	.pll3 = 0x1a,
345762306a36Sopenharmony_ci	.dp_padctl0 = 0x5c,
345862306a36Sopenharmony_ci	.dp_padctl2 = 0x73,
345962306a36Sopenharmony_ci};
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_cistatic const u8 tegra210_sor_xbar_cfg[5] = {
346262306a36Sopenharmony_ci	2, 1, 0, 3, 4
346362306a36Sopenharmony_ci};
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_cistatic const u8 tegra210_sor_lane_map[4] = {
346662306a36Sopenharmony_ci	0, 1, 2, 3,
346762306a36Sopenharmony_ci};
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_cistatic const struct tegra_sor_soc tegra210_sor = {
347062306a36Sopenharmony_ci	.supports_lvds = false,
347162306a36Sopenharmony_ci	.supports_hdmi = false,
347262306a36Sopenharmony_ci	.supports_dp = true,
347362306a36Sopenharmony_ci	.supports_audio = false,
347462306a36Sopenharmony_ci	.supports_hdcp = false,
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	.regs = &tegra210_sor_regs,
347762306a36Sopenharmony_ci	.has_nvdisplay = false,
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	.xbar_cfg = tegra210_sor_xbar_cfg,
348062306a36Sopenharmony_ci	.lane_map = tegra210_sor_lane_map,
348162306a36Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
348262306a36Sopenharmony_ci	.pre_emphasis = tegra124_sor_pre_emphasis,
348362306a36Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
348462306a36Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
348562306a36Sopenharmony_ci};
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_cistatic const struct tegra_sor_soc tegra210_sor1 = {
348862306a36Sopenharmony_ci	.supports_lvds = false,
348962306a36Sopenharmony_ci	.supports_hdmi = true,
349062306a36Sopenharmony_ci	.supports_dp = true,
349162306a36Sopenharmony_ci	.supports_audio = true,
349262306a36Sopenharmony_ci	.supports_hdcp = true,
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_ci	.regs = &tegra210_sor_regs,
349562306a36Sopenharmony_ci	.has_nvdisplay = false,
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci	.num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults),
349862306a36Sopenharmony_ci	.settings = tegra210_sor_hdmi_defaults,
349962306a36Sopenharmony_ci	.xbar_cfg = tegra210_sor_xbar_cfg,
350062306a36Sopenharmony_ci	.lane_map = tegra210_sor_lane_map,
350162306a36Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
350262306a36Sopenharmony_ci	.pre_emphasis = tegra124_sor_pre_emphasis,
350362306a36Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
350462306a36Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
350562306a36Sopenharmony_ci};
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_cistatic const struct tegra_sor_regs tegra186_sor_regs = {
350862306a36Sopenharmony_ci	.head_state0 = 0x151,
350962306a36Sopenharmony_ci	.head_state1 = 0x154,
351062306a36Sopenharmony_ci	.head_state2 = 0x157,
351162306a36Sopenharmony_ci	.head_state3 = 0x15a,
351262306a36Sopenharmony_ci	.head_state4 = 0x15d,
351362306a36Sopenharmony_ci	.head_state5 = 0x160,
351462306a36Sopenharmony_ci	.pll0 = 0x163,
351562306a36Sopenharmony_ci	.pll1 = 0x164,
351662306a36Sopenharmony_ci	.pll2 = 0x165,
351762306a36Sopenharmony_ci	.pll3 = 0x166,
351862306a36Sopenharmony_ci	.dp_padctl0 = 0x168,
351962306a36Sopenharmony_ci	.dp_padctl2 = 0x16a,
352062306a36Sopenharmony_ci};
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_cistatic const u8 tegra186_sor_voltage_swing[4][4][4] = {
352362306a36Sopenharmony_ci	{
352462306a36Sopenharmony_ci		{ 0x13, 0x19, 0x1e, 0x28 },
352562306a36Sopenharmony_ci		{ 0x1e, 0x25, 0x2d, },
352662306a36Sopenharmony_ci		{ 0x28, 0x32, },
352762306a36Sopenharmony_ci		{ 0x39, },
352862306a36Sopenharmony_ci	}, {
352962306a36Sopenharmony_ci		{ 0x12, 0x16, 0x1b, 0x25 },
353062306a36Sopenharmony_ci		{ 0x1c, 0x23, 0x2a, },
353162306a36Sopenharmony_ci		{ 0x25, 0x2f, },
353262306a36Sopenharmony_ci		{ 0x37, }
353362306a36Sopenharmony_ci	}, {
353462306a36Sopenharmony_ci		{ 0x12, 0x16, 0x1a, 0x22 },
353562306a36Sopenharmony_ci		{ 0x1b, 0x20, 0x27, },
353662306a36Sopenharmony_ci		{ 0x24, 0x2d, },
353762306a36Sopenharmony_ci		{ 0x35, },
353862306a36Sopenharmony_ci	}, {
353962306a36Sopenharmony_ci		{ 0x11, 0x14, 0x17, 0x1f },
354062306a36Sopenharmony_ci		{ 0x19, 0x1e, 0x24, },
354162306a36Sopenharmony_ci		{ 0x22, 0x2a, },
354262306a36Sopenharmony_ci		{ 0x32, },
354362306a36Sopenharmony_ci	},
354462306a36Sopenharmony_ci};
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_cistatic const u8 tegra186_sor_pre_emphasis[4][4][4] = {
354762306a36Sopenharmony_ci	{
354862306a36Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
354962306a36Sopenharmony_ci		{ 0x01, 0x0e, 0x1d, },
355062306a36Sopenharmony_ci		{ 0x01, 0x13, },
355162306a36Sopenharmony_ci		{ 0x00, },
355262306a36Sopenharmony_ci	}, {
355362306a36Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
355462306a36Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
355562306a36Sopenharmony_ci		{ 0x00, 0x13, },
355662306a36Sopenharmony_ci		{ 0x00 },
355762306a36Sopenharmony_ci	}, {
355862306a36Sopenharmony_ci		{ 0x00, 0x08, 0x14, 0x24 },
355962306a36Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
356062306a36Sopenharmony_ci		{ 0x00, 0x13, },
356162306a36Sopenharmony_ci		{ 0x00, },
356262306a36Sopenharmony_ci	}, {
356362306a36Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
356462306a36Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
356562306a36Sopenharmony_ci		{ 0x00, 0x13, },
356662306a36Sopenharmony_ci		{ 0x00, },
356762306a36Sopenharmony_ci	},
356862306a36Sopenharmony_ci};
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_cistatic const struct tegra_sor_soc tegra186_sor = {
357162306a36Sopenharmony_ci	.supports_lvds = false,
357262306a36Sopenharmony_ci	.supports_hdmi = true,
357362306a36Sopenharmony_ci	.supports_dp = true,
357462306a36Sopenharmony_ci	.supports_audio = true,
357562306a36Sopenharmony_ci	.supports_hdcp = true,
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	.regs = &tegra186_sor_regs,
357862306a36Sopenharmony_ci	.has_nvdisplay = true,
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci	.num_settings = ARRAY_SIZE(tegra186_sor_hdmi_defaults),
358162306a36Sopenharmony_ci	.settings = tegra186_sor_hdmi_defaults,
358262306a36Sopenharmony_ci	.xbar_cfg = tegra124_sor_xbar_cfg,
358362306a36Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
358462306a36Sopenharmony_ci	.voltage_swing = tegra186_sor_voltage_swing,
358562306a36Sopenharmony_ci	.pre_emphasis = tegra186_sor_pre_emphasis,
358662306a36Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
358762306a36Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
358862306a36Sopenharmony_ci};
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_cistatic const struct tegra_sor_regs tegra194_sor_regs = {
359162306a36Sopenharmony_ci	.head_state0 = 0x151,
359262306a36Sopenharmony_ci	.head_state1 = 0x155,
359362306a36Sopenharmony_ci	.head_state2 = 0x159,
359462306a36Sopenharmony_ci	.head_state3 = 0x15d,
359562306a36Sopenharmony_ci	.head_state4 = 0x161,
359662306a36Sopenharmony_ci	.head_state5 = 0x165,
359762306a36Sopenharmony_ci	.pll0 = 0x169,
359862306a36Sopenharmony_ci	.pll1 = 0x16a,
359962306a36Sopenharmony_ci	.pll2 = 0x16b,
360062306a36Sopenharmony_ci	.pll3 = 0x16c,
360162306a36Sopenharmony_ci	.dp_padctl0 = 0x16e,
360262306a36Sopenharmony_ci	.dp_padctl2 = 0x16f,
360362306a36Sopenharmony_ci};
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_cistatic const struct tegra_sor_soc tegra194_sor = {
360662306a36Sopenharmony_ci	.supports_lvds = false,
360762306a36Sopenharmony_ci	.supports_hdmi = true,
360862306a36Sopenharmony_ci	.supports_dp = true,
360962306a36Sopenharmony_ci	.supports_audio = true,
361062306a36Sopenharmony_ci	.supports_hdcp = true,
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ci	.regs = &tegra194_sor_regs,
361362306a36Sopenharmony_ci	.has_nvdisplay = true,
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	.num_settings = ARRAY_SIZE(tegra194_sor_hdmi_defaults),
361662306a36Sopenharmony_ci	.settings = tegra194_sor_hdmi_defaults,
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci	.xbar_cfg = tegra210_sor_xbar_cfg,
361962306a36Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
362062306a36Sopenharmony_ci	.voltage_swing = tegra186_sor_voltage_swing,
362162306a36Sopenharmony_ci	.pre_emphasis = tegra186_sor_pre_emphasis,
362262306a36Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
362362306a36Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
362462306a36Sopenharmony_ci};
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_cistatic const struct of_device_id tegra_sor_of_match[] = {
362762306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra194-sor", .data = &tegra194_sor },
362862306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra186-sor", .data = &tegra186_sor },
362962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-sor1", .data = &tegra210_sor1 },
363062306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-sor", .data = &tegra210_sor },
363162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra132-sor", .data = &tegra132_sor },
363262306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-sor", .data = &tegra124_sor },
363362306a36Sopenharmony_ci	{ },
363462306a36Sopenharmony_ci};
363562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_sor_of_match);
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_cistatic int tegra_sor_parse_dt(struct tegra_sor *sor)
363862306a36Sopenharmony_ci{
363962306a36Sopenharmony_ci	struct device_node *np = sor->dev->of_node;
364062306a36Sopenharmony_ci	u32 xbar_cfg[5];
364162306a36Sopenharmony_ci	unsigned int i;
364262306a36Sopenharmony_ci	u32 value;
364362306a36Sopenharmony_ci	int err;
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci	if (sor->soc->has_nvdisplay) {
364662306a36Sopenharmony_ci		err = of_property_read_u32(np, "nvidia,interface", &value);
364762306a36Sopenharmony_ci		if (err < 0)
364862306a36Sopenharmony_ci			return err;
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci		sor->index = value;
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci		/*
365362306a36Sopenharmony_ci		 * override the default that we already set for Tegra210 and
365462306a36Sopenharmony_ci		 * earlier
365562306a36Sopenharmony_ci		 */
365662306a36Sopenharmony_ci		sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index;
365762306a36Sopenharmony_ci	} else {
365862306a36Sopenharmony_ci		if (!sor->soc->supports_audio)
365962306a36Sopenharmony_ci			sor->index = 0;
366062306a36Sopenharmony_ci		else
366162306a36Sopenharmony_ci			sor->index = 1;
366262306a36Sopenharmony_ci	}
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci	err = of_property_read_u32_array(np, "nvidia,xbar-cfg", xbar_cfg, 5);
366562306a36Sopenharmony_ci	if (err < 0) {
366662306a36Sopenharmony_ci		/* fall back to default per-SoC XBAR configuration */
366762306a36Sopenharmony_ci		for (i = 0; i < 5; i++)
366862306a36Sopenharmony_ci			sor->xbar_cfg[i] = sor->soc->xbar_cfg[i];
366962306a36Sopenharmony_ci	} else {
367062306a36Sopenharmony_ci		/* copy cells to SOR XBAR configuration */
367162306a36Sopenharmony_ci		for (i = 0; i < 5; i++)
367262306a36Sopenharmony_ci			sor->xbar_cfg[i] = xbar_cfg[i];
367362306a36Sopenharmony_ci	}
367462306a36Sopenharmony_ci
367562306a36Sopenharmony_ci	return 0;
367662306a36Sopenharmony_ci}
367762306a36Sopenharmony_ci
367862306a36Sopenharmony_cistatic irqreturn_t tegra_sor_irq(int irq, void *data)
367962306a36Sopenharmony_ci{
368062306a36Sopenharmony_ci	struct tegra_sor *sor = data;
368162306a36Sopenharmony_ci	u32 value;
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_INT_STATUS);
368462306a36Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_INT_STATUS);
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	if (value & SOR_INT_CODEC_SCRATCH0) {
368762306a36Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_AUDIO_HDA_CODEC_SCRATCH0);
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci		if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) {
369062306a36Sopenharmony_ci			unsigned int format;
369162306a36Sopenharmony_ci
369262306a36Sopenharmony_ci			format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK;
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci			tegra_hda_parse_format(format, &sor->format);
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci			if (sor->ops->audio_enable)
369762306a36Sopenharmony_ci				sor->ops->audio_enable(sor);
369862306a36Sopenharmony_ci		} else {
369962306a36Sopenharmony_ci			if (sor->ops->audio_disable)
370062306a36Sopenharmony_ci				sor->ops->audio_disable(sor);
370162306a36Sopenharmony_ci		}
370262306a36Sopenharmony_ci	}
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci	return IRQ_HANDLED;
370562306a36Sopenharmony_ci}
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_cistatic int tegra_sor_probe(struct platform_device *pdev)
370862306a36Sopenharmony_ci{
370962306a36Sopenharmony_ci	struct device_node *np;
371062306a36Sopenharmony_ci	struct tegra_sor *sor;
371162306a36Sopenharmony_ci	int err;
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_ci	sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL);
371462306a36Sopenharmony_ci	if (!sor)
371562306a36Sopenharmony_ci		return -ENOMEM;
371662306a36Sopenharmony_ci
371762306a36Sopenharmony_ci	sor->soc = of_device_get_match_data(&pdev->dev);
371862306a36Sopenharmony_ci	sor->output.dev = sor->dev = &pdev->dev;
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci	sor->settings = devm_kmemdup(&pdev->dev, sor->soc->settings,
372162306a36Sopenharmony_ci				     sor->soc->num_settings *
372262306a36Sopenharmony_ci					sizeof(*sor->settings),
372362306a36Sopenharmony_ci				     GFP_KERNEL);
372462306a36Sopenharmony_ci	if (!sor->settings)
372562306a36Sopenharmony_ci		return -ENOMEM;
372662306a36Sopenharmony_ci
372762306a36Sopenharmony_ci	sor->num_settings = sor->soc->num_settings;
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0);
373062306a36Sopenharmony_ci	if (np) {
373162306a36Sopenharmony_ci		sor->aux = drm_dp_aux_find_by_of_node(np);
373262306a36Sopenharmony_ci		of_node_put(np);
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci		if (!sor->aux)
373562306a36Sopenharmony_ci			return -EPROBE_DEFER;
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_ci		if (get_device(sor->aux->dev))
373862306a36Sopenharmony_ci			sor->output.ddc = &sor->aux->ddc;
373962306a36Sopenharmony_ci	}
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci	if (!sor->aux) {
374262306a36Sopenharmony_ci		if (sor->soc->supports_hdmi) {
374362306a36Sopenharmony_ci			sor->ops = &tegra_sor_hdmi_ops;
374462306a36Sopenharmony_ci			sor->pad = TEGRA_IO_PAD_HDMI;
374562306a36Sopenharmony_ci		} else if (sor->soc->supports_lvds) {
374662306a36Sopenharmony_ci			dev_err(&pdev->dev, "LVDS not supported yet\n");
374762306a36Sopenharmony_ci			return -ENODEV;
374862306a36Sopenharmony_ci		} else {
374962306a36Sopenharmony_ci			dev_err(&pdev->dev, "unknown (non-DP) support\n");
375062306a36Sopenharmony_ci			return -ENODEV;
375162306a36Sopenharmony_ci		}
375262306a36Sopenharmony_ci	} else {
375362306a36Sopenharmony_ci		np = of_parse_phandle(pdev->dev.of_node, "nvidia,panel", 0);
375462306a36Sopenharmony_ci		/*
375562306a36Sopenharmony_ci		 * No need to keep this around since we only use it as a check
375662306a36Sopenharmony_ci		 * to see if a panel is connected (eDP) or not (DP).
375762306a36Sopenharmony_ci		 */
375862306a36Sopenharmony_ci		of_node_put(np);
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci		sor->ops = &tegra_sor_dp_ops;
376162306a36Sopenharmony_ci		sor->pad = TEGRA_IO_PAD_LVDS;
376262306a36Sopenharmony_ci	}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci	err = tegra_sor_parse_dt(sor);
376562306a36Sopenharmony_ci	if (err < 0)
376662306a36Sopenharmony_ci		goto put_aux;
376762306a36Sopenharmony_ci
376862306a36Sopenharmony_ci	err = tegra_output_probe(&sor->output);
376962306a36Sopenharmony_ci	if (err < 0) {
377062306a36Sopenharmony_ci		dev_err_probe(&pdev->dev, err, "failed to probe output\n");
377162306a36Sopenharmony_ci		goto put_aux;
377262306a36Sopenharmony_ci	}
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	if (sor->ops && sor->ops->probe) {
377562306a36Sopenharmony_ci		err = sor->ops->probe(sor);
377662306a36Sopenharmony_ci		if (err < 0) {
377762306a36Sopenharmony_ci			dev_err(&pdev->dev, "failed to probe %s: %d\n",
377862306a36Sopenharmony_ci				sor->ops->name, err);
377962306a36Sopenharmony_ci			goto remove;
378062306a36Sopenharmony_ci		}
378162306a36Sopenharmony_ci	}
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci	sor->regs = devm_platform_ioremap_resource(pdev, 0);
378462306a36Sopenharmony_ci	if (IS_ERR(sor->regs)) {
378562306a36Sopenharmony_ci		err = PTR_ERR(sor->regs);
378662306a36Sopenharmony_ci		goto remove;
378762306a36Sopenharmony_ci	}
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ci	err = platform_get_irq(pdev, 0);
379062306a36Sopenharmony_ci	if (err < 0)
379162306a36Sopenharmony_ci		goto remove;
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci	sor->irq = err;
379462306a36Sopenharmony_ci
379562306a36Sopenharmony_ci	err = devm_request_irq(sor->dev, sor->irq, tegra_sor_irq, 0,
379662306a36Sopenharmony_ci			       dev_name(sor->dev), sor);
379762306a36Sopenharmony_ci	if (err < 0) {
379862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
379962306a36Sopenharmony_ci		goto remove;
380062306a36Sopenharmony_ci	}
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_ci	sor->rst = devm_reset_control_get_exclusive_released(&pdev->dev, "sor");
380362306a36Sopenharmony_ci	if (IS_ERR(sor->rst)) {
380462306a36Sopenharmony_ci		err = PTR_ERR(sor->rst);
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci		if (err != -EBUSY || WARN_ON(!pdev->dev.pm_domain)) {
380762306a36Sopenharmony_ci			dev_err(&pdev->dev, "failed to get reset control: %d\n",
380862306a36Sopenharmony_ci				err);
380962306a36Sopenharmony_ci			goto remove;
381062306a36Sopenharmony_ci		}
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci		/*
381362306a36Sopenharmony_ci		 * At this point, the reset control is most likely being used
381462306a36Sopenharmony_ci		 * by the generic power domain implementation. With any luck
381562306a36Sopenharmony_ci		 * the power domain will have taken care of resetting the SOR
381662306a36Sopenharmony_ci		 * and we don't have to do anything.
381762306a36Sopenharmony_ci		 */
381862306a36Sopenharmony_ci		sor->rst = NULL;
381962306a36Sopenharmony_ci	}
382062306a36Sopenharmony_ci
382162306a36Sopenharmony_ci	sor->clk = devm_clk_get(&pdev->dev, NULL);
382262306a36Sopenharmony_ci	if (IS_ERR(sor->clk)) {
382362306a36Sopenharmony_ci		err = PTR_ERR(sor->clk);
382462306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get module clock: %d\n", err);
382562306a36Sopenharmony_ci		goto remove;
382662306a36Sopenharmony_ci	}
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	if (sor->soc->supports_hdmi || sor->soc->supports_dp) {
382962306a36Sopenharmony_ci		struct device_node *np = pdev->dev.of_node;
383062306a36Sopenharmony_ci		const char *name;
383162306a36Sopenharmony_ci
383262306a36Sopenharmony_ci		/*
383362306a36Sopenharmony_ci		 * For backwards compatibility with Tegra210 device trees,
383462306a36Sopenharmony_ci		 * fall back to the old clock name "source" if the new "out"
383562306a36Sopenharmony_ci		 * clock is not available.
383662306a36Sopenharmony_ci		 */
383762306a36Sopenharmony_ci		if (of_property_match_string(np, "clock-names", "out") < 0)
383862306a36Sopenharmony_ci			name = "source";
383962306a36Sopenharmony_ci		else
384062306a36Sopenharmony_ci			name = "out";
384162306a36Sopenharmony_ci
384262306a36Sopenharmony_ci		sor->clk_out = devm_clk_get(&pdev->dev, name);
384362306a36Sopenharmony_ci		if (IS_ERR(sor->clk_out)) {
384462306a36Sopenharmony_ci			err = PTR_ERR(sor->clk_out);
384562306a36Sopenharmony_ci			dev_err(sor->dev, "failed to get %s clock: %d\n",
384662306a36Sopenharmony_ci				name, err);
384762306a36Sopenharmony_ci			goto remove;
384862306a36Sopenharmony_ci		}
384962306a36Sopenharmony_ci	} else {
385062306a36Sopenharmony_ci		/* fall back to the module clock on SOR0 (eDP/LVDS only) */
385162306a36Sopenharmony_ci		sor->clk_out = sor->clk;
385262306a36Sopenharmony_ci	}
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	sor->clk_parent = devm_clk_get(&pdev->dev, "parent");
385562306a36Sopenharmony_ci	if (IS_ERR(sor->clk_parent)) {
385662306a36Sopenharmony_ci		err = PTR_ERR(sor->clk_parent);
385762306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get parent clock: %d\n", err);
385862306a36Sopenharmony_ci		goto remove;
385962306a36Sopenharmony_ci	}
386062306a36Sopenharmony_ci
386162306a36Sopenharmony_ci	sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
386262306a36Sopenharmony_ci	if (IS_ERR(sor->clk_safe)) {
386362306a36Sopenharmony_ci		err = PTR_ERR(sor->clk_safe);
386462306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get safe clock: %d\n", err);
386562306a36Sopenharmony_ci		goto remove;
386662306a36Sopenharmony_ci	}
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci	sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
386962306a36Sopenharmony_ci	if (IS_ERR(sor->clk_dp)) {
387062306a36Sopenharmony_ci		err = PTR_ERR(sor->clk_dp);
387162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get DP clock: %d\n", err);
387262306a36Sopenharmony_ci		goto remove;
387362306a36Sopenharmony_ci	}
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	/*
387662306a36Sopenharmony_ci	 * Starting with Tegra186, the BPMP provides an implementation for
387762306a36Sopenharmony_ci	 * the pad output clock, so we have to look it up from device tree.
387862306a36Sopenharmony_ci	 */
387962306a36Sopenharmony_ci	sor->clk_pad = devm_clk_get(&pdev->dev, "pad");
388062306a36Sopenharmony_ci	if (IS_ERR(sor->clk_pad)) {
388162306a36Sopenharmony_ci		if (sor->clk_pad != ERR_PTR(-ENOENT)) {
388262306a36Sopenharmony_ci			err = PTR_ERR(sor->clk_pad);
388362306a36Sopenharmony_ci			goto remove;
388462306a36Sopenharmony_ci		}
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci		/*
388762306a36Sopenharmony_ci		 * If the pad output clock is not available, then we assume
388862306a36Sopenharmony_ci		 * we're on Tegra210 or earlier and have to provide our own
388962306a36Sopenharmony_ci		 * implementation.
389062306a36Sopenharmony_ci		 */
389162306a36Sopenharmony_ci		sor->clk_pad = NULL;
389262306a36Sopenharmony_ci	}
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	/*
389562306a36Sopenharmony_ci	 * The bootloader may have set up the SOR such that it's module clock
389662306a36Sopenharmony_ci	 * is sourced by one of the display PLLs. However, that doesn't work
389762306a36Sopenharmony_ci	 * without properly having set up other bits of the SOR.
389862306a36Sopenharmony_ci	 */
389962306a36Sopenharmony_ci	err = clk_set_parent(sor->clk_out, sor->clk_safe);
390062306a36Sopenharmony_ci	if (err < 0) {
390162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to use safe clock: %d\n", err);
390262306a36Sopenharmony_ci		goto remove;
390362306a36Sopenharmony_ci	}
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	platform_set_drvdata(pdev, sor);
390662306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
390762306a36Sopenharmony_ci
390862306a36Sopenharmony_ci	host1x_client_init(&sor->client);
390962306a36Sopenharmony_ci	sor->client.ops = &sor_client_ops;
391062306a36Sopenharmony_ci	sor->client.dev = &pdev->dev;
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	/*
391362306a36Sopenharmony_ci	 * On Tegra210 and earlier, provide our own implementation for the
391462306a36Sopenharmony_ci	 * pad output clock.
391562306a36Sopenharmony_ci	 */
391662306a36Sopenharmony_ci	if (!sor->clk_pad) {
391762306a36Sopenharmony_ci		char *name;
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci		name = devm_kasprintf(sor->dev, GFP_KERNEL, "sor%u_pad_clkout",
392062306a36Sopenharmony_ci				      sor->index);
392162306a36Sopenharmony_ci		if (!name) {
392262306a36Sopenharmony_ci			err = -ENOMEM;
392362306a36Sopenharmony_ci			goto uninit;
392462306a36Sopenharmony_ci		}
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci		err = host1x_client_resume(&sor->client);
392762306a36Sopenharmony_ci		if (err < 0) {
392862306a36Sopenharmony_ci			dev_err(sor->dev, "failed to resume: %d\n", err);
392962306a36Sopenharmony_ci			goto uninit;
393062306a36Sopenharmony_ci		}
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_ci		sor->clk_pad = tegra_clk_sor_pad_register(sor, name);
393362306a36Sopenharmony_ci		host1x_client_suspend(&sor->client);
393462306a36Sopenharmony_ci	}
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	if (IS_ERR(sor->clk_pad)) {
393762306a36Sopenharmony_ci		err = PTR_ERR(sor->clk_pad);
393862306a36Sopenharmony_ci		dev_err(sor->dev, "failed to register SOR pad clock: %d\n",
393962306a36Sopenharmony_ci			err);
394062306a36Sopenharmony_ci		goto uninit;
394162306a36Sopenharmony_ci	}
394262306a36Sopenharmony_ci
394362306a36Sopenharmony_ci	err = __host1x_client_register(&sor->client);
394462306a36Sopenharmony_ci	if (err < 0) {
394562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
394662306a36Sopenharmony_ci			err);
394762306a36Sopenharmony_ci		goto uninit;
394862306a36Sopenharmony_ci	}
394962306a36Sopenharmony_ci
395062306a36Sopenharmony_ci	return 0;
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_ciuninit:
395362306a36Sopenharmony_ci	host1x_client_exit(&sor->client);
395462306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
395562306a36Sopenharmony_ciremove:
395662306a36Sopenharmony_ci	if (sor->aux)
395762306a36Sopenharmony_ci		sor->output.ddc = NULL;
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	tegra_output_remove(&sor->output);
396062306a36Sopenharmony_ciput_aux:
396162306a36Sopenharmony_ci	if (sor->aux)
396262306a36Sopenharmony_ci		put_device(sor->aux->dev);
396362306a36Sopenharmony_ci
396462306a36Sopenharmony_ci	return err;
396562306a36Sopenharmony_ci}
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_cistatic void tegra_sor_remove(struct platform_device *pdev)
396862306a36Sopenharmony_ci{
396962306a36Sopenharmony_ci	struct tegra_sor *sor = platform_get_drvdata(pdev);
397062306a36Sopenharmony_ci
397162306a36Sopenharmony_ci	host1x_client_unregister(&sor->client);
397262306a36Sopenharmony_ci
397362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
397462306a36Sopenharmony_ci
397562306a36Sopenharmony_ci	if (sor->aux) {
397662306a36Sopenharmony_ci		put_device(sor->aux->dev);
397762306a36Sopenharmony_ci		sor->output.ddc = NULL;
397862306a36Sopenharmony_ci	}
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	tegra_output_remove(&sor->output);
398162306a36Sopenharmony_ci}
398262306a36Sopenharmony_ci
398362306a36Sopenharmony_cistatic int __maybe_unused tegra_sor_suspend(struct device *dev)
398462306a36Sopenharmony_ci{
398562306a36Sopenharmony_ci	struct tegra_sor *sor = dev_get_drvdata(dev);
398662306a36Sopenharmony_ci	int err;
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_ci	err = tegra_output_suspend(&sor->output);
398962306a36Sopenharmony_ci	if (err < 0) {
399062306a36Sopenharmony_ci		dev_err(dev, "failed to suspend output: %d\n", err);
399162306a36Sopenharmony_ci		return err;
399262306a36Sopenharmony_ci	}
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_ci	if (sor->hdmi_supply) {
399562306a36Sopenharmony_ci		err = regulator_disable(sor->hdmi_supply);
399662306a36Sopenharmony_ci		if (err < 0) {
399762306a36Sopenharmony_ci			tegra_output_resume(&sor->output);
399862306a36Sopenharmony_ci			return err;
399962306a36Sopenharmony_ci		}
400062306a36Sopenharmony_ci	}
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci	return 0;
400362306a36Sopenharmony_ci}
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_cistatic int __maybe_unused tegra_sor_resume(struct device *dev)
400662306a36Sopenharmony_ci{
400762306a36Sopenharmony_ci	struct tegra_sor *sor = dev_get_drvdata(dev);
400862306a36Sopenharmony_ci	int err;
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci	if (sor->hdmi_supply) {
401162306a36Sopenharmony_ci		err = regulator_enable(sor->hdmi_supply);
401262306a36Sopenharmony_ci		if (err < 0)
401362306a36Sopenharmony_ci			return err;
401462306a36Sopenharmony_ci	}
401562306a36Sopenharmony_ci
401662306a36Sopenharmony_ci	err = tegra_output_resume(&sor->output);
401762306a36Sopenharmony_ci	if (err < 0) {
401862306a36Sopenharmony_ci		dev_err(dev, "failed to resume output: %d\n", err);
401962306a36Sopenharmony_ci
402062306a36Sopenharmony_ci		if (sor->hdmi_supply)
402162306a36Sopenharmony_ci			regulator_disable(sor->hdmi_supply);
402262306a36Sopenharmony_ci
402362306a36Sopenharmony_ci		return err;
402462306a36Sopenharmony_ci	}
402562306a36Sopenharmony_ci
402662306a36Sopenharmony_ci	return 0;
402762306a36Sopenharmony_ci}
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_cistatic const struct dev_pm_ops tegra_sor_pm_ops = {
403062306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(tegra_sor_suspend, tegra_sor_resume)
403162306a36Sopenharmony_ci};
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_cistruct platform_driver tegra_sor_driver = {
403462306a36Sopenharmony_ci	.driver = {
403562306a36Sopenharmony_ci		.name = "tegra-sor",
403662306a36Sopenharmony_ci		.of_match_table = tegra_sor_of_match,
403762306a36Sopenharmony_ci		.pm = &tegra_sor_pm_ops,
403862306a36Sopenharmony_ci	},
403962306a36Sopenharmony_ci	.probe = tegra_sor_probe,
404062306a36Sopenharmony_ci	.remove_new = tegra_sor_remove,
404162306a36Sopenharmony_ci};
4042