18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
88c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/of_device.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
158c2ecf20Sopenharmony_ci#include <linux/reset.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <soc/tegra/pmc.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
208c2ecf20Sopenharmony_ci#include <drm/drm_debugfs.h>
218c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h>
228c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
238c2ecf20Sopenharmony_ci#include <drm/drm_panel.h>
248c2ecf20Sopenharmony_ci#include <drm/drm_scdc_helper.h>
258c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "dc.h"
288c2ecf20Sopenharmony_ci#include "dp.h"
298c2ecf20Sopenharmony_ci#include "drm.h"
308c2ecf20Sopenharmony_ci#include "hda.h"
318c2ecf20Sopenharmony_ci#include "sor.h"
328c2ecf20Sopenharmony_ci#include "trace.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define SOR_REKEY 0x38
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct tegra_sor_hdmi_settings {
378c2ecf20Sopenharmony_ci	unsigned long frequency;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	u8 vcocap;
408c2ecf20Sopenharmony_ci	u8 filter;
418c2ecf20Sopenharmony_ci	u8 ichpmp;
428c2ecf20Sopenharmony_ci	u8 loadadj;
438c2ecf20Sopenharmony_ci	u8 tmds_termadj;
448c2ecf20Sopenharmony_ci	u8 tx_pu_value;
458c2ecf20Sopenharmony_ci	u8 bg_temp_coef;
468c2ecf20Sopenharmony_ci	u8 bg_vref_level;
478c2ecf20Sopenharmony_ci	u8 avdd10_level;
488c2ecf20Sopenharmony_ci	u8 avdd14_level;
498c2ecf20Sopenharmony_ci	u8 sparepll;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	u8 drive_current[4];
528c2ecf20Sopenharmony_ci	u8 preemphasis[4];
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#if 1
568c2ecf20Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = {
578c2ecf20Sopenharmony_ci	{
588c2ecf20Sopenharmony_ci		.frequency = 54000000,
598c2ecf20Sopenharmony_ci		.vcocap = 0x0,
608c2ecf20Sopenharmony_ci		.filter = 0x0,
618c2ecf20Sopenharmony_ci		.ichpmp = 0x1,
628c2ecf20Sopenharmony_ci		.loadadj = 0x3,
638c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
648c2ecf20Sopenharmony_ci		.tx_pu_value = 0x10,
658c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
668c2ecf20Sopenharmony_ci		.bg_vref_level = 0x8,
678c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
688c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
698c2ecf20Sopenharmony_ci		.sparepll = 0x0,
708c2ecf20Sopenharmony_ci		.drive_current = { 0x33, 0x3a, 0x3a, 0x3a },
718c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
728c2ecf20Sopenharmony_ci	}, {
738c2ecf20Sopenharmony_ci		.frequency = 75000000,
748c2ecf20Sopenharmony_ci		.vcocap = 0x3,
758c2ecf20Sopenharmony_ci		.filter = 0x0,
768c2ecf20Sopenharmony_ci		.ichpmp = 0x1,
778c2ecf20Sopenharmony_ci		.loadadj = 0x3,
788c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
798c2ecf20Sopenharmony_ci		.tx_pu_value = 0x40,
808c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
818c2ecf20Sopenharmony_ci		.bg_vref_level = 0x8,
828c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
838c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
848c2ecf20Sopenharmony_ci		.sparepll = 0x0,
858c2ecf20Sopenharmony_ci		.drive_current = { 0x33, 0x3a, 0x3a, 0x3a },
868c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
878c2ecf20Sopenharmony_ci	}, {
888c2ecf20Sopenharmony_ci		.frequency = 150000000,
898c2ecf20Sopenharmony_ci		.vcocap = 0x3,
908c2ecf20Sopenharmony_ci		.filter = 0x0,
918c2ecf20Sopenharmony_ci		.ichpmp = 0x1,
928c2ecf20Sopenharmony_ci		.loadadj = 0x3,
938c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
948c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66,
958c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
968c2ecf20Sopenharmony_ci		.bg_vref_level = 0x8,
978c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
988c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
998c2ecf20Sopenharmony_ci		.sparepll = 0x0,
1008c2ecf20Sopenharmony_ci		.drive_current = { 0x33, 0x3a, 0x3a, 0x3a },
1018c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
1028c2ecf20Sopenharmony_ci	}, {
1038c2ecf20Sopenharmony_ci		.frequency = 300000000,
1048c2ecf20Sopenharmony_ci		.vcocap = 0x3,
1058c2ecf20Sopenharmony_ci		.filter = 0x0,
1068c2ecf20Sopenharmony_ci		.ichpmp = 0x1,
1078c2ecf20Sopenharmony_ci		.loadadj = 0x3,
1088c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
1098c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66,
1108c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
1118c2ecf20Sopenharmony_ci		.bg_vref_level = 0xa,
1128c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
1138c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
1148c2ecf20Sopenharmony_ci		.sparepll = 0x0,
1158c2ecf20Sopenharmony_ci		.drive_current = { 0x33, 0x3f, 0x3f, 0x3f },
1168c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x17, 0x17, 0x17 },
1178c2ecf20Sopenharmony_ci	}, {
1188c2ecf20Sopenharmony_ci		.frequency = 600000000,
1198c2ecf20Sopenharmony_ci		.vcocap = 0x3,
1208c2ecf20Sopenharmony_ci		.filter = 0x0,
1218c2ecf20Sopenharmony_ci		.ichpmp = 0x1,
1228c2ecf20Sopenharmony_ci		.loadadj = 0x3,
1238c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
1248c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66,
1258c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
1268c2ecf20Sopenharmony_ci		.bg_vref_level = 0x8,
1278c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
1288c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
1298c2ecf20Sopenharmony_ci		.sparepll = 0x0,
1308c2ecf20Sopenharmony_ci		.drive_current = { 0x33, 0x3f, 0x3f, 0x3f },
1318c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
1328c2ecf20Sopenharmony_ci	},
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci#else
1358c2ecf20Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = {
1368c2ecf20Sopenharmony_ci	{
1378c2ecf20Sopenharmony_ci		.frequency = 75000000,
1388c2ecf20Sopenharmony_ci		.vcocap = 0x3,
1398c2ecf20Sopenharmony_ci		.filter = 0x0,
1408c2ecf20Sopenharmony_ci		.ichpmp = 0x1,
1418c2ecf20Sopenharmony_ci		.loadadj = 0x3,
1428c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
1438c2ecf20Sopenharmony_ci		.tx_pu_value = 0x40,
1448c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
1458c2ecf20Sopenharmony_ci		.bg_vref_level = 0x8,
1468c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
1478c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
1488c2ecf20Sopenharmony_ci		.sparepll = 0x0,
1498c2ecf20Sopenharmony_ci		.drive_current = { 0x29, 0x29, 0x29, 0x29 },
1508c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
1518c2ecf20Sopenharmony_ci	}, {
1528c2ecf20Sopenharmony_ci		.frequency = 150000000,
1538c2ecf20Sopenharmony_ci		.vcocap = 0x3,
1548c2ecf20Sopenharmony_ci		.filter = 0x0,
1558c2ecf20Sopenharmony_ci		.ichpmp = 0x1,
1568c2ecf20Sopenharmony_ci		.loadadj = 0x3,
1578c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
1588c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66,
1598c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
1608c2ecf20Sopenharmony_ci		.bg_vref_level = 0x8,
1618c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
1628c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
1638c2ecf20Sopenharmony_ci		.sparepll = 0x0,
1648c2ecf20Sopenharmony_ci		.drive_current = { 0x30, 0x37, 0x37, 0x37 },
1658c2ecf20Sopenharmony_ci		.preemphasis = { 0x01, 0x02, 0x02, 0x02 },
1668c2ecf20Sopenharmony_ci	}, {
1678c2ecf20Sopenharmony_ci		.frequency = 300000000,
1688c2ecf20Sopenharmony_ci		.vcocap = 0x3,
1698c2ecf20Sopenharmony_ci		.filter = 0x0,
1708c2ecf20Sopenharmony_ci		.ichpmp = 0x6,
1718c2ecf20Sopenharmony_ci		.loadadj = 0x3,
1728c2ecf20Sopenharmony_ci		.tmds_termadj = 0x9,
1738c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66,
1748c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
1758c2ecf20Sopenharmony_ci		.bg_vref_level = 0xf,
1768c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
1778c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
1788c2ecf20Sopenharmony_ci		.sparepll = 0x0,
1798c2ecf20Sopenharmony_ci		.drive_current = { 0x30, 0x37, 0x37, 0x37 },
1808c2ecf20Sopenharmony_ci		.preemphasis = { 0x10, 0x3e, 0x3e, 0x3e },
1818c2ecf20Sopenharmony_ci	}, {
1828c2ecf20Sopenharmony_ci		.frequency = 600000000,
1838c2ecf20Sopenharmony_ci		.vcocap = 0x3,
1848c2ecf20Sopenharmony_ci		.filter = 0x0,
1858c2ecf20Sopenharmony_ci		.ichpmp = 0xa,
1868c2ecf20Sopenharmony_ci		.loadadj = 0x3,
1878c2ecf20Sopenharmony_ci		.tmds_termadj = 0xb,
1888c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66,
1898c2ecf20Sopenharmony_ci		.bg_temp_coef = 0x3,
1908c2ecf20Sopenharmony_ci		.bg_vref_level = 0xe,
1918c2ecf20Sopenharmony_ci		.avdd10_level = 0x4,
1928c2ecf20Sopenharmony_ci		.avdd14_level = 0x4,
1938c2ecf20Sopenharmony_ci		.sparepll = 0x0,
1948c2ecf20Sopenharmony_ci		.drive_current = { 0x35, 0x3e, 0x3e, 0x3e },
1958c2ecf20Sopenharmony_ci		.preemphasis = { 0x02, 0x3f, 0x3f, 0x3f },
1968c2ecf20Sopenharmony_ci	},
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ci#endif
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra186_sor_hdmi_defaults[] = {
2018c2ecf20Sopenharmony_ci	{
2028c2ecf20Sopenharmony_ci		.frequency = 54000000,
2038c2ecf20Sopenharmony_ci		.vcocap = 0,
2048c2ecf20Sopenharmony_ci		.filter = 5,
2058c2ecf20Sopenharmony_ci		.ichpmp = 5,
2068c2ecf20Sopenharmony_ci		.loadadj = 3,
2078c2ecf20Sopenharmony_ci		.tmds_termadj = 0xf,
2088c2ecf20Sopenharmony_ci		.tx_pu_value = 0,
2098c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
2108c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
2118c2ecf20Sopenharmony_ci		.avdd10_level = 4,
2128c2ecf20Sopenharmony_ci		.avdd14_level = 4,
2138c2ecf20Sopenharmony_ci		.sparepll = 0x54,
2148c2ecf20Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
2158c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
2168c2ecf20Sopenharmony_ci	}, {
2178c2ecf20Sopenharmony_ci		.frequency = 75000000,
2188c2ecf20Sopenharmony_ci		.vcocap = 1,
2198c2ecf20Sopenharmony_ci		.filter = 5,
2208c2ecf20Sopenharmony_ci		.ichpmp = 5,
2218c2ecf20Sopenharmony_ci		.loadadj = 3,
2228c2ecf20Sopenharmony_ci		.tmds_termadj = 0xf,
2238c2ecf20Sopenharmony_ci		.tx_pu_value = 0,
2248c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
2258c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
2268c2ecf20Sopenharmony_ci		.avdd10_level = 4,
2278c2ecf20Sopenharmony_ci		.avdd14_level = 4,
2288c2ecf20Sopenharmony_ci		.sparepll = 0x44,
2298c2ecf20Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
2308c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
2318c2ecf20Sopenharmony_ci	}, {
2328c2ecf20Sopenharmony_ci		.frequency = 150000000,
2338c2ecf20Sopenharmony_ci		.vcocap = 3,
2348c2ecf20Sopenharmony_ci		.filter = 5,
2358c2ecf20Sopenharmony_ci		.ichpmp = 5,
2368c2ecf20Sopenharmony_ci		.loadadj = 3,
2378c2ecf20Sopenharmony_ci		.tmds_termadj = 15,
2388c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66 /* 0 */,
2398c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
2408c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
2418c2ecf20Sopenharmony_ci		.avdd10_level = 4,
2428c2ecf20Sopenharmony_ci		.avdd14_level = 4,
2438c2ecf20Sopenharmony_ci		.sparepll = 0x00, /* 0x34 */
2448c2ecf20Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x37 },
2458c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
2468c2ecf20Sopenharmony_ci	}, {
2478c2ecf20Sopenharmony_ci		.frequency = 300000000,
2488c2ecf20Sopenharmony_ci		.vcocap = 3,
2498c2ecf20Sopenharmony_ci		.filter = 5,
2508c2ecf20Sopenharmony_ci		.ichpmp = 5,
2518c2ecf20Sopenharmony_ci		.loadadj = 3,
2528c2ecf20Sopenharmony_ci		.tmds_termadj = 15,
2538c2ecf20Sopenharmony_ci		.tx_pu_value = 64,
2548c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
2558c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
2568c2ecf20Sopenharmony_ci		.avdd10_level = 4,
2578c2ecf20Sopenharmony_ci		.avdd14_level = 4,
2588c2ecf20Sopenharmony_ci		.sparepll = 0x34,
2598c2ecf20Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
2608c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
2618c2ecf20Sopenharmony_ci	}, {
2628c2ecf20Sopenharmony_ci		.frequency = 600000000,
2638c2ecf20Sopenharmony_ci		.vcocap = 3,
2648c2ecf20Sopenharmony_ci		.filter = 5,
2658c2ecf20Sopenharmony_ci		.ichpmp = 5,
2668c2ecf20Sopenharmony_ci		.loadadj = 3,
2678c2ecf20Sopenharmony_ci		.tmds_termadj = 12,
2688c2ecf20Sopenharmony_ci		.tx_pu_value = 96,
2698c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
2708c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
2718c2ecf20Sopenharmony_ci		.avdd10_level = 4,
2728c2ecf20Sopenharmony_ci		.avdd14_level = 4,
2738c2ecf20Sopenharmony_ci		.sparepll = 0x34,
2748c2ecf20Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
2758c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci};
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic const struct tegra_sor_hdmi_settings tegra194_sor_hdmi_defaults[] = {
2808c2ecf20Sopenharmony_ci	{
2818c2ecf20Sopenharmony_ci		.frequency = 54000000,
2828c2ecf20Sopenharmony_ci		.vcocap = 0,
2838c2ecf20Sopenharmony_ci		.filter = 5,
2848c2ecf20Sopenharmony_ci		.ichpmp = 5,
2858c2ecf20Sopenharmony_ci		.loadadj = 3,
2868c2ecf20Sopenharmony_ci		.tmds_termadj = 0xf,
2878c2ecf20Sopenharmony_ci		.tx_pu_value = 0,
2888c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
2898c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
2908c2ecf20Sopenharmony_ci		.avdd10_level = 4,
2918c2ecf20Sopenharmony_ci		.avdd14_level = 4,
2928c2ecf20Sopenharmony_ci		.sparepll = 0x54,
2938c2ecf20Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
2948c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
2958c2ecf20Sopenharmony_ci	}, {
2968c2ecf20Sopenharmony_ci		.frequency = 75000000,
2978c2ecf20Sopenharmony_ci		.vcocap = 1,
2988c2ecf20Sopenharmony_ci		.filter = 5,
2998c2ecf20Sopenharmony_ci		.ichpmp = 5,
3008c2ecf20Sopenharmony_ci		.loadadj = 3,
3018c2ecf20Sopenharmony_ci		.tmds_termadj = 0xf,
3028c2ecf20Sopenharmony_ci		.tx_pu_value = 0,
3038c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
3048c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
3058c2ecf20Sopenharmony_ci		.avdd10_level = 4,
3068c2ecf20Sopenharmony_ci		.avdd14_level = 4,
3078c2ecf20Sopenharmony_ci		.sparepll = 0x44,
3088c2ecf20Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x33 },
3098c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
3108c2ecf20Sopenharmony_ci	}, {
3118c2ecf20Sopenharmony_ci		.frequency = 150000000,
3128c2ecf20Sopenharmony_ci		.vcocap = 3,
3138c2ecf20Sopenharmony_ci		.filter = 5,
3148c2ecf20Sopenharmony_ci		.ichpmp = 5,
3158c2ecf20Sopenharmony_ci		.loadadj = 3,
3168c2ecf20Sopenharmony_ci		.tmds_termadj = 15,
3178c2ecf20Sopenharmony_ci		.tx_pu_value = 0x66 /* 0 */,
3188c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
3198c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
3208c2ecf20Sopenharmony_ci		.avdd10_level = 4,
3218c2ecf20Sopenharmony_ci		.avdd14_level = 4,
3228c2ecf20Sopenharmony_ci		.sparepll = 0x00, /* 0x34 */
3238c2ecf20Sopenharmony_ci		.drive_current = { 0x3a, 0x3a, 0x3a, 0x37 },
3248c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
3258c2ecf20Sopenharmony_ci	}, {
3268c2ecf20Sopenharmony_ci		.frequency = 300000000,
3278c2ecf20Sopenharmony_ci		.vcocap = 3,
3288c2ecf20Sopenharmony_ci		.filter = 5,
3298c2ecf20Sopenharmony_ci		.ichpmp = 5,
3308c2ecf20Sopenharmony_ci		.loadadj = 3,
3318c2ecf20Sopenharmony_ci		.tmds_termadj = 15,
3328c2ecf20Sopenharmony_ci		.tx_pu_value = 64,
3338c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
3348c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
3358c2ecf20Sopenharmony_ci		.avdd10_level = 4,
3368c2ecf20Sopenharmony_ci		.avdd14_level = 4,
3378c2ecf20Sopenharmony_ci		.sparepll = 0x34,
3388c2ecf20Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
3398c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
3408c2ecf20Sopenharmony_ci	}, {
3418c2ecf20Sopenharmony_ci		.frequency = 600000000,
3428c2ecf20Sopenharmony_ci		.vcocap = 3,
3438c2ecf20Sopenharmony_ci		.filter = 5,
3448c2ecf20Sopenharmony_ci		.ichpmp = 5,
3458c2ecf20Sopenharmony_ci		.loadadj = 3,
3468c2ecf20Sopenharmony_ci		.tmds_termadj = 12,
3478c2ecf20Sopenharmony_ci		.tx_pu_value = 96,
3488c2ecf20Sopenharmony_ci		.bg_temp_coef = 3,
3498c2ecf20Sopenharmony_ci		.bg_vref_level = 8,
3508c2ecf20Sopenharmony_ci		.avdd10_level = 4,
3518c2ecf20Sopenharmony_ci		.avdd14_level = 4,
3528c2ecf20Sopenharmony_ci		.sparepll = 0x34,
3538c2ecf20Sopenharmony_ci		.drive_current = { 0x3d, 0x3d, 0x3d, 0x33 },
3548c2ecf20Sopenharmony_ci		.preemphasis = { 0x00, 0x00, 0x00, 0x00 },
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistruct tegra_sor_regs {
3598c2ecf20Sopenharmony_ci	unsigned int head_state0;
3608c2ecf20Sopenharmony_ci	unsigned int head_state1;
3618c2ecf20Sopenharmony_ci	unsigned int head_state2;
3628c2ecf20Sopenharmony_ci	unsigned int head_state3;
3638c2ecf20Sopenharmony_ci	unsigned int head_state4;
3648c2ecf20Sopenharmony_ci	unsigned int head_state5;
3658c2ecf20Sopenharmony_ci	unsigned int pll0;
3668c2ecf20Sopenharmony_ci	unsigned int pll1;
3678c2ecf20Sopenharmony_ci	unsigned int pll2;
3688c2ecf20Sopenharmony_ci	unsigned int pll3;
3698c2ecf20Sopenharmony_ci	unsigned int dp_padctl0;
3708c2ecf20Sopenharmony_ci	unsigned int dp_padctl2;
3718c2ecf20Sopenharmony_ci};
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistruct tegra_sor_soc {
3748c2ecf20Sopenharmony_ci	bool supports_lvds;
3758c2ecf20Sopenharmony_ci	bool supports_hdmi;
3768c2ecf20Sopenharmony_ci	bool supports_dp;
3778c2ecf20Sopenharmony_ci	bool supports_audio;
3788c2ecf20Sopenharmony_ci	bool supports_hdcp;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	const struct tegra_sor_regs *regs;
3818c2ecf20Sopenharmony_ci	bool has_nvdisplay;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	const struct tegra_sor_hdmi_settings *settings;
3848c2ecf20Sopenharmony_ci	unsigned int num_settings;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	const u8 *xbar_cfg;
3878c2ecf20Sopenharmony_ci	const u8 *lane_map;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	const u8 (*voltage_swing)[4][4];
3908c2ecf20Sopenharmony_ci	const u8 (*pre_emphasis)[4][4];
3918c2ecf20Sopenharmony_ci	const u8 (*post_cursor)[4][4];
3928c2ecf20Sopenharmony_ci	const u8 (*tx_pu)[4][4];
3938c2ecf20Sopenharmony_ci};
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistruct tegra_sor;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistruct tegra_sor_ops {
3988c2ecf20Sopenharmony_ci	const char *name;
3998c2ecf20Sopenharmony_ci	int (*probe)(struct tegra_sor *sor);
4008c2ecf20Sopenharmony_ci	void (*audio_enable)(struct tegra_sor *sor);
4018c2ecf20Sopenharmony_ci	void (*audio_disable)(struct tegra_sor *sor);
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistruct tegra_sor {
4058c2ecf20Sopenharmony_ci	struct host1x_client client;
4068c2ecf20Sopenharmony_ci	struct tegra_output output;
4078c2ecf20Sopenharmony_ci	struct device *dev;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	const struct tegra_sor_soc *soc;
4108c2ecf20Sopenharmony_ci	void __iomem *regs;
4118c2ecf20Sopenharmony_ci	unsigned int index;
4128c2ecf20Sopenharmony_ci	unsigned int irq;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	struct reset_control *rst;
4158c2ecf20Sopenharmony_ci	struct clk *clk_parent;
4168c2ecf20Sopenharmony_ci	struct clk *clk_safe;
4178c2ecf20Sopenharmony_ci	struct clk *clk_out;
4188c2ecf20Sopenharmony_ci	struct clk *clk_pad;
4198c2ecf20Sopenharmony_ci	struct clk *clk_dp;
4208c2ecf20Sopenharmony_ci	struct clk *clk;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	u8 xbar_cfg[5];
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	struct drm_dp_link link;
4258c2ecf20Sopenharmony_ci	struct drm_dp_aux *aux;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	struct drm_info_list *debugfs_files;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	const struct tegra_sor_ops *ops;
4308c2ecf20Sopenharmony_ci	enum tegra_io_pad pad;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* for HDMI 2.0 */
4338c2ecf20Sopenharmony_ci	struct tegra_sor_hdmi_settings *settings;
4348c2ecf20Sopenharmony_ci	unsigned int num_settings;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	struct regulator *avdd_io_supply;
4378c2ecf20Sopenharmony_ci	struct regulator *vdd_pll_supply;
4388c2ecf20Sopenharmony_ci	struct regulator *hdmi_supply;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	struct delayed_work scdc;
4418c2ecf20Sopenharmony_ci	bool scdc_enabled;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	struct tegra_hda_format format;
4448c2ecf20Sopenharmony_ci};
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistruct tegra_sor_state {
4478c2ecf20Sopenharmony_ci	struct drm_connector_state base;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	unsigned int link_speed;
4508c2ecf20Sopenharmony_ci	unsigned long pclk;
4518c2ecf20Sopenharmony_ci	unsigned int bpc;
4528c2ecf20Sopenharmony_ci};
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic inline struct tegra_sor_state *
4558c2ecf20Sopenharmony_cito_sor_state(struct drm_connector_state *state)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	return container_of(state, struct tegra_sor_state, base);
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistruct tegra_sor_config {
4618c2ecf20Sopenharmony_ci	u32 bits_per_pixel;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	u32 active_polarity;
4648c2ecf20Sopenharmony_ci	u32 active_count;
4658c2ecf20Sopenharmony_ci	u32 tu_size;
4668c2ecf20Sopenharmony_ci	u32 active_frac;
4678c2ecf20Sopenharmony_ci	u32 watermark;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	u32 hblank_symbols;
4708c2ecf20Sopenharmony_ci	u32 vblank_symbols;
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic inline struct tegra_sor *
4748c2ecf20Sopenharmony_cihost1x_client_to_sor(struct host1x_client *client)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	return container_of(client, struct tegra_sor, client);
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic inline struct tegra_sor *to_sor(struct tegra_output *output)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	return container_of(output, struct tegra_sor, output);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic inline u32 tegra_sor_readl(struct tegra_sor *sor, unsigned int offset)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	u32 value = readl(sor->regs + (offset << 2));
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	trace_sor_readl(sor->dev, offset, value);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	return value;
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic inline void tegra_sor_writel(struct tegra_sor *sor, u32 value,
4948c2ecf20Sopenharmony_ci				    unsigned int offset)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	trace_sor_writel(sor->dev, offset, value);
4978c2ecf20Sopenharmony_ci	writel(value, sor->regs + (offset << 2));
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int tegra_sor_set_parent_clock(struct tegra_sor *sor, struct clk *parent)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	int err;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	clk_disable_unprepare(sor->clk);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	err = clk_set_parent(sor->clk_out, parent);
5078c2ecf20Sopenharmony_ci	if (err < 0)
5088c2ecf20Sopenharmony_ci		return err;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	err = clk_prepare_enable(sor->clk);
5118c2ecf20Sopenharmony_ci	if (err < 0)
5128c2ecf20Sopenharmony_ci		return err;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return 0;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistruct tegra_clk_sor_pad {
5188c2ecf20Sopenharmony_ci	struct clk_hw hw;
5198c2ecf20Sopenharmony_ci	struct tegra_sor *sor;
5208c2ecf20Sopenharmony_ci};
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic inline struct tegra_clk_sor_pad *to_pad(struct clk_hw *hw)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	return container_of(hw, struct tegra_clk_sor_pad, hw);
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic const char * const tegra_clk_sor_pad_parents[2][2] = {
5288c2ecf20Sopenharmony_ci	{ "pll_d_out0", "pll_dp" },
5298c2ecf20Sopenharmony_ci	{ "pll_d2_out0", "pll_dp" },
5308c2ecf20Sopenharmony_ci};
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci/*
5338c2ecf20Sopenharmony_ci * Implementing ->set_parent() here isn't really required because the parent
5348c2ecf20Sopenharmony_ci * will be explicitly selected in the driver code via the DP_CLK_SEL mux in
5358c2ecf20Sopenharmony_ci * the SOR_CLK_CNTRL register. This is primarily for compatibility with the
5368c2ecf20Sopenharmony_ci * Tegra186 and later SoC generations where the BPMP implements this clock
5378c2ecf20Sopenharmony_ci * and doesn't expose the mux via the common clock framework.
5388c2ecf20Sopenharmony_ci */
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic int tegra_clk_sor_pad_set_parent(struct clk_hw *hw, u8 index)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct tegra_clk_sor_pad *pad = to_pad(hw);
5438c2ecf20Sopenharmony_ci	struct tegra_sor *sor = pad->sor;
5448c2ecf20Sopenharmony_ci	u32 value;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
5478c2ecf20Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	switch (index) {
5508c2ecf20Sopenharmony_ci	case 0:
5518c2ecf20Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK;
5528c2ecf20Sopenharmony_ci		break;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	case 1:
5558c2ecf20Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
5568c2ecf20Sopenharmony_ci		break;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return 0;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic u8 tegra_clk_sor_pad_get_parent(struct clk_hw *hw)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct tegra_clk_sor_pad *pad = to_pad(hw);
5678c2ecf20Sopenharmony_ci	struct tegra_sor *sor = pad->sor;
5688c2ecf20Sopenharmony_ci	u8 parent = U8_MAX;
5698c2ecf20Sopenharmony_ci	u32 value;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	switch (value & SOR_CLK_CNTRL_DP_CLK_SEL_MASK) {
5748c2ecf20Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK:
5758c2ecf20Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK:
5768c2ecf20Sopenharmony_ci		parent = 0;
5778c2ecf20Sopenharmony_ci		break;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK:
5808c2ecf20Sopenharmony_ci	case SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK:
5818c2ecf20Sopenharmony_ci		parent = 1;
5828c2ecf20Sopenharmony_ci		break;
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	return parent;
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic const struct clk_ops tegra_clk_sor_pad_ops = {
5898c2ecf20Sopenharmony_ci	.set_parent = tegra_clk_sor_pad_set_parent,
5908c2ecf20Sopenharmony_ci	.get_parent = tegra_clk_sor_pad_get_parent,
5918c2ecf20Sopenharmony_ci};
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic struct clk *tegra_clk_sor_pad_register(struct tegra_sor *sor,
5948c2ecf20Sopenharmony_ci					      const char *name)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct tegra_clk_sor_pad *pad;
5978c2ecf20Sopenharmony_ci	struct clk_init_data init;
5988c2ecf20Sopenharmony_ci	struct clk *clk;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	pad = devm_kzalloc(sor->dev, sizeof(*pad), GFP_KERNEL);
6018c2ecf20Sopenharmony_ci	if (!pad)
6028c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	pad->sor = sor;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	init.name = name;
6078c2ecf20Sopenharmony_ci	init.flags = 0;
6088c2ecf20Sopenharmony_ci	init.parent_names = tegra_clk_sor_pad_parents[sor->index];
6098c2ecf20Sopenharmony_ci	init.num_parents = ARRAY_SIZE(tegra_clk_sor_pad_parents[sor->index]);
6108c2ecf20Sopenharmony_ci	init.ops = &tegra_clk_sor_pad_ops;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	pad->hw.init = &init;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	clk = devm_clk_register(sor->dev, &pad->hw);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return clk;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic void tegra_sor_filter_rates(struct tegra_sor *sor)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct drm_dp_link *link = &sor->link;
6228c2ecf20Sopenharmony_ci	unsigned int i;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	/* Tegra only supports RBR, HBR and HBR2 */
6258c2ecf20Sopenharmony_ci	for (i = 0; i < link->num_rates; i++) {
6268c2ecf20Sopenharmony_ci		switch (link->rates[i]) {
6278c2ecf20Sopenharmony_ci		case 1620000:
6288c2ecf20Sopenharmony_ci		case 2700000:
6298c2ecf20Sopenharmony_ci		case 5400000:
6308c2ecf20Sopenharmony_ci			break;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci		default:
6338c2ecf20Sopenharmony_ci			DRM_DEBUG_KMS("link rate %lu kHz not supported\n",
6348c2ecf20Sopenharmony_ci				      link->rates[i]);
6358c2ecf20Sopenharmony_ci			link->rates[i] = 0;
6368c2ecf20Sopenharmony_ci			break;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	drm_dp_link_update_rates(link);
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int tegra_sor_power_up_lanes(struct tegra_sor *sor, unsigned int lanes)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	unsigned long timeout;
6468c2ecf20Sopenharmony_ci	u32 value;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/*
6498c2ecf20Sopenharmony_ci	 * Clear or set the PD_TXD bit corresponding to each lane, depending
6508c2ecf20Sopenharmony_ci	 * on whether it is used or not.
6518c2ecf20Sopenharmony_ci	 */
6528c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (lanes <= 2)
6558c2ecf20Sopenharmony_ci		value &= ~(SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[3]) |
6568c2ecf20Sopenharmony_ci			   SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[2]));
6578c2ecf20Sopenharmony_ci	else
6588c2ecf20Sopenharmony_ci		value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[3]) |
6598c2ecf20Sopenharmony_ci			 SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[2]);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (lanes <= 1)
6628c2ecf20Sopenharmony_ci		value &= ~SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[1]);
6638c2ecf20Sopenharmony_ci	else
6648c2ecf20Sopenharmony_ci		value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[1]);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (lanes == 0)
6678c2ecf20Sopenharmony_ci		value &= ~SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[0]);
6688c2ecf20Sopenharmony_ci	else
6698c2ecf20Sopenharmony_ci		value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[0]);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* start lane sequencer */
6748c2ecf20Sopenharmony_ci	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
6758c2ecf20Sopenharmony_ci		SOR_LANE_SEQ_CTL_POWER_STATE_UP;
6768c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
6818c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
6828c2ecf20Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
6838c2ecf20Sopenharmony_ci			break;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci		usleep_range(250, 1000);
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
6898c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	return 0;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic int tegra_sor_power_down_lanes(struct tegra_sor *sor)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	unsigned long timeout;
6978c2ecf20Sopenharmony_ci	u32 value;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/* power down all lanes */
7008c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
7018c2ecf20Sopenharmony_ci	value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
7028c2ecf20Sopenharmony_ci		   SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
7038c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* start lane sequencer */
7068c2ecf20Sopenharmony_ci	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
7078c2ecf20Sopenharmony_ci		SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
7088c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
7138c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
7148c2ecf20Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
7158c2ecf20Sopenharmony_ci			break;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		usleep_range(25, 100);
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
7218c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	return 0;
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic void tegra_sor_dp_precharge(struct tegra_sor *sor, unsigned int lanes)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	u32 value;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/* pre-charge all used lanes */
7318c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (lanes <= 2)
7348c2ecf20Sopenharmony_ci		value &= ~(SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[3]) |
7358c2ecf20Sopenharmony_ci			   SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[2]));
7368c2ecf20Sopenharmony_ci	else
7378c2ecf20Sopenharmony_ci		value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[3]) |
7388c2ecf20Sopenharmony_ci			 SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[2]);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (lanes <= 1)
7418c2ecf20Sopenharmony_ci		value &= ~SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[1]);
7428c2ecf20Sopenharmony_ci	else
7438c2ecf20Sopenharmony_ci		value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[1]);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	if (lanes == 0)
7468c2ecf20Sopenharmony_ci		value &= ~SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[0]);
7478c2ecf20Sopenharmony_ci	else
7488c2ecf20Sopenharmony_ci		value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[0]);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	usleep_range(15, 100);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
7558c2ecf20Sopenharmony_ci	value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
7568c2ecf20Sopenharmony_ci		   SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0);
7578c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void tegra_sor_dp_term_calibrate(struct tegra_sor *sor)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	u32 mask = 0x08, adj = 0, value;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/* enable pad calibration logic */
7658c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
7668c2ecf20Sopenharmony_ci	value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
7678c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
7708c2ecf20Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERM;
7718c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	while (mask) {
7748c2ecf20Sopenharmony_ci		adj |= mask;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, sor->soc->regs->pll1);
7778c2ecf20Sopenharmony_ci		value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
7788c2ecf20Sopenharmony_ci		value |= SOR_PLL1_TMDS_TERMADJ(adj);
7798c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, value, sor->soc->regs->pll1);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci		usleep_range(100, 200);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, sor->soc->regs->pll1);
7848c2ecf20Sopenharmony_ci		if (value & SOR_PLL1_TERM_COMPOUT)
7858c2ecf20Sopenharmony_ci			adj &= ~mask;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		mask >>= 1;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
7918c2ecf20Sopenharmony_ci	value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
7928c2ecf20Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERMADJ(adj);
7938c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	/* disable pad calibration logic */
7968c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
7978c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_PAD_CAL_PD;
7988c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic int tegra_sor_dp_link_apply_training(struct drm_dp_link *link)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	struct tegra_sor *sor = container_of(link, struct tegra_sor, link);
8048c2ecf20Sopenharmony_ci	u32 voltage_swing = 0, pre_emphasis = 0, post_cursor = 0;
8058c2ecf20Sopenharmony_ci	const struct tegra_sor_soc *soc = sor->soc;
8068c2ecf20Sopenharmony_ci	u32 pattern = 0, tx_pu = 0, value;
8078c2ecf20Sopenharmony_ci	unsigned int i;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	for (value = 0, i = 0; i < link->lanes; i++) {
8108c2ecf20Sopenharmony_ci		u8 vs = link->train.request.voltage_swing[i];
8118c2ecf20Sopenharmony_ci		u8 pe = link->train.request.pre_emphasis[i];
8128c2ecf20Sopenharmony_ci		u8 pc = link->train.request.post_cursor[i];
8138c2ecf20Sopenharmony_ci		u8 shift = sor->soc->lane_map[i] << 3;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci		voltage_swing |= soc->voltage_swing[pc][vs][pe] << shift;
8168c2ecf20Sopenharmony_ci		pre_emphasis |= soc->pre_emphasis[pc][vs][pe] << shift;
8178c2ecf20Sopenharmony_ci		post_cursor |= soc->post_cursor[pc][vs][pe] << shift;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci		if (sor->soc->tx_pu[pc][vs][pe] > tx_pu)
8208c2ecf20Sopenharmony_ci			tx_pu = sor->soc->tx_pu[pc][vs][pe];
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci		switch (link->train.pattern) {
8238c2ecf20Sopenharmony_ci		case DP_TRAINING_PATTERN_DISABLE:
8248c2ecf20Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_GALIOS |
8258c2ecf20Sopenharmony_ci				SOR_DP_TPG_PATTERN_NONE;
8268c2ecf20Sopenharmony_ci			break;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci		case DP_TRAINING_PATTERN_1:
8298c2ecf20Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_NONE |
8308c2ecf20Sopenharmony_ci				SOR_DP_TPG_PATTERN_TRAIN1;
8318c2ecf20Sopenharmony_ci			break;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		case DP_TRAINING_PATTERN_2:
8348c2ecf20Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_NONE |
8358c2ecf20Sopenharmony_ci				SOR_DP_TPG_PATTERN_TRAIN2;
8368c2ecf20Sopenharmony_ci			break;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci		case DP_TRAINING_PATTERN_3:
8398c2ecf20Sopenharmony_ci			value = SOR_DP_TPG_SCRAMBLER_NONE |
8408c2ecf20Sopenharmony_ci				SOR_DP_TPG_PATTERN_TRAIN3;
8418c2ecf20Sopenharmony_ci			break;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci		default:
8448c2ecf20Sopenharmony_ci			return -EINVAL;
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci		if (link->caps.channel_coding)
8488c2ecf20Sopenharmony_ci			value |= SOR_DP_TPG_CHANNEL_CODING;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		pattern = pattern << 8 | value;
8518c2ecf20Sopenharmony_ci	}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, voltage_swing, SOR_LANE_DRIVE_CURRENT0);
8548c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, pre_emphasis, SOR_LANE_PREEMPHASIS0);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	if (link->caps.tps3_supported)
8578c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, post_cursor, SOR_LANE_POSTCURSOR0);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, pattern, SOR_DP_TPG);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
8628c2ecf20Sopenharmony_ci	value &= ~SOR_DP_PADCTL_TX_PU_MASK;
8638c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU_ENABLE;
8648c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU(tx_pu);
8658c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	usleep_range(20, 100);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	return 0;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic int tegra_sor_dp_link_configure(struct drm_dp_link *link)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct tegra_sor *sor = container_of(link, struct tegra_sor, link);
8758c2ecf20Sopenharmony_ci	unsigned int rate, lanes;
8768c2ecf20Sopenharmony_ci	u32 value;
8778c2ecf20Sopenharmony_ci	int err;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	rate = drm_dp_link_rate_to_bw_code(link->rate);
8808c2ecf20Sopenharmony_ci	lanes = link->lanes;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* configure link speed and lane count */
8838c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
8848c2ecf20Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
8858c2ecf20Sopenharmony_ci	value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate);
8868c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
8898c2ecf20Sopenharmony_ci	value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
8908c2ecf20Sopenharmony_ci	value |= SOR_DP_LINKCTL_LANE_COUNT(lanes);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	if (link->caps.enhanced_framing)
8938c2ecf20Sopenharmony_ci		value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	usleep_range(400, 1000);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	/* configure load pulse position adjustment */
9008c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
9018c2ecf20Sopenharmony_ci	value &= ~SOR_PLL1_LOADADJ_MASK;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	switch (rate) {
9048c2ecf20Sopenharmony_ci	case DP_LINK_BW_1_62:
9058c2ecf20Sopenharmony_ci		value |= SOR_PLL1_LOADADJ(0x3);
9068c2ecf20Sopenharmony_ci		break;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	case DP_LINK_BW_2_7:
9098c2ecf20Sopenharmony_ci		value |= SOR_PLL1_LOADADJ(0x4);
9108c2ecf20Sopenharmony_ci		break;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	case DP_LINK_BW_5_4:
9138c2ecf20Sopenharmony_ci		value |= SOR_PLL1_LOADADJ(0x6);
9148c2ecf20Sopenharmony_ci		break;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* use alternate scrambler reset for eDP */
9208c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (link->edp == 0)
9238c2ecf20Sopenharmony_ci		value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
9248c2ecf20Sopenharmony_ci	else
9258c2ecf20Sopenharmony_ci		value |= SOR_DP_SPARE_PANEL_INTERNAL;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	err = tegra_sor_power_down_lanes(sor);
9308c2ecf20Sopenharmony_ci	if (err < 0) {
9318c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power down lanes: %d\n", err);
9328c2ecf20Sopenharmony_ci		return err;
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	/* power up and pre-charge lanes */
9368c2ecf20Sopenharmony_ci	err = tegra_sor_power_up_lanes(sor, lanes);
9378c2ecf20Sopenharmony_ci	if (err < 0) {
9388c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power up %u lane%s: %d\n",
9398c2ecf20Sopenharmony_ci			lanes, (lanes != 1) ? "s" : "", err);
9408c2ecf20Sopenharmony_ci		return err;
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	tegra_sor_dp_precharge(sor, lanes);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	return 0;
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic const struct drm_dp_link_ops tegra_sor_dp_link_ops = {
9498c2ecf20Sopenharmony_ci	.apply_training = tegra_sor_dp_link_apply_training,
9508c2ecf20Sopenharmony_ci	.configure = tegra_sor_dp_link_configure,
9518c2ecf20Sopenharmony_ci};
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_cistatic void tegra_sor_super_update(struct tegra_sor *sor)
9548c2ecf20Sopenharmony_ci{
9558c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_SUPER_STATE0);
9568c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 1, SOR_SUPER_STATE0);
9578c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_SUPER_STATE0);
9588c2ecf20Sopenharmony_ci}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic void tegra_sor_update(struct tegra_sor *sor)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE0);
9638c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 1, SOR_STATE0);
9648c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE0);
9658c2ecf20Sopenharmony_ci}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_cistatic int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	u32 value;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWM_DIV);
9728c2ecf20Sopenharmony_ci	value &= ~SOR_PWM_DIV_MASK;
9738c2ecf20Sopenharmony_ci	value |= 0x400; /* period */
9748c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWM_DIV);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWM_CTL);
9778c2ecf20Sopenharmony_ci	value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK;
9788c2ecf20Sopenharmony_ci	value |= 0x400; /* duty cycle */
9798c2ecf20Sopenharmony_ci	value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */
9808c2ecf20Sopenharmony_ci	value |= SOR_PWM_CTL_TRIGGER;
9818c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWM_CTL);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
9868c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWM_CTL);
9878c2ecf20Sopenharmony_ci		if ((value & SOR_PWM_CTL_TRIGGER) == 0)
9888c2ecf20Sopenharmony_ci			return 0;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci		usleep_range(25, 100);
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic int tegra_sor_attach(struct tegra_sor *sor)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	unsigned long value, timeout;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	/* wake up in normal mode */
10018c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
10028c2ecf20Sopenharmony_ci	value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE;
10038c2ecf20Sopenharmony_ci	value |= SOR_SUPER_STATE_MODE_NORMAL;
10048c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
10058c2ecf20Sopenharmony_ci	tegra_sor_super_update(sor);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* attach */
10088c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
10098c2ecf20Sopenharmony_ci	value |= SOR_SUPER_STATE_ATTACHED;
10108c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
10118c2ecf20Sopenharmony_ci	tegra_sor_super_update(sor);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
10168c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_TEST);
10178c2ecf20Sopenharmony_ci		if ((value & SOR_TEST_ATTACHED) != 0)
10188c2ecf20Sopenharmony_ci			return 0;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		usleep_range(25, 100);
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic int tegra_sor_wakeup(struct tegra_sor *sor)
10278c2ecf20Sopenharmony_ci{
10288c2ecf20Sopenharmony_ci	unsigned long value, timeout;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	/* wait for head to wake up */
10338c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
10348c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_TEST);
10358c2ecf20Sopenharmony_ci		value &= SOR_TEST_HEAD_MODE_MASK;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci		if (value == SOR_TEST_HEAD_MODE_AWAKE)
10388c2ecf20Sopenharmony_ci			return 0;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci		usleep_range(25, 100);
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_cistatic int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout)
10478c2ecf20Sopenharmony_ci{
10488c2ecf20Sopenharmony_ci	u32 value;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWR);
10518c2ecf20Sopenharmony_ci	value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU;
10528c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWR);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
10578c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWR);
10588c2ecf20Sopenharmony_ci		if ((value & SOR_PWR_TRIGGER) == 0)
10598c2ecf20Sopenharmony_ci			return 0;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci		usleep_range(25, 100);
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistruct tegra_sor_params {
10688c2ecf20Sopenharmony_ci	/* number of link clocks per line */
10698c2ecf20Sopenharmony_ci	unsigned int num_clocks;
10708c2ecf20Sopenharmony_ci	/* ratio between input and output */
10718c2ecf20Sopenharmony_ci	u64 ratio;
10728c2ecf20Sopenharmony_ci	/* precision factor */
10738c2ecf20Sopenharmony_ci	u64 precision;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	unsigned int active_polarity;
10768c2ecf20Sopenharmony_ci	unsigned int active_count;
10778c2ecf20Sopenharmony_ci	unsigned int active_frac;
10788c2ecf20Sopenharmony_ci	unsigned int tu_size;
10798c2ecf20Sopenharmony_ci	unsigned int error;
10808c2ecf20Sopenharmony_ci};
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_cistatic int tegra_sor_compute_params(struct tegra_sor *sor,
10838c2ecf20Sopenharmony_ci				    struct tegra_sor_params *params,
10848c2ecf20Sopenharmony_ci				    unsigned int tu_size)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	u64 active_sym, active_count, frac, approx;
10878c2ecf20Sopenharmony_ci	u32 active_polarity, active_frac = 0;
10888c2ecf20Sopenharmony_ci	const u64 f = params->precision;
10898c2ecf20Sopenharmony_ci	s64 error;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	active_sym = params->ratio * tu_size;
10928c2ecf20Sopenharmony_ci	active_count = div_u64(active_sym, f) * f;
10938c2ecf20Sopenharmony_ci	frac = active_sym - active_count;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/* fraction < 0.5 */
10968c2ecf20Sopenharmony_ci	if (frac >= (f / 2)) {
10978c2ecf20Sopenharmony_ci		active_polarity = 1;
10988c2ecf20Sopenharmony_ci		frac = f - frac;
10998c2ecf20Sopenharmony_ci	} else {
11008c2ecf20Sopenharmony_ci		active_polarity = 0;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	if (frac != 0) {
11048c2ecf20Sopenharmony_ci		frac = div_u64(f * f,  frac); /* 1/fraction */
11058c2ecf20Sopenharmony_ci		if (frac <= (15 * f)) {
11068c2ecf20Sopenharmony_ci			active_frac = div_u64(frac, f);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci			/* round up */
11098c2ecf20Sopenharmony_ci			if (active_polarity)
11108c2ecf20Sopenharmony_ci				active_frac++;
11118c2ecf20Sopenharmony_ci		} else {
11128c2ecf20Sopenharmony_ci			active_frac = active_polarity ? 1 : 15;
11138c2ecf20Sopenharmony_ci		}
11148c2ecf20Sopenharmony_ci	}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (active_frac == 1)
11178c2ecf20Sopenharmony_ci		active_polarity = 0;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	if (active_polarity == 1) {
11208c2ecf20Sopenharmony_ci		if (active_frac) {
11218c2ecf20Sopenharmony_ci			approx = active_count + (active_frac * (f - 1)) * f;
11228c2ecf20Sopenharmony_ci			approx = div_u64(approx, active_frac * f);
11238c2ecf20Sopenharmony_ci		} else {
11248c2ecf20Sopenharmony_ci			approx = active_count + f;
11258c2ecf20Sopenharmony_ci		}
11268c2ecf20Sopenharmony_ci	} else {
11278c2ecf20Sopenharmony_ci		if (active_frac)
11288c2ecf20Sopenharmony_ci			approx = active_count + div_u64(f, active_frac);
11298c2ecf20Sopenharmony_ci		else
11308c2ecf20Sopenharmony_ci			approx = active_count;
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	error = div_s64(active_sym - approx, tu_size);
11348c2ecf20Sopenharmony_ci	error *= params->num_clocks;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	if (error <= 0 && abs(error) < params->error) {
11378c2ecf20Sopenharmony_ci		params->active_count = div_u64(active_count, f);
11388c2ecf20Sopenharmony_ci		params->active_polarity = active_polarity;
11398c2ecf20Sopenharmony_ci		params->active_frac = active_frac;
11408c2ecf20Sopenharmony_ci		params->error = abs(error);
11418c2ecf20Sopenharmony_ci		params->tu_size = tu_size;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci		if (error == 0)
11448c2ecf20Sopenharmony_ci			return true;
11458c2ecf20Sopenharmony_ci	}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return false;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic int tegra_sor_compute_config(struct tegra_sor *sor,
11518c2ecf20Sopenharmony_ci				    const struct drm_display_mode *mode,
11528c2ecf20Sopenharmony_ci				    struct tegra_sor_config *config,
11538c2ecf20Sopenharmony_ci				    struct drm_dp_link *link)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	const u64 f = 100000, link_rate = link->rate * 1000;
11568c2ecf20Sopenharmony_ci	const u64 pclk = (u64)mode->clock * 1000;
11578c2ecf20Sopenharmony_ci	u64 input, output, watermark, num;
11588c2ecf20Sopenharmony_ci	struct tegra_sor_params params;
11598c2ecf20Sopenharmony_ci	u32 num_syms_per_line;
11608c2ecf20Sopenharmony_ci	unsigned int i;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	if (!link_rate || !link->lanes || !pclk || !config->bits_per_pixel)
11638c2ecf20Sopenharmony_ci		return -EINVAL;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	input = pclk * config->bits_per_pixel;
11668c2ecf20Sopenharmony_ci	output = link_rate * 8 * link->lanes;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (input >= output)
11698c2ecf20Sopenharmony_ci		return -ERANGE;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	memset(&params, 0, sizeof(params));
11728c2ecf20Sopenharmony_ci	params.ratio = div64_u64(input * f, output);
11738c2ecf20Sopenharmony_ci	params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk);
11748c2ecf20Sopenharmony_ci	params.precision = f;
11758c2ecf20Sopenharmony_ci	params.error = 64 * f;
11768c2ecf20Sopenharmony_ci	params.tu_size = 64;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	for (i = params.tu_size; i >= 32; i--)
11798c2ecf20Sopenharmony_ci		if (tegra_sor_compute_params(sor, &params, i))
11808c2ecf20Sopenharmony_ci			break;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	if (params.active_frac == 0) {
11838c2ecf20Sopenharmony_ci		config->active_polarity = 0;
11848c2ecf20Sopenharmony_ci		config->active_count = params.active_count;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci		if (!params.active_polarity)
11878c2ecf20Sopenharmony_ci			config->active_count--;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci		config->tu_size = params.tu_size;
11908c2ecf20Sopenharmony_ci		config->active_frac = 1;
11918c2ecf20Sopenharmony_ci	} else {
11928c2ecf20Sopenharmony_ci		config->active_polarity = params.active_polarity;
11938c2ecf20Sopenharmony_ci		config->active_count = params.active_count;
11948c2ecf20Sopenharmony_ci		config->active_frac = params.active_frac;
11958c2ecf20Sopenharmony_ci		config->tu_size = params.tu_size;
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	dev_dbg(sor->dev,
11998c2ecf20Sopenharmony_ci		"polarity: %d active count: %d tu size: %d active frac: %d\n",
12008c2ecf20Sopenharmony_ci		config->active_polarity, config->active_count,
12018c2ecf20Sopenharmony_ci		config->tu_size, config->active_frac);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	watermark = params.ratio * config->tu_size * (f - params.ratio);
12048c2ecf20Sopenharmony_ci	watermark = div_u64(watermark, f);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	watermark = div_u64(watermark + params.error, f);
12078c2ecf20Sopenharmony_ci	config->watermark = watermark + (config->bits_per_pixel / 8) + 2;
12088c2ecf20Sopenharmony_ci	num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) *
12098c2ecf20Sopenharmony_ci			    (link->lanes * 8);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	if (config->watermark > 30) {
12128c2ecf20Sopenharmony_ci		config->watermark = 30;
12138c2ecf20Sopenharmony_ci		dev_err(sor->dev,
12148c2ecf20Sopenharmony_ci			"unable to compute TU size, forcing watermark to %u\n",
12158c2ecf20Sopenharmony_ci			config->watermark);
12168c2ecf20Sopenharmony_ci	} else if (config->watermark > num_syms_per_line) {
12178c2ecf20Sopenharmony_ci		config->watermark = num_syms_per_line;
12188c2ecf20Sopenharmony_ci		dev_err(sor->dev, "watermark too high, forcing to %u\n",
12198c2ecf20Sopenharmony_ci			config->watermark);
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* compute the number of symbols per horizontal blanking interval */
12238c2ecf20Sopenharmony_ci	num = ((mode->htotal - mode->hdisplay) - 7) * link_rate;
12248c2ecf20Sopenharmony_ci	config->hblank_symbols = div_u64(num, pclk);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	if (link->caps.enhanced_framing)
12278c2ecf20Sopenharmony_ci		config->hblank_symbols -= 3;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	config->hblank_symbols -= 12 / link->lanes;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	/* compute the number of symbols per vertical blanking interval */
12328c2ecf20Sopenharmony_ci	num = (mode->hdisplay - 25) * link_rate;
12338c2ecf20Sopenharmony_ci	config->vblank_symbols = div_u64(num, pclk);
12348c2ecf20Sopenharmony_ci	config->vblank_symbols -= 36 / link->lanes + 4;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols,
12378c2ecf20Sopenharmony_ci		config->vblank_symbols);
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	return 0;
12408c2ecf20Sopenharmony_ci}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_cistatic void tegra_sor_apply_config(struct tegra_sor *sor,
12438c2ecf20Sopenharmony_ci				   const struct tegra_sor_config *config)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	u32 value;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
12488c2ecf20Sopenharmony_ci	value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
12498c2ecf20Sopenharmony_ci	value |= SOR_DP_LINKCTL_TU_SIZE(config->tu_size);
12508c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_CONFIG0);
12538c2ecf20Sopenharmony_ci	value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
12548c2ecf20Sopenharmony_ci	value |= SOR_DP_CONFIG_WATERMARK(config->watermark);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
12578c2ecf20Sopenharmony_ci	value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config->active_count);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
12608c2ecf20Sopenharmony_ci	value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config->active_frac);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	if (config->active_polarity)
12638c2ecf20Sopenharmony_ci		value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
12648c2ecf20Sopenharmony_ci	else
12658c2ecf20Sopenharmony_ci		value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
12688c2ecf20Sopenharmony_ci	value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE;
12698c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_CONFIG0);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
12728c2ecf20Sopenharmony_ci	value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
12738c2ecf20Sopenharmony_ci	value |= config->hblank_symbols & 0xffff;
12748c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
12778c2ecf20Sopenharmony_ci	value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
12788c2ecf20Sopenharmony_ci	value |= config->vblank_symbols & 0xffff;
12798c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
12808c2ecf20Sopenharmony_ci}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_cistatic void tegra_sor_mode_set(struct tegra_sor *sor,
12838c2ecf20Sopenharmony_ci			       const struct drm_display_mode *mode,
12848c2ecf20Sopenharmony_ci			       struct tegra_sor_state *state)
12858c2ecf20Sopenharmony_ci{
12868c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc);
12878c2ecf20Sopenharmony_ci	unsigned int vbe, vse, hbe, hse, vbs, hbs;
12888c2ecf20Sopenharmony_ci	u32 value;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
12918c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_PIXELDEPTH_MASK;
12928c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
12938c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_OWNER_MASK;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	value |= SOR_STATE_ASY_CRC_MODE_COMPLETE |
12968c2ecf20Sopenharmony_ci		 SOR_STATE_ASY_OWNER(dc->pipe + 1);
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
12998c2ecf20Sopenharmony_ci		value &= ~SOR_STATE_ASY_HSYNCPOL;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
13028c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_HSYNCPOL;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
13058c2ecf20Sopenharmony_ci		value &= ~SOR_STATE_ASY_VSYNCPOL;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
13088c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_VSYNCPOL;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	switch (state->bpc) {
13118c2ecf20Sopenharmony_ci	case 16:
13128c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_48_444;
13138c2ecf20Sopenharmony_ci		break;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	case 12:
13168c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_36_444;
13178c2ecf20Sopenharmony_ci		break;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	case 10:
13208c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_30_444;
13218c2ecf20Sopenharmony_ci		break;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	case 8:
13248c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
13258c2ecf20Sopenharmony_ci		break;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	case 6:
13288c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444;
13298c2ecf20Sopenharmony_ci		break;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	default:
13328c2ecf20Sopenharmony_ci		value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
13338c2ecf20Sopenharmony_ci		break;
13348c2ecf20Sopenharmony_ci	}
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	/*
13398c2ecf20Sopenharmony_ci	 * TODO: The video timing programming below doesn't seem to match the
13408c2ecf20Sopenharmony_ci	 * register definitions.
13418c2ecf20Sopenharmony_ci	 */
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
13448c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state1 + dc->pipe);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	/* sync end = sync width - 1 */
13478c2ecf20Sopenharmony_ci	vse = mode->vsync_end - mode->vsync_start - 1;
13488c2ecf20Sopenharmony_ci	hse = mode->hsync_end - mode->hsync_start - 1;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
13518c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state2 + dc->pipe);
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	/* blank end = sync end + back porch */
13548c2ecf20Sopenharmony_ci	vbe = vse + (mode->vtotal - mode->vsync_end);
13558c2ecf20Sopenharmony_ci	hbe = hse + (mode->htotal - mode->hsync_end);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
13588c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state3 + dc->pipe);
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	/* blank start = blank end + active */
13618c2ecf20Sopenharmony_ci	vbs = vbe + mode->vdisplay;
13628c2ecf20Sopenharmony_ci	hbs = hbe + mode->hdisplay;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
13658c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state4 + dc->pipe);
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	/* XXX interlacing support */
13688c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0x001, sor->soc->regs->head_state5 + dc->pipe);
13698c2ecf20Sopenharmony_ci}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_cistatic int tegra_sor_detach(struct tegra_sor *sor)
13728c2ecf20Sopenharmony_ci{
13738c2ecf20Sopenharmony_ci	unsigned long value, timeout;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	/* switch to safe mode */
13768c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
13778c2ecf20Sopenharmony_ci	value &= ~SOR_SUPER_STATE_MODE_NORMAL;
13788c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
13798c2ecf20Sopenharmony_ci	tegra_sor_super_update(sor);
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
13848c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWR);
13858c2ecf20Sopenharmony_ci		if (value & SOR_PWR_MODE_SAFE)
13868c2ecf20Sopenharmony_ci			break;
13878c2ecf20Sopenharmony_ci	}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	if ((value & SOR_PWR_MODE_SAFE) == 0)
13908c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	/* go to sleep */
13938c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
13948c2ecf20Sopenharmony_ci	value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
13958c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
13968c2ecf20Sopenharmony_ci	tegra_sor_super_update(sor);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	/* detach */
13998c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
14008c2ecf20Sopenharmony_ci	value &= ~SOR_SUPER_STATE_ATTACHED;
14018c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
14028c2ecf20Sopenharmony_ci	tegra_sor_super_update(sor);
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
14078c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_TEST);
14088c2ecf20Sopenharmony_ci		if ((value & SOR_TEST_ATTACHED) == 0)
14098c2ecf20Sopenharmony_ci			break;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci		usleep_range(25, 100);
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	if ((value & SOR_TEST_ATTACHED) != 0)
14158c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	return 0;
14188c2ecf20Sopenharmony_ci}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_cistatic int tegra_sor_power_down(struct tegra_sor *sor)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	unsigned long value, timeout;
14238c2ecf20Sopenharmony_ci	int err;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_PWR);
14268c2ecf20Sopenharmony_ci	value &= ~SOR_PWR_NORMAL_STATE_PU;
14278c2ecf20Sopenharmony_ci	value |= SOR_PWR_TRIGGER;
14288c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_PWR);
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
14338c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_PWR);
14348c2ecf20Sopenharmony_ci		if ((value & SOR_PWR_TRIGGER) == 0)
14358c2ecf20Sopenharmony_ci			return 0;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci		usleep_range(25, 100);
14388c2ecf20Sopenharmony_ci	}
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	if ((value & SOR_PWR_TRIGGER) != 0)
14418c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	/* switch to safe parent clock */
14448c2ecf20Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
14458c2ecf20Sopenharmony_ci	if (err < 0) {
14468c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
14478c2ecf20Sopenharmony_ci		return err;
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
14518c2ecf20Sopenharmony_ci	value |= SOR_PLL2_PORT_POWERDOWN;
14528c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	usleep_range(20, 100);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
14578c2ecf20Sopenharmony_ci	value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR;
14588c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
14618c2ecf20Sopenharmony_ci	value |= SOR_PLL2_SEQ_PLLCAPPD;
14628c2ecf20Sopenharmony_ci	value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
14638c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	usleep_range(20, 100);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	return 0;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cistatic int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
14718c2ecf20Sopenharmony_ci{
14728c2ecf20Sopenharmony_ci	u32 value;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
14778c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_CRCA);
14788c2ecf20Sopenharmony_ci		if (value & SOR_CRCA_VALID)
14798c2ecf20Sopenharmony_ci			return 0;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci		usleep_range(100, 200);
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic int tegra_sor_show_crc(struct seq_file *s, void *data)
14888c2ecf20Sopenharmony_ci{
14898c2ecf20Sopenharmony_ci	struct drm_info_node *node = s->private;
14908c2ecf20Sopenharmony_ci	struct tegra_sor *sor = node->info_ent->data;
14918c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = sor->output.encoder.crtc;
14928c2ecf20Sopenharmony_ci	struct drm_device *drm = node->minor->dev;
14938c2ecf20Sopenharmony_ci	int err = 0;
14948c2ecf20Sopenharmony_ci	u32 value;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	drm_modeset_lock_all(drm);
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	if (!crtc || !crtc->state->active) {
14998c2ecf20Sopenharmony_ci		err = -EBUSY;
15008c2ecf20Sopenharmony_ci		goto unlock;
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
15048c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
15058c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
15088c2ecf20Sopenharmony_ci	value |= SOR_CRC_CNTRL_ENABLE;
15098c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_TEST);
15128c2ecf20Sopenharmony_ci	value &= ~SOR_TEST_CRC_POST_SERIALIZE;
15138c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_TEST);
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	err = tegra_sor_crc_wait(sor, 100);
15168c2ecf20Sopenharmony_ci	if (err < 0)
15178c2ecf20Sopenharmony_ci		goto unlock;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, SOR_CRCA_RESET, SOR_CRCA);
15208c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CRCB);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	seq_printf(s, "%08x\n", value);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ciunlock:
15258c2ecf20Sopenharmony_ci	drm_modeset_unlock_all(drm);
15268c2ecf20Sopenharmony_ci	return err;
15278c2ecf20Sopenharmony_ci}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_cistatic const struct debugfs_reg32 tegra_sor_regs[] = {
15328c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CTXSW),
15338c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SUPER_STATE0),
15348c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SUPER_STATE1),
15358c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_STATE0),
15368c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_STATE1),
15378c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE0(0)),
15388c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE0(1)),
15398c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE1(0)),
15408c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE1(1)),
15418c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE2(0)),
15428c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE2(1)),
15438c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE3(0)),
15448c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE3(1)),
15458c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE4(0)),
15468c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE4(1)),
15478c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE5(0)),
15488c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_HEAD_STATE5(1)),
15498c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CRC_CNTRL),
15508c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_DEBUG_MVID),
15518c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CLK_CNTRL),
15528c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CAP),
15538c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_PWR),
15548c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_TEST),
15558c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL0),
15568c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL1),
15578c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL2),
15588c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_PLL3),
15598c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CSTM),
15608c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LVDS),
15618c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CRCA),
15628c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CRCB),
15638c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_BLANK),
15648c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_CTL),
15658c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_SEQ_CTL),
15668c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(0)),
15678c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(1)),
15688c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(2)),
15698c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(3)),
15708c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(4)),
15718c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(5)),
15728c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(6)),
15738c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(7)),
15748c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(8)),
15758c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(9)),
15768c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(10)),
15778c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(11)),
15788c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(12)),
15798c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(13)),
15808c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(14)),
15818c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_SEQ_INST(15)),
15828c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_PWM_DIV),
15838c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_PWM_CTL),
15848c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_A0),
15858c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_A1),
15868c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_B0),
15878c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_VCRC_B1),
15888c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_A0),
15898c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_A1),
15908c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_B0),
15918c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_CCRC_B1),
15928c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_A0),
15938c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_A1),
15948c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_B0),
15958c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_EDATA_B1),
15968c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_A0),
15978c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_A1),
15988c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_B0),
15998c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_COUNT_B1),
16008c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_A0),
16018c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_A1),
16028c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_B0),
16038c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DEBUG_B1),
16048c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_TRIG),
16058c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_MSCHECK),
16068c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_XBAR_CTRL),
16078c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_XBAR_POL),
16088c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LINKCTL0),
16098c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LINKCTL1),
16108c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT0),
16118c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT1),
16128c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT0),
16138c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT1),
16148c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_PREEMPHASIS0),
16158c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_PREEMPHASIS1),
16168c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS0),
16178c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS1),
16188c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_POSTCURSOR0),
16198c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_LANE_POSTCURSOR1),
16208c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_CONFIG0),
16218c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_CONFIG1),
16228c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_MN0),
16238c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_MN1),
16248c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_PADCTL0),
16258c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_PADCTL1),
16268c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_PADCTL2),
16278c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_DEBUG0),
16288c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_DEBUG1),
16298c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_SPARE0),
16308c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_SPARE1),
16318c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_AUDIO_CTRL),
16328c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_AUDIO_HBLANK_SYMBOLS),
16338c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_AUDIO_VBLANK_SYMBOLS),
16348c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_HEADER),
16358c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK0),
16368c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK1),
16378c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK2),
16388c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK3),
16398c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK4),
16408c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK5),
16418c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK6),
16428c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_TPG),
16438c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_TPG_CONFIG),
16448c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LQ_CSTM0),
16458c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LQ_CSTM1),
16468c2ecf20Sopenharmony_ci	DEBUGFS_REG32(SOR_DP_LQ_CSTM2),
16478c2ecf20Sopenharmony_ci};
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_cistatic int tegra_sor_show_regs(struct seq_file *s, void *data)
16508c2ecf20Sopenharmony_ci{
16518c2ecf20Sopenharmony_ci	struct drm_info_node *node = s->private;
16528c2ecf20Sopenharmony_ci	struct tegra_sor *sor = node->info_ent->data;
16538c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = sor->output.encoder.crtc;
16548c2ecf20Sopenharmony_ci	struct drm_device *drm = node->minor->dev;
16558c2ecf20Sopenharmony_ci	unsigned int i;
16568c2ecf20Sopenharmony_ci	int err = 0;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	drm_modeset_lock_all(drm);
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if (!crtc || !crtc->state->active) {
16618c2ecf20Sopenharmony_ci		err = -EBUSY;
16628c2ecf20Sopenharmony_ci		goto unlock;
16638c2ecf20Sopenharmony_ci	}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tegra_sor_regs); i++) {
16668c2ecf20Sopenharmony_ci		unsigned int offset = tegra_sor_regs[i].offset;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci		seq_printf(s, "%-38s %#05x %08x\n", tegra_sor_regs[i].name,
16698c2ecf20Sopenharmony_ci			   offset, tegra_sor_readl(sor, offset));
16708c2ecf20Sopenharmony_ci	}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ciunlock:
16738c2ecf20Sopenharmony_ci	drm_modeset_unlock_all(drm);
16748c2ecf20Sopenharmony_ci	return err;
16758c2ecf20Sopenharmony_ci}
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_cistatic const struct drm_info_list debugfs_files[] = {
16788c2ecf20Sopenharmony_ci	{ "crc", tegra_sor_show_crc, 0, NULL },
16798c2ecf20Sopenharmony_ci	{ "regs", tegra_sor_show_regs, 0, NULL },
16808c2ecf20Sopenharmony_ci};
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_cistatic int tegra_sor_late_register(struct drm_connector *connector)
16838c2ecf20Sopenharmony_ci{
16848c2ecf20Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
16858c2ecf20Sopenharmony_ci	unsigned int i, count = ARRAY_SIZE(debugfs_files);
16868c2ecf20Sopenharmony_ci	struct drm_minor *minor = connector->dev->primary;
16878c2ecf20Sopenharmony_ci	struct dentry *root = connector->debugfs_entry;
16888c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
16918c2ecf20Sopenharmony_ci				     GFP_KERNEL);
16928c2ecf20Sopenharmony_ci	if (!sor->debugfs_files)
16938c2ecf20Sopenharmony_ci		return -ENOMEM;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
16968c2ecf20Sopenharmony_ci		sor->debugfs_files[i].data = sor;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	drm_debugfs_create_files(sor->debugfs_files, count, root, minor);
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	return 0;
17018c2ecf20Sopenharmony_ci}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_cistatic void tegra_sor_early_unregister(struct drm_connector *connector)
17048c2ecf20Sopenharmony_ci{
17058c2ecf20Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
17068c2ecf20Sopenharmony_ci	unsigned int count = ARRAY_SIZE(debugfs_files);
17078c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	drm_debugfs_remove_files(sor->debugfs_files, count,
17108c2ecf20Sopenharmony_ci				 connector->dev->primary);
17118c2ecf20Sopenharmony_ci	kfree(sor->debugfs_files);
17128c2ecf20Sopenharmony_ci	sor->debugfs_files = NULL;
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_cistatic void tegra_sor_connector_reset(struct drm_connector *connector)
17168c2ecf20Sopenharmony_ci{
17178c2ecf20Sopenharmony_ci	struct tegra_sor_state *state;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
17208c2ecf20Sopenharmony_ci	if (!state)
17218c2ecf20Sopenharmony_ci		return;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	if (connector->state) {
17248c2ecf20Sopenharmony_ci		__drm_atomic_helper_connector_destroy_state(connector->state);
17258c2ecf20Sopenharmony_ci		kfree(connector->state);
17268c2ecf20Sopenharmony_ci	}
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	__drm_atomic_helper_connector_reset(connector, &state->base);
17298c2ecf20Sopenharmony_ci}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_cistatic enum drm_connector_status
17328c2ecf20Sopenharmony_citegra_sor_connector_detect(struct drm_connector *connector, bool force)
17338c2ecf20Sopenharmony_ci{
17348c2ecf20Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
17358c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	if (sor->aux)
17388c2ecf20Sopenharmony_ci		return drm_dp_aux_detect(sor->aux);
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	return tegra_output_connector_detect(connector, force);
17418c2ecf20Sopenharmony_ci}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_cistatic struct drm_connector_state *
17448c2ecf20Sopenharmony_citegra_sor_connector_duplicate_state(struct drm_connector *connector)
17458c2ecf20Sopenharmony_ci{
17468c2ecf20Sopenharmony_ci	struct tegra_sor_state *state = to_sor_state(connector->state);
17478c2ecf20Sopenharmony_ci	struct tegra_sor_state *copy;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
17508c2ecf20Sopenharmony_ci	if (!copy)
17518c2ecf20Sopenharmony_ci		return NULL;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	__drm_atomic_helper_connector_duplicate_state(connector, &copy->base);
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	return &copy->base;
17568c2ecf20Sopenharmony_ci}
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs tegra_sor_connector_funcs = {
17598c2ecf20Sopenharmony_ci	.reset = tegra_sor_connector_reset,
17608c2ecf20Sopenharmony_ci	.detect = tegra_sor_connector_detect,
17618c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
17628c2ecf20Sopenharmony_ci	.destroy = tegra_output_connector_destroy,
17638c2ecf20Sopenharmony_ci	.atomic_duplicate_state = tegra_sor_connector_duplicate_state,
17648c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
17658c2ecf20Sopenharmony_ci	.late_register = tegra_sor_late_register,
17668c2ecf20Sopenharmony_ci	.early_unregister = tegra_sor_early_unregister,
17678c2ecf20Sopenharmony_ci};
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_cistatic int tegra_sor_connector_get_modes(struct drm_connector *connector)
17708c2ecf20Sopenharmony_ci{
17718c2ecf20Sopenharmony_ci	struct tegra_output *output = connector_to_output(connector);
17728c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
17738c2ecf20Sopenharmony_ci	int err;
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	if (sor->aux)
17768c2ecf20Sopenharmony_ci		drm_dp_aux_enable(sor->aux);
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	err = tegra_output_connector_get_modes(connector);
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	if (sor->aux)
17818c2ecf20Sopenharmony_ci		drm_dp_aux_disable(sor->aux);
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	return err;
17848c2ecf20Sopenharmony_ci}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_cistatic enum drm_mode_status
17878c2ecf20Sopenharmony_citegra_sor_connector_mode_valid(struct drm_connector *connector,
17888c2ecf20Sopenharmony_ci			       struct drm_display_mode *mode)
17898c2ecf20Sopenharmony_ci{
17908c2ecf20Sopenharmony_ci	return MODE_OK;
17918c2ecf20Sopenharmony_ci}
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
17948c2ecf20Sopenharmony_ci	.get_modes = tegra_sor_connector_get_modes,
17958c2ecf20Sopenharmony_ci	.mode_valid = tegra_sor_connector_mode_valid,
17968c2ecf20Sopenharmony_ci};
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_cistatic int
17998c2ecf20Sopenharmony_citegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
18008c2ecf20Sopenharmony_ci			       struct drm_crtc_state *crtc_state,
18018c2ecf20Sopenharmony_ci			       struct drm_connector_state *conn_state)
18028c2ecf20Sopenharmony_ci{
18038c2ecf20Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
18048c2ecf20Sopenharmony_ci	struct tegra_sor_state *state = to_sor_state(conn_state);
18058c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
18068c2ecf20Sopenharmony_ci	unsigned long pclk = crtc_state->mode.clock * 1000;
18078c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
18088c2ecf20Sopenharmony_ci	struct drm_display_info *info;
18098c2ecf20Sopenharmony_ci	int err;
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_ci	info = &output->connector.display_info;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	/*
18148c2ecf20Sopenharmony_ci	 * For HBR2 modes, the SOR brick needs to use the x20 multiplier, so
18158c2ecf20Sopenharmony_ci	 * the pixel clock must be corrected accordingly.
18168c2ecf20Sopenharmony_ci	 */
18178c2ecf20Sopenharmony_ci	if (pclk >= 340000000) {
18188c2ecf20Sopenharmony_ci		state->link_speed = 20;
18198c2ecf20Sopenharmony_ci		state->pclk = pclk / 2;
18208c2ecf20Sopenharmony_ci	} else {
18218c2ecf20Sopenharmony_ci		state->link_speed = 10;
18228c2ecf20Sopenharmony_ci		state->pclk = pclk;
18238c2ecf20Sopenharmony_ci	}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent,
18268c2ecf20Sopenharmony_ci					 pclk, 0);
18278c2ecf20Sopenharmony_ci	if (err < 0) {
18288c2ecf20Sopenharmony_ci		dev_err(output->dev, "failed to setup CRTC state: %d\n", err);
18298c2ecf20Sopenharmony_ci		return err;
18308c2ecf20Sopenharmony_ci	}
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	switch (info->bpc) {
18338c2ecf20Sopenharmony_ci	case 8:
18348c2ecf20Sopenharmony_ci	case 6:
18358c2ecf20Sopenharmony_ci		state->bpc = info->bpc;
18368c2ecf20Sopenharmony_ci		break;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	default:
18398c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("%u bits-per-color not supported\n", info->bpc);
18408c2ecf20Sopenharmony_ci		state->bpc = 8;
18418c2ecf20Sopenharmony_ci		break;
18428c2ecf20Sopenharmony_ci	}
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	return 0;
18458c2ecf20Sopenharmony_ci}
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_cistatic inline u32 tegra_sor_hdmi_subpack(const u8 *ptr, size_t size)
18488c2ecf20Sopenharmony_ci{
18498c2ecf20Sopenharmony_ci	u32 value = 0;
18508c2ecf20Sopenharmony_ci	size_t i;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	for (i = size; i > 0; i--)
18538c2ecf20Sopenharmony_ci		value = (value << 8) | ptr[i - 1];
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	return value;
18568c2ecf20Sopenharmony_ci}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor,
18598c2ecf20Sopenharmony_ci					  const void *data, size_t size)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	const u8 *ptr = data;
18628c2ecf20Sopenharmony_ci	unsigned long offset;
18638c2ecf20Sopenharmony_ci	size_t i, j;
18648c2ecf20Sopenharmony_ci	u32 value;
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	switch (ptr[0]) {
18678c2ecf20Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_AVI:
18688c2ecf20Sopenharmony_ci		offset = SOR_HDMI_AVI_INFOFRAME_HEADER;
18698c2ecf20Sopenharmony_ci		break;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_AUDIO:
18728c2ecf20Sopenharmony_ci		offset = SOR_HDMI_AUDIO_INFOFRAME_HEADER;
18738c2ecf20Sopenharmony_ci		break;
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_VENDOR:
18768c2ecf20Sopenharmony_ci		offset = SOR_HDMI_VSI_INFOFRAME_HEADER;
18778c2ecf20Sopenharmony_ci		break;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	default:
18808c2ecf20Sopenharmony_ci		dev_err(sor->dev, "unsupported infoframe type: %02x\n",
18818c2ecf20Sopenharmony_ci			ptr[0]);
18828c2ecf20Sopenharmony_ci		return;
18838c2ecf20Sopenharmony_ci	}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	value = INFOFRAME_HEADER_TYPE(ptr[0]) |
18868c2ecf20Sopenharmony_ci		INFOFRAME_HEADER_VERSION(ptr[1]) |
18878c2ecf20Sopenharmony_ci		INFOFRAME_HEADER_LEN(ptr[2]);
18888c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, offset);
18898c2ecf20Sopenharmony_ci	offset++;
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	/*
18928c2ecf20Sopenharmony_ci	 * Each subpack contains 7 bytes, divided into:
18938c2ecf20Sopenharmony_ci	 * - subpack_low: bytes 0 - 3
18948c2ecf20Sopenharmony_ci	 * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00)
18958c2ecf20Sopenharmony_ci	 */
18968c2ecf20Sopenharmony_ci	for (i = 3, j = 0; i < size; i += 7, j += 8) {
18978c2ecf20Sopenharmony_ci		size_t rem = size - i, num = min_t(size_t, rem, 4);
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci		value = tegra_sor_hdmi_subpack(&ptr[i], num);
19008c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, value, offset++);
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci		num = min_t(size_t, rem - num, 3);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci		value = tegra_sor_hdmi_subpack(&ptr[i + 4], num);
19058c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, value, offset++);
19068c2ecf20Sopenharmony_ci	}
19078c2ecf20Sopenharmony_ci}
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_cistatic int
19108c2ecf20Sopenharmony_citegra_sor_hdmi_setup_avi_infoframe(struct tegra_sor *sor,
19118c2ecf20Sopenharmony_ci				   const struct drm_display_mode *mode)
19128c2ecf20Sopenharmony_ci{
19138c2ecf20Sopenharmony_ci	u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
19148c2ecf20Sopenharmony_ci	struct hdmi_avi_infoframe frame;
19158c2ecf20Sopenharmony_ci	u32 value;
19168c2ecf20Sopenharmony_ci	int err;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	/* disable AVI infoframe */
19198c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AVI_INFOFRAME_CTRL);
19208c2ecf20Sopenharmony_ci	value &= ~INFOFRAME_CTRL_SINGLE;
19218c2ecf20Sopenharmony_ci	value &= ~INFOFRAME_CTRL_OTHER;
19228c2ecf20Sopenharmony_ci	value &= ~INFOFRAME_CTRL_ENABLE;
19238c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AVI_INFOFRAME_CTRL);
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	err = drm_hdmi_avi_infoframe_from_display_mode(&frame,
19268c2ecf20Sopenharmony_ci						       &sor->output.connector, mode);
19278c2ecf20Sopenharmony_ci	if (err < 0) {
19288c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to setup AVI infoframe: %d\n", err);
19298c2ecf20Sopenharmony_ci		return err;
19308c2ecf20Sopenharmony_ci	}
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
19338c2ecf20Sopenharmony_ci	if (err < 0) {
19348c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to pack AVI infoframe: %d\n", err);
19358c2ecf20Sopenharmony_ci		return err;
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	tegra_sor_hdmi_write_infopack(sor, buffer, err);
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	/* enable AVI infoframe */
19418c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AVI_INFOFRAME_CTRL);
19428c2ecf20Sopenharmony_ci	value |= INFOFRAME_CTRL_CHECKSUM_ENABLE;
19438c2ecf20Sopenharmony_ci	value |= INFOFRAME_CTRL_ENABLE;
19448c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AVI_INFOFRAME_CTRL);
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	return 0;
19478c2ecf20Sopenharmony_ci}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_cistatic void tegra_sor_write_eld(struct tegra_sor *sor)
19508c2ecf20Sopenharmony_ci{
19518c2ecf20Sopenharmony_ci	size_t length = drm_eld_size(sor->output.connector.eld), i;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	for (i = 0; i < length; i++)
19548c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, i << 8 | sor->output.connector.eld[i],
19558c2ecf20Sopenharmony_ci				 SOR_AUDIO_HDA_ELD_BUFWR);
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	/*
19588c2ecf20Sopenharmony_ci	 * The HDA codec will always report an ELD buffer size of 96 bytes and
19598c2ecf20Sopenharmony_ci	 * the HDA codec driver will check that each byte read from the buffer
19608c2ecf20Sopenharmony_ci	 * is valid. Therefore every byte must be written, even if no 96 bytes
19618c2ecf20Sopenharmony_ci	 * were parsed from EDID.
19628c2ecf20Sopenharmony_ci	 */
19638c2ecf20Sopenharmony_ci	for (i = length; i < 96; i++)
19648c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, i << 8 | 0, SOR_AUDIO_HDA_ELD_BUFWR);
19658c2ecf20Sopenharmony_ci}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_cistatic void tegra_sor_audio_prepare(struct tegra_sor *sor)
19688c2ecf20Sopenharmony_ci{
19698c2ecf20Sopenharmony_ci	u32 value;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	/*
19728c2ecf20Sopenharmony_ci	 * Enable and unmask the HDA codec SCRATCH0 register interrupt. This
19738c2ecf20Sopenharmony_ci	 * is used for interoperability between the HDA codec driver and the
19748c2ecf20Sopenharmony_ci	 * HDMI/DP driver.
19758c2ecf20Sopenharmony_ci	 */
19768c2ecf20Sopenharmony_ci	value = SOR_INT_CODEC_SCRATCH1 | SOR_INT_CODEC_SCRATCH0;
19778c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_INT_ENABLE);
19788c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_INT_MASK);
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	tegra_sor_write_eld(sor);
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	value = SOR_AUDIO_HDA_PRESENSE_ELDV | SOR_AUDIO_HDA_PRESENSE_PD;
19838c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_HDA_PRESENSE);
19848c2ecf20Sopenharmony_ci}
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_cistatic void tegra_sor_audio_unprepare(struct tegra_sor *sor)
19878c2ecf20Sopenharmony_ci{
19888c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_AUDIO_HDA_PRESENSE);
19898c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_INT_MASK);
19908c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_INT_ENABLE);
19918c2ecf20Sopenharmony_ci}
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_cistatic void tegra_sor_audio_enable(struct tegra_sor *sor)
19948c2ecf20Sopenharmony_ci{
19958c2ecf20Sopenharmony_ci	u32 value;
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_AUDIO_CNTRL);
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	/* select HDA audio input */
20008c2ecf20Sopenharmony_ci	value &= ~SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_MASK);
20018c2ecf20Sopenharmony_ci	value |= SOR_AUDIO_CNTRL_SOURCE_SELECT(SOURCE_SELECT_HDA);
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	/* inject null samples */
20048c2ecf20Sopenharmony_ci	if (sor->format.channels != 2)
20058c2ecf20Sopenharmony_ci		value &= ~SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
20068c2ecf20Sopenharmony_ci	else
20078c2ecf20Sopenharmony_ci		value |= SOR_AUDIO_CNTRL_INJECT_NULLSMPL;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	value |= SOR_AUDIO_CNTRL_AFIFO_FLUSH;
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_CNTRL);
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	/* enable advertising HBR capability */
20148c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, SOR_AUDIO_SPARE_HBR_ENABLE, SOR_AUDIO_SPARE);
20158c2ecf20Sopenharmony_ci}
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_cistatic int tegra_sor_hdmi_enable_audio_infoframe(struct tegra_sor *sor)
20188c2ecf20Sopenharmony_ci{
20198c2ecf20Sopenharmony_ci	u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)];
20208c2ecf20Sopenharmony_ci	struct hdmi_audio_infoframe frame;
20218c2ecf20Sopenharmony_ci	u32 value;
20228c2ecf20Sopenharmony_ci	int err;
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	err = hdmi_audio_infoframe_init(&frame);
20258c2ecf20Sopenharmony_ci	if (err < 0) {
20268c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to setup audio infoframe: %d\n", err);
20278c2ecf20Sopenharmony_ci		return err;
20288c2ecf20Sopenharmony_ci	}
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	frame.channels = sor->format.channels;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
20338c2ecf20Sopenharmony_ci	if (err < 0) {
20348c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to pack audio infoframe: %d\n", err);
20358c2ecf20Sopenharmony_ci		return err;
20368c2ecf20Sopenharmony_ci	}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	tegra_sor_hdmi_write_infopack(sor, buffer, err);
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
20418c2ecf20Sopenharmony_ci	value |= INFOFRAME_CTRL_CHECKSUM_ENABLE;
20428c2ecf20Sopenharmony_ci	value |= INFOFRAME_CTRL_ENABLE;
20438c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	return 0;
20468c2ecf20Sopenharmony_ci}
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_audio_enable(struct tegra_sor *sor)
20498c2ecf20Sopenharmony_ci{
20508c2ecf20Sopenharmony_ci	u32 value;
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	tegra_sor_audio_enable(sor);
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_HDMI_ACR_CTRL);
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	value = SOR_HDMI_SPARE_ACR_PRIORITY_HIGH |
20578c2ecf20Sopenharmony_ci		SOR_HDMI_SPARE_CTS_RESET(1) |
20588c2ecf20Sopenharmony_ci		SOR_HDMI_SPARE_HW_CTS_ENABLE;
20598c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_SPARE);
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	/* enable HW CTS */
20628c2ecf20Sopenharmony_ci	value = SOR_HDMI_ACR_SUBPACK_LOW_SB1(0);
20638c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_ACR_0441_SUBPACK_LOW);
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	/* allow packet to be sent */
20668c2ecf20Sopenharmony_ci	value = SOR_HDMI_ACR_SUBPACK_HIGH_ENABLE;
20678c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_ACR_0441_SUBPACK_HIGH);
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	/* reset N counter and enable lookup */
20708c2ecf20Sopenharmony_ci	value = SOR_HDMI_AUDIO_N_RESET | SOR_HDMI_AUDIO_N_LOOKUP;
20718c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N);
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	value = (24000 * 4096) / (128 * sor->format.sample_rate / 1000);
20748c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0320);
20758c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 4096, SOR_AUDIO_NVAL_0320);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_0441);
20788c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 4704, SOR_AUDIO_NVAL_0441);
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_0882);
20818c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 9408, SOR_AUDIO_NVAL_0882);
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 20000, SOR_AUDIO_AVAL_1764);
20848c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 18816, SOR_AUDIO_NVAL_1764);
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	value = (24000 * 6144) / (128 * sor->format.sample_rate / 1000);
20878c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0480);
20888c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 6144, SOR_AUDIO_NVAL_0480);
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	value = (24000 * 12288) / (128 * sor->format.sample_rate / 1000);
20918c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_0960);
20928c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 12288, SOR_AUDIO_NVAL_0960);
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	value = (24000 * 24576) / (128 * sor->format.sample_rate / 1000);
20958c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_AUDIO_AVAL_1920);
20968c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 24576, SOR_AUDIO_NVAL_1920);
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AUDIO_N);
20998c2ecf20Sopenharmony_ci	value &= ~SOR_HDMI_AUDIO_N_RESET;
21008c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_N);
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	tegra_sor_hdmi_enable_audio_infoframe(sor);
21038c2ecf20Sopenharmony_ci}
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_disable_audio_infoframe(struct tegra_sor *sor)
21068c2ecf20Sopenharmony_ci{
21078c2ecf20Sopenharmony_ci	u32 value;
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
21108c2ecf20Sopenharmony_ci	value &= ~INFOFRAME_CTRL_ENABLE;
21118c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_audio_disable(struct tegra_sor *sor)
21158c2ecf20Sopenharmony_ci{
21168c2ecf20Sopenharmony_ci	tegra_sor_hdmi_disable_audio_infoframe(sor);
21178c2ecf20Sopenharmony_ci}
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_cistatic struct tegra_sor_hdmi_settings *
21208c2ecf20Sopenharmony_citegra_sor_hdmi_find_settings(struct tegra_sor *sor, unsigned long frequency)
21218c2ecf20Sopenharmony_ci{
21228c2ecf20Sopenharmony_ci	unsigned int i;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	for (i = 0; i < sor->num_settings; i++)
21258c2ecf20Sopenharmony_ci		if (frequency <= sor->settings[i].frequency)
21268c2ecf20Sopenharmony_ci			return &sor->settings[i];
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci	return NULL;
21298c2ecf20Sopenharmony_ci}
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_disable_scrambling(struct tegra_sor *sor)
21328c2ecf20Sopenharmony_ci{
21338c2ecf20Sopenharmony_ci	u32 value;
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI2_CTRL);
21368c2ecf20Sopenharmony_ci	value &= ~SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4;
21378c2ecf20Sopenharmony_ci	value &= ~SOR_HDMI2_CTRL_SCRAMBLE;
21388c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI2_CTRL);
21398c2ecf20Sopenharmony_ci}
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_scdc_disable(struct tegra_sor *sor)
21428c2ecf20Sopenharmony_ci{
21438c2ecf20Sopenharmony_ci	struct i2c_adapter *ddc = sor->output.ddc;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	drm_scdc_set_high_tmds_clock_ratio(ddc, false);
21468c2ecf20Sopenharmony_ci	drm_scdc_set_scrambling(ddc, false);
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	tegra_sor_hdmi_disable_scrambling(sor);
21498c2ecf20Sopenharmony_ci}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_scdc_stop(struct tegra_sor *sor)
21528c2ecf20Sopenharmony_ci{
21538c2ecf20Sopenharmony_ci	if (sor->scdc_enabled) {
21548c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&sor->scdc);
21558c2ecf20Sopenharmony_ci		tegra_sor_hdmi_scdc_disable(sor);
21568c2ecf20Sopenharmony_ci	}
21578c2ecf20Sopenharmony_ci}
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_enable_scrambling(struct tegra_sor *sor)
21608c2ecf20Sopenharmony_ci{
21618c2ecf20Sopenharmony_ci	u32 value;
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_HDMI2_CTRL);
21648c2ecf20Sopenharmony_ci	value |= SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4;
21658c2ecf20Sopenharmony_ci	value |= SOR_HDMI2_CTRL_SCRAMBLE;
21668c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI2_CTRL);
21678c2ecf20Sopenharmony_ci}
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_scdc_enable(struct tegra_sor *sor)
21708c2ecf20Sopenharmony_ci{
21718c2ecf20Sopenharmony_ci	struct i2c_adapter *ddc = sor->output.ddc;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	drm_scdc_set_high_tmds_clock_ratio(ddc, true);
21748c2ecf20Sopenharmony_ci	drm_scdc_set_scrambling(ddc, true);
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci	tegra_sor_hdmi_enable_scrambling(sor);
21778c2ecf20Sopenharmony_ci}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_scdc_work(struct work_struct *work)
21808c2ecf20Sopenharmony_ci{
21818c2ecf20Sopenharmony_ci	struct tegra_sor *sor = container_of(work, struct tegra_sor, scdc.work);
21828c2ecf20Sopenharmony_ci	struct i2c_adapter *ddc = sor->output.ddc;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	if (!drm_scdc_get_scrambling_status(ddc)) {
21858c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("SCDC not scrambled\n");
21868c2ecf20Sopenharmony_ci		tegra_sor_hdmi_scdc_enable(sor);
21878c2ecf20Sopenharmony_ci	}
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000));
21908c2ecf20Sopenharmony_ci}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_scdc_start(struct tegra_sor *sor)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	struct drm_scdc *scdc = &sor->output.connector.display_info.hdmi.scdc;
21958c2ecf20Sopenharmony_ci	struct drm_display_mode *mode;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	mode = &sor->output.encoder.crtc->state->adjusted_mode;
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	if (mode->clock >= 340000 && scdc->supported) {
22008c2ecf20Sopenharmony_ci		schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000));
22018c2ecf20Sopenharmony_ci		tegra_sor_hdmi_scdc_enable(sor);
22028c2ecf20Sopenharmony_ci		sor->scdc_enabled = true;
22038c2ecf20Sopenharmony_ci	}
22048c2ecf20Sopenharmony_ci}
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
22078c2ecf20Sopenharmony_ci{
22088c2ecf20Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
22098c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
22108c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
22118c2ecf20Sopenharmony_ci	u32 value;
22128c2ecf20Sopenharmony_ci	int err;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	tegra_sor_audio_unprepare(sor);
22158c2ecf20Sopenharmony_ci	tegra_sor_hdmi_scdc_stop(sor);
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci	err = tegra_sor_detach(sor);
22188c2ecf20Sopenharmony_ci	if (err < 0)
22198c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE1);
22228c2ecf20Sopenharmony_ci	tegra_sor_update(sor);
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	/* disable display to SOR clock */
22258c2ecf20Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci	if (!sor->soc->has_nvdisplay)
22288c2ecf20Sopenharmony_ci		value &= ~SOR1_TIMING_CYA;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	value &= ~SOR_ENABLE(sor->index);
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	tegra_dc_commit(dc);
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci	err = tegra_sor_power_down(sor);
22378c2ecf20Sopenharmony_ci	if (err < 0)
22388c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power down SOR: %d\n", err);
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	err = tegra_io_pad_power_disable(sor->pad);
22418c2ecf20Sopenharmony_ci	if (err < 0)
22428c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	host1x_client_suspend(&sor->client);
22458c2ecf20Sopenharmony_ci}
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_cistatic void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
22488c2ecf20Sopenharmony_ci{
22498c2ecf20Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
22508c2ecf20Sopenharmony_ci	unsigned int h_ref_to_sync = 1, pulse_start, max_ac;
22518c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
22528c2ecf20Sopenharmony_ci	struct tegra_sor_hdmi_settings *settings;
22538c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
22548c2ecf20Sopenharmony_ci	struct tegra_sor_state *state;
22558c2ecf20Sopenharmony_ci	struct drm_display_mode *mode;
22568c2ecf20Sopenharmony_ci	unsigned long rate, pclk;
22578c2ecf20Sopenharmony_ci	unsigned int div, i;
22588c2ecf20Sopenharmony_ci	u32 value;
22598c2ecf20Sopenharmony_ci	int err;
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	state = to_sor_state(output->connector.state);
22628c2ecf20Sopenharmony_ci	mode = &encoder->crtc->state->adjusted_mode;
22638c2ecf20Sopenharmony_ci	pclk = mode->clock * 1000;
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci	err = host1x_client_resume(&sor->client);
22668c2ecf20Sopenharmony_ci	if (err < 0) {
22678c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to resume: %d\n", err);
22688c2ecf20Sopenharmony_ci		return;
22698c2ecf20Sopenharmony_ci	}
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	/* switch to safe parent clock */
22728c2ecf20Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
22738c2ecf20Sopenharmony_ci	if (err < 0) {
22748c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
22758c2ecf20Sopenharmony_ci		return;
22768c2ecf20Sopenharmony_ci	}
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	div = clk_get_rate(sor->clk) / 1000000 * 4;
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	err = tegra_io_pad_power_enable(sor->pad);
22818c2ecf20Sopenharmony_ci	if (err < 0)
22828c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power on I/O pad: %d\n", err);
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_ci	usleep_range(20, 100);
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
22878c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
22888c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci	usleep_range(20, 100);
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
22938c2ecf20Sopenharmony_ci	value &= ~SOR_PLL3_PLL_VDD_MODE_3V3;
22948c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
22978c2ecf20Sopenharmony_ci	value &= ~SOR_PLL0_VCOPD;
22988c2ecf20Sopenharmony_ci	value &= ~SOR_PLL0_PWR;
22998c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
23028c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
23038c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	usleep_range(200, 400);
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
23088c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_POWERDOWN_OVERRIDE;
23098c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_PORT_POWERDOWN;
23108c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	usleep_range(20, 100);
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
23158c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
23168c2ecf20Sopenharmony_ci		 SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2;
23178c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	while (true) {
23208c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
23218c2ecf20Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_STATE_BUSY) == 0)
23228c2ecf20Sopenharmony_ci			break;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci		usleep_range(250, 1000);
23258c2ecf20Sopenharmony_ci	}
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci	value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
23288c2ecf20Sopenharmony_ci		SOR_LANE_SEQ_CTL_POWER_STATE_UP | SOR_LANE_SEQ_CTL_DELAY(5);
23298c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci	while (true) {
23328c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
23338c2ecf20Sopenharmony_ci		if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
23348c2ecf20Sopenharmony_ci			break;
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci		usleep_range(250, 1000);
23378c2ecf20Sopenharmony_ci	}
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
23408c2ecf20Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
23418c2ecf20Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	if (mode->clock < 340000) {
23448c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("setting 2.7 GHz link speed\n");
23458c2ecf20Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
23468c2ecf20Sopenharmony_ci	} else {
23478c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("setting 5.4 GHz link speed\n");
23488c2ecf20Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40;
23498c2ecf20Sopenharmony_ci	}
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK;
23528c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	/* SOR pad PLL stabilization time */
23558c2ecf20Sopenharmony_ci	usleep_range(250, 1000);
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
23588c2ecf20Sopenharmony_ci	value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
23598c2ecf20Sopenharmony_ci	value |= SOR_DP_LINKCTL_LANE_COUNT(4);
23608c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
23638c2ecf20Sopenharmony_ci	value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE;
23648c2ecf20Sopenharmony_ci	value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
23658c2ecf20Sopenharmony_ci	value &= ~SOR_DP_SPARE_SEQ_ENABLE;
23668c2ecf20Sopenharmony_ci	value &= ~SOR_DP_SPARE_MACRO_SOR_CLK;
23678c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci	value = SOR_SEQ_CTL_PU_PC(0) | SOR_SEQ_CTL_PU_PC_ALT(0) |
23708c2ecf20Sopenharmony_ci		SOR_SEQ_CTL_PD_PC(8) | SOR_SEQ_CTL_PD_PC_ALT(8);
23718c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SEQ_CTL);
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ci	value = SOR_SEQ_INST_DRIVE_PWM_OUT_LO | SOR_SEQ_INST_HALT |
23748c2ecf20Sopenharmony_ci		SOR_SEQ_INST_WAIT_VSYNC | SOR_SEQ_INST_WAIT(1);
23758c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SEQ_INST(0));
23768c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_SEQ_INST(8));
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	if (!sor->soc->has_nvdisplay) {
23798c2ecf20Sopenharmony_ci		/* program the reference clock */
23808c2ecf20Sopenharmony_ci		value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div);
23818c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, value, SOR_REFCLK);
23828c2ecf20Sopenharmony_ci	}
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	/* XXX not in TRM */
23858c2ecf20Sopenharmony_ci	for (value = 0, i = 0; i < 5; i++)
23868c2ecf20Sopenharmony_ci		value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) |
23878c2ecf20Sopenharmony_ci			 SOR_XBAR_CTRL_LINK1_XSEL(i, i);
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
23908c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci	/*
23938c2ecf20Sopenharmony_ci	 * Switch the pad clock to the DP clock. Note that we cannot actually
23948c2ecf20Sopenharmony_ci	 * do this because Tegra186 and later don't support clk_set_parent()
23958c2ecf20Sopenharmony_ci	 * on the sorX_pad_clkout clocks. We already do the equivalent above
23968c2ecf20Sopenharmony_ci	 * using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register.
23978c2ecf20Sopenharmony_ci	 */
23988c2ecf20Sopenharmony_ci#if 0
23998c2ecf20Sopenharmony_ci	err = clk_set_parent(sor->clk_pad, sor->clk_dp);
24008c2ecf20Sopenharmony_ci	if (err < 0) {
24018c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to select pad parent clock: %d\n",
24028c2ecf20Sopenharmony_ci			err);
24038c2ecf20Sopenharmony_ci		return;
24048c2ecf20Sopenharmony_ci	}
24058c2ecf20Sopenharmony_ci#endif
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	/* switch the SOR clock to the pad clock */
24088c2ecf20Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_pad);
24098c2ecf20Sopenharmony_ci	if (err < 0) {
24108c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to select SOR parent clock: %d\n",
24118c2ecf20Sopenharmony_ci			err);
24128c2ecf20Sopenharmony_ci		return;
24138c2ecf20Sopenharmony_ci	}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci	/* switch the output clock to the parent pixel clock */
24168c2ecf20Sopenharmony_ci	err = clk_set_parent(sor->clk, sor->clk_parent);
24178c2ecf20Sopenharmony_ci	if (err < 0) {
24188c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to select output parent clock: %d\n",
24198c2ecf20Sopenharmony_ci			err);
24208c2ecf20Sopenharmony_ci		return;
24218c2ecf20Sopenharmony_ci	}
24228c2ecf20Sopenharmony_ci
24238c2ecf20Sopenharmony_ci	/* adjust clock rate for HDMI 2.0 modes */
24248c2ecf20Sopenharmony_ci	rate = clk_get_rate(sor->clk_parent);
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci	if (mode->clock >= 340000)
24278c2ecf20Sopenharmony_ci		rate /= 2;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("setting clock to %lu Hz, mode: %lu Hz\n", rate, pclk);
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	clk_set_rate(sor->clk, rate);
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci	if (!sor->soc->has_nvdisplay) {
24348c2ecf20Sopenharmony_ci		value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe);
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci		/* XXX is this the proper check? */
24378c2ecf20Sopenharmony_ci		if (mode->clock < 75000)
24388c2ecf20Sopenharmony_ci			value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED;
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, value, SOR_INPUT_CONTROL);
24418c2ecf20Sopenharmony_ci	}
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	max_ac = ((mode->htotal - mode->hdisplay) - SOR_REKEY - 18) / 32;
24448c2ecf20Sopenharmony_ci
24458c2ecf20Sopenharmony_ci	value = SOR_HDMI_CTRL_ENABLE | SOR_HDMI_CTRL_MAX_AC_PACKET(max_ac) |
24468c2ecf20Sopenharmony_ci		SOR_HDMI_CTRL_AUDIO_LAYOUT | SOR_HDMI_CTRL_REKEY(SOR_REKEY);
24478c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_HDMI_CTRL);
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
24508c2ecf20Sopenharmony_ci		/* H_PULSE2 setup */
24518c2ecf20Sopenharmony_ci		pulse_start = h_ref_to_sync +
24528c2ecf20Sopenharmony_ci			      (mode->hsync_end - mode->hsync_start) +
24538c2ecf20Sopenharmony_ci			      (mode->htotal - mode->hsync_end) - 10;
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci		value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE |
24568c2ecf20Sopenharmony_ci			PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL;
24578c2ecf20Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL);
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci		value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start);
24608c2ecf20Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A);
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci		value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0);
24638c2ecf20Sopenharmony_ci		value |= H_PULSE2_ENABLE;
24648c2ecf20Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0);
24658c2ecf20Sopenharmony_ci	}
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci	/* infoframe setup */
24688c2ecf20Sopenharmony_ci	err = tegra_sor_hdmi_setup_avi_infoframe(sor, mode);
24698c2ecf20Sopenharmony_ci	if (err < 0)
24708c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to setup AVI infoframe: %d\n", err);
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_ci	/* XXX HDMI audio support not implemented yet */
24738c2ecf20Sopenharmony_ci	tegra_sor_hdmi_disable_audio_infoframe(sor);
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci	/* use single TMDS protocol */
24768c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
24778c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
24788c2ecf20Sopenharmony_ci	value |= SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A;
24798c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci	/* power up pad calibration */
24828c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
24838c2ecf20Sopenharmony_ci	value &= ~SOR_DP_PADCTL_PAD_CAL_PD;
24848c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	/* production settings */
24878c2ecf20Sopenharmony_ci	settings = tegra_sor_hdmi_find_settings(sor, mode->clock * 1000);
24888c2ecf20Sopenharmony_ci	if (!settings) {
24898c2ecf20Sopenharmony_ci		dev_err(sor->dev, "no settings for pixel clock %d Hz\n",
24908c2ecf20Sopenharmony_ci			mode->clock * 1000);
24918c2ecf20Sopenharmony_ci		return;
24928c2ecf20Sopenharmony_ci	}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
24958c2ecf20Sopenharmony_ci	value &= ~SOR_PLL0_ICHPMP_MASK;
24968c2ecf20Sopenharmony_ci	value &= ~SOR_PLL0_FILTER_MASK;
24978c2ecf20Sopenharmony_ci	value &= ~SOR_PLL0_VCOCAP_MASK;
24988c2ecf20Sopenharmony_ci	value |= SOR_PLL0_ICHPMP(settings->ichpmp);
24998c2ecf20Sopenharmony_ci	value |= SOR_PLL0_FILTER(settings->filter);
25008c2ecf20Sopenharmony_ci	value |= SOR_PLL0_VCOCAP(settings->vcocap);
25018c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci	/* XXX not in TRM */
25048c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll1);
25058c2ecf20Sopenharmony_ci	value &= ~SOR_PLL1_LOADADJ_MASK;
25068c2ecf20Sopenharmony_ci	value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
25078c2ecf20Sopenharmony_ci	value |= SOR_PLL1_LOADADJ(settings->loadadj);
25088c2ecf20Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERMADJ(settings->tmds_termadj);
25098c2ecf20Sopenharmony_ci	value |= SOR_PLL1_TMDS_TERM;
25108c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll1);
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
25138c2ecf20Sopenharmony_ci	value &= ~SOR_PLL3_BG_TEMP_COEF_MASK;
25148c2ecf20Sopenharmony_ci	value &= ~SOR_PLL3_BG_VREF_LEVEL_MASK;
25158c2ecf20Sopenharmony_ci	value &= ~SOR_PLL3_AVDD10_LEVEL_MASK;
25168c2ecf20Sopenharmony_ci	value &= ~SOR_PLL3_AVDD14_LEVEL_MASK;
25178c2ecf20Sopenharmony_ci	value |= SOR_PLL3_BG_TEMP_COEF(settings->bg_temp_coef);
25188c2ecf20Sopenharmony_ci	value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref_level);
25198c2ecf20Sopenharmony_ci	value |= SOR_PLL3_AVDD10_LEVEL(settings->avdd10_level);
25208c2ecf20Sopenharmony_ci	value |= SOR_PLL3_AVDD14_LEVEL(settings->avdd14_level);
25218c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	value = settings->drive_current[3] << 24 |
25248c2ecf20Sopenharmony_ci		settings->drive_current[2] << 16 |
25258c2ecf20Sopenharmony_ci		settings->drive_current[1] <<  8 |
25268c2ecf20Sopenharmony_ci		settings->drive_current[0] <<  0;
25278c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT0);
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci	value = settings->preemphasis[3] << 24 |
25308c2ecf20Sopenharmony_ci		settings->preemphasis[2] << 16 |
25318c2ecf20Sopenharmony_ci		settings->preemphasis[1] <<  8 |
25328c2ecf20Sopenharmony_ci		settings->preemphasis[0] <<  0;
25338c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS0);
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
25368c2ecf20Sopenharmony_ci	value &= ~SOR_DP_PADCTL_TX_PU_MASK;
25378c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU_ENABLE;
25388c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu_value);
25398c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl2);
25428c2ecf20Sopenharmony_ci	value &= ~SOR_DP_PADCTL_SPAREPLL_MASK;
25438c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_SPAREPLL(settings->sparepll);
25448c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl2);
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	/* power down pad calibration */
25478c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
25488c2ecf20Sopenharmony_ci	value |= SOR_DP_PADCTL_PAD_CAL_PD;
25498c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
25508c2ecf20Sopenharmony_ci
25518c2ecf20Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
25528c2ecf20Sopenharmony_ci		/* miscellaneous display controller settings */
25538c2ecf20Sopenharmony_ci		value = VSYNC_H_POSITION(1);
25548c2ecf20Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS);
25558c2ecf20Sopenharmony_ci	}
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
25588c2ecf20Sopenharmony_ci	value &= ~DITHER_CONTROL_MASK;
25598c2ecf20Sopenharmony_ci	value &= ~BASE_COLOR_SIZE_MASK;
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	switch (state->bpc) {
25628c2ecf20Sopenharmony_ci	case 6:
25638c2ecf20Sopenharmony_ci		value |= BASE_COLOR_SIZE_666;
25648c2ecf20Sopenharmony_ci		break;
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	case 8:
25678c2ecf20Sopenharmony_ci		value |= BASE_COLOR_SIZE_888;
25688c2ecf20Sopenharmony_ci		break;
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci	case 10:
25718c2ecf20Sopenharmony_ci		value |= BASE_COLOR_SIZE_101010;
25728c2ecf20Sopenharmony_ci		break;
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci	case 12:
25758c2ecf20Sopenharmony_ci		value |= BASE_COLOR_SIZE_121212;
25768c2ecf20Sopenharmony_ci		break;
25778c2ecf20Sopenharmony_ci
25788c2ecf20Sopenharmony_ci	default:
25798c2ecf20Sopenharmony_ci		WARN(1, "%u bits-per-color not supported\n", state->bpc);
25808c2ecf20Sopenharmony_ci		value |= BASE_COLOR_SIZE_888;
25818c2ecf20Sopenharmony_ci		break;
25828c2ecf20Sopenharmony_ci	}
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL);
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	/* XXX set display head owner */
25878c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
25888c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_OWNER_MASK;
25898c2ecf20Sopenharmony_ci	value |= SOR_STATE_ASY_OWNER(1 + dc->pipe);
25908c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci	err = tegra_sor_power_up(sor, 250);
25938c2ecf20Sopenharmony_ci	if (err < 0)
25948c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power up SOR: %d\n", err);
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci	/* configure dynamic range of output */
25978c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe);
25988c2ecf20Sopenharmony_ci	value &= ~SOR_HEAD_STATE_RANGECOMPRESS_MASK;
25998c2ecf20Sopenharmony_ci	value &= ~SOR_HEAD_STATE_DYNRANGE_MASK;
26008c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe);
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	/* configure colorspace */
26038c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe);
26048c2ecf20Sopenharmony_ci	value &= ~SOR_HEAD_STATE_COLORSPACE_MASK;
26058c2ecf20Sopenharmony_ci	value |= SOR_HEAD_STATE_COLORSPACE_RGB;
26068c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe);
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	tegra_sor_mode_set(sor, mode, state);
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_ci	tegra_sor_update(sor);
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	/* program preamble timing in SOR (XXX) */
26138c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
26148c2ecf20Sopenharmony_ci	value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE;
26158c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
26168c2ecf20Sopenharmony_ci
26178c2ecf20Sopenharmony_ci	err = tegra_sor_attach(sor);
26188c2ecf20Sopenharmony_ci	if (err < 0)
26198c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	/* enable display to SOR clock and generate HDMI preamble */
26228c2ecf20Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_ci	if (!sor->soc->has_nvdisplay)
26258c2ecf20Sopenharmony_ci		value |= SOR1_TIMING_CYA;
26268c2ecf20Sopenharmony_ci
26278c2ecf20Sopenharmony_ci	value |= SOR_ENABLE(sor->index);
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	if (dc->soc->has_nvdisplay) {
26328c2ecf20Sopenharmony_ci		value = tegra_dc_readl(dc, DC_DISP_CORE_SOR_SET_CONTROL(sor->index));
26338c2ecf20Sopenharmony_ci		value &= ~PROTOCOL_MASK;
26348c2ecf20Sopenharmony_ci		value |= PROTOCOL_SINGLE_TMDS_A;
26358c2ecf20Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_CORE_SOR_SET_CONTROL(sor->index));
26368c2ecf20Sopenharmony_ci	}
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	tegra_dc_commit(dc);
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	err = tegra_sor_wakeup(sor);
26418c2ecf20Sopenharmony_ci	if (err < 0)
26428c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to wakeup SOR: %d\n", err);
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_ci	tegra_sor_hdmi_scdc_start(sor);
26458c2ecf20Sopenharmony_ci	tegra_sor_audio_prepare(sor);
26468c2ecf20Sopenharmony_ci}
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs tegra_sor_hdmi_helpers = {
26498c2ecf20Sopenharmony_ci	.disable = tegra_sor_hdmi_disable,
26508c2ecf20Sopenharmony_ci	.enable = tegra_sor_hdmi_enable,
26518c2ecf20Sopenharmony_ci	.atomic_check = tegra_sor_encoder_atomic_check,
26528c2ecf20Sopenharmony_ci};
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_cistatic void tegra_sor_dp_disable(struct drm_encoder *encoder)
26558c2ecf20Sopenharmony_ci{
26568c2ecf20Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
26578c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
26588c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
26598c2ecf20Sopenharmony_ci	u32 value;
26608c2ecf20Sopenharmony_ci	int err;
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ci	if (output->panel)
26638c2ecf20Sopenharmony_ci		drm_panel_disable(output->panel);
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	/*
26668c2ecf20Sopenharmony_ci	 * Do not attempt to power down a DP link if we're not connected since
26678c2ecf20Sopenharmony_ci	 * the AUX transactions would just be timing out.
26688c2ecf20Sopenharmony_ci	 */
26698c2ecf20Sopenharmony_ci	if (output->connector.status != connector_status_disconnected) {
26708c2ecf20Sopenharmony_ci		err = drm_dp_link_power_down(sor->aux, &sor->link);
26718c2ecf20Sopenharmony_ci		if (err < 0)
26728c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to power down link: %d\n",
26738c2ecf20Sopenharmony_ci				err);
26748c2ecf20Sopenharmony_ci	}
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci	err = tegra_sor_detach(sor);
26778c2ecf20Sopenharmony_ci	if (err < 0)
26788c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to detach SOR: %d\n", err);
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_STATE1);
26818c2ecf20Sopenharmony_ci	tegra_sor_update(sor);
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
26848c2ecf20Sopenharmony_ci	value &= ~SOR_ENABLE(sor->index);
26858c2ecf20Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
26868c2ecf20Sopenharmony_ci	tegra_dc_commit(dc);
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
26898c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
26908c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_SUBOWNER_MASK;
26918c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_OWNER_MASK;
26928c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
26938c2ecf20Sopenharmony_ci	tegra_sor_update(sor);
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ci	/* switch to safe parent clock */
26968c2ecf20Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
26978c2ecf20Sopenharmony_ci	if (err < 0)
26988c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to set safe clock: %d\n", err);
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	err = tegra_sor_power_down(sor);
27018c2ecf20Sopenharmony_ci	if (err < 0)
27028c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power down SOR: %d\n", err);
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	err = tegra_io_pad_power_disable(sor->pad);
27058c2ecf20Sopenharmony_ci	if (err < 0)
27068c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	err = drm_dp_aux_disable(sor->aux);
27098c2ecf20Sopenharmony_ci	if (err < 0)
27108c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed disable DPAUX: %d\n", err);
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci	if (output->panel)
27138c2ecf20Sopenharmony_ci		drm_panel_unprepare(output->panel);
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_ci	host1x_client_suspend(&sor->client);
27168c2ecf20Sopenharmony_ci}
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_cistatic void tegra_sor_dp_enable(struct drm_encoder *encoder)
27198c2ecf20Sopenharmony_ci{
27208c2ecf20Sopenharmony_ci	struct tegra_output *output = encoder_to_output(encoder);
27218c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
27228c2ecf20Sopenharmony_ci	struct tegra_sor *sor = to_sor(output);
27238c2ecf20Sopenharmony_ci	struct tegra_sor_config config;
27248c2ecf20Sopenharmony_ci	struct tegra_sor_state *state;
27258c2ecf20Sopenharmony_ci	struct drm_display_mode *mode;
27268c2ecf20Sopenharmony_ci	struct drm_display_info *info;
27278c2ecf20Sopenharmony_ci	unsigned int i;
27288c2ecf20Sopenharmony_ci	u32 value;
27298c2ecf20Sopenharmony_ci	int err;
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	state = to_sor_state(output->connector.state);
27328c2ecf20Sopenharmony_ci	mode = &encoder->crtc->state->adjusted_mode;
27338c2ecf20Sopenharmony_ci	info = &output->connector.display_info;
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci	err = host1x_client_resume(&sor->client);
27368c2ecf20Sopenharmony_ci	if (err < 0) {
27378c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to resume: %d\n", err);
27388c2ecf20Sopenharmony_ci		return;
27398c2ecf20Sopenharmony_ci	}
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_ci	/* switch to safe parent clock */
27428c2ecf20Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
27438c2ecf20Sopenharmony_ci	if (err < 0)
27448c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci	err = tegra_io_pad_power_enable(sor->pad);
27478c2ecf20Sopenharmony_ci	if (err < 0)
27488c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power on LVDS rail: %d\n", err);
27498c2ecf20Sopenharmony_ci
27508c2ecf20Sopenharmony_ci	usleep_range(20, 100);
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci	err = drm_dp_aux_enable(sor->aux);
27538c2ecf20Sopenharmony_ci	if (err < 0)
27548c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to enable DPAUX: %d\n", err);
27558c2ecf20Sopenharmony_ci
27568c2ecf20Sopenharmony_ci	err = drm_dp_link_probe(sor->aux, &sor->link);
27578c2ecf20Sopenharmony_ci	if (err < 0)
27588c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to probe DP link: %d\n", err);
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	tegra_sor_filter_rates(sor);
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci	err = drm_dp_link_choose(&sor->link, mode, info);
27638c2ecf20Sopenharmony_ci	if (err < 0)
27648c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to choose link: %d\n", err);
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci	if (output->panel)
27678c2ecf20Sopenharmony_ci		drm_panel_prepare(output->panel);
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
27708c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
27718c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci	usleep_range(20, 40);
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll3);
27768c2ecf20Sopenharmony_ci	value |= SOR_PLL3_PLL_VDD_MODE_3V3;
27778c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll3);
27788c2ecf20Sopenharmony_ci
27798c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
27808c2ecf20Sopenharmony_ci	value &= ~(SOR_PLL0_VCOPD | SOR_PLL0_PWR);
27818c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
27828c2ecf20Sopenharmony_ci
27838c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
27848c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
27858c2ecf20Sopenharmony_ci	value |= SOR_PLL2_SEQ_PLLCAPPD;
27868c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
27878c2ecf20Sopenharmony_ci
27888c2ecf20Sopenharmony_ci	usleep_range(200, 400);
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll2);
27918c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_POWERDOWN_OVERRIDE;
27928c2ecf20Sopenharmony_ci	value &= ~SOR_PLL2_PORT_POWERDOWN;
27938c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll2);
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
27968c2ecf20Sopenharmony_ci	value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
27978c2ecf20Sopenharmony_ci
27988c2ecf20Sopenharmony_ci	if (output->panel)
27998c2ecf20Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
28008c2ecf20Sopenharmony_ci	else
28018c2ecf20Sopenharmony_ci		value |= SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK;
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci	usleep_range(200, 400);
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_SPARE0);
28088c2ecf20Sopenharmony_ci	/* XXX not in TRM */
28098c2ecf20Sopenharmony_ci	if (output->panel)
28108c2ecf20Sopenharmony_ci		value |= SOR_DP_SPARE_PANEL_INTERNAL;
28118c2ecf20Sopenharmony_ci	else
28128c2ecf20Sopenharmony_ci		value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
28138c2ecf20Sopenharmony_ci
28148c2ecf20Sopenharmony_ci	value |= SOR_DP_SPARE_SEQ_ENABLE;
28158c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_SPARE0);
28168c2ecf20Sopenharmony_ci
28178c2ecf20Sopenharmony_ci	/* XXX not in TRM */
28188c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0, SOR_LVDS);
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, sor->soc->regs->pll0);
28218c2ecf20Sopenharmony_ci	value &= ~SOR_PLL0_ICHPMP_MASK;
28228c2ecf20Sopenharmony_ci	value &= ~SOR_PLL0_VCOCAP_MASK;
28238c2ecf20Sopenharmony_ci	value |= SOR_PLL0_ICHPMP(0x1);
28248c2ecf20Sopenharmony_ci	value |= SOR_PLL0_VCOCAP(0x3);
28258c2ecf20Sopenharmony_ci	value |= SOR_PLL0_RESISTOR_EXT;
28268c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, sor->soc->regs->pll0);
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_ci	/* XXX not in TRM */
28298c2ecf20Sopenharmony_ci	for (value = 0, i = 0; i < 5; i++)
28308c2ecf20Sopenharmony_ci		value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) |
28318c2ecf20Sopenharmony_ci			 SOR_XBAR_CTRL_LINK1_XSEL(i, i);
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
28348c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
28358c2ecf20Sopenharmony_ci
28368c2ecf20Sopenharmony_ci	/*
28378c2ecf20Sopenharmony_ci	 * Switch the pad clock to the DP clock. Note that we cannot actually
28388c2ecf20Sopenharmony_ci	 * do this because Tegra186 and later don't support clk_set_parent()
28398c2ecf20Sopenharmony_ci	 * on the sorX_pad_clkout clocks. We already do the equivalent above
28408c2ecf20Sopenharmony_ci	 * using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register.
28418c2ecf20Sopenharmony_ci	 */
28428c2ecf20Sopenharmony_ci#if 0
28438c2ecf20Sopenharmony_ci	err = clk_set_parent(sor->clk_pad, sor->clk_parent);
28448c2ecf20Sopenharmony_ci	if (err < 0) {
28458c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to select pad parent clock: %d\n",
28468c2ecf20Sopenharmony_ci			err);
28478c2ecf20Sopenharmony_ci		return;
28488c2ecf20Sopenharmony_ci	}
28498c2ecf20Sopenharmony_ci#endif
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci	/* switch the SOR clock to the pad clock */
28528c2ecf20Sopenharmony_ci	err = tegra_sor_set_parent_clock(sor, sor->clk_pad);
28538c2ecf20Sopenharmony_ci	if (err < 0) {
28548c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to select SOR parent clock: %d\n",
28558c2ecf20Sopenharmony_ci			err);
28568c2ecf20Sopenharmony_ci		return;
28578c2ecf20Sopenharmony_ci	}
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_ci	/* switch the output clock to the parent pixel clock */
28608c2ecf20Sopenharmony_ci	err = clk_set_parent(sor->clk, sor->clk_parent);
28618c2ecf20Sopenharmony_ci	if (err < 0) {
28628c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to select output parent clock: %d\n",
28638c2ecf20Sopenharmony_ci			err);
28648c2ecf20Sopenharmony_ci		return;
28658c2ecf20Sopenharmony_ci	}
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	/* use DP-A protocol */
28688c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_STATE1);
28698c2ecf20Sopenharmony_ci	value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
28708c2ecf20Sopenharmony_ci	value |= SOR_STATE_ASY_PROTOCOL_DP_A;
28718c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_STATE1);
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	/* enable port */
28748c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
28758c2ecf20Sopenharmony_ci	value |= SOR_DP_LINKCTL_ENABLE;
28768c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	tegra_sor_dp_term_calibrate(sor);
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci	err = drm_dp_link_train(&sor->link);
28818c2ecf20Sopenharmony_ci	if (err < 0)
28828c2ecf20Sopenharmony_ci		dev_err(sor->dev, "link training failed: %d\n", err);
28838c2ecf20Sopenharmony_ci	else
28848c2ecf20Sopenharmony_ci		dev_dbg(sor->dev, "link training succeeded\n");
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci	err = drm_dp_link_power_up(sor->aux, &sor->link);
28878c2ecf20Sopenharmony_ci	if (err < 0)
28888c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power up DP link: %d\n", err);
28898c2ecf20Sopenharmony_ci
28908c2ecf20Sopenharmony_ci	/* compute configuration */
28918c2ecf20Sopenharmony_ci	memset(&config, 0, sizeof(config));
28928c2ecf20Sopenharmony_ci	config.bits_per_pixel = state->bpc * 3;
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci	err = tegra_sor_compute_config(sor, mode, &config, &sor->link);
28958c2ecf20Sopenharmony_ci	if (err < 0)
28968c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to compute configuration: %d\n", err);
28978c2ecf20Sopenharmony_ci
28988c2ecf20Sopenharmony_ci	tegra_sor_apply_config(sor, &config);
28998c2ecf20Sopenharmony_ci	tegra_sor_mode_set(sor, mode, state);
29008c2ecf20Sopenharmony_ci
29018c2ecf20Sopenharmony_ci	if (output->panel) {
29028c2ecf20Sopenharmony_ci		/* CSTM (LVDS, link A/B, upper) */
29038c2ecf20Sopenharmony_ci		value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
29048c2ecf20Sopenharmony_ci			SOR_CSTM_UPPER;
29058c2ecf20Sopenharmony_ci		tegra_sor_writel(sor, value, SOR_CSTM);
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci		/* PWM setup */
29088c2ecf20Sopenharmony_ci		err = tegra_sor_setup_pwm(sor, 250);
29098c2ecf20Sopenharmony_ci		if (err < 0)
29108c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to setup PWM: %d\n", err);
29118c2ecf20Sopenharmony_ci	}
29128c2ecf20Sopenharmony_ci
29138c2ecf20Sopenharmony_ci	tegra_sor_update(sor);
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_ci	err = tegra_sor_power_up(sor, 250);
29168c2ecf20Sopenharmony_ci	if (err < 0)
29178c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to power up SOR: %d\n", err);
29188c2ecf20Sopenharmony_ci
29198c2ecf20Sopenharmony_ci	/* attach and wake up */
29208c2ecf20Sopenharmony_ci	err = tegra_sor_attach(sor);
29218c2ecf20Sopenharmony_ci	if (err < 0)
29228c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to attach SOR: %d\n", err);
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
29258c2ecf20Sopenharmony_ci	value |= SOR_ENABLE(sor->index);
29268c2ecf20Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci	tegra_dc_commit(dc);
29298c2ecf20Sopenharmony_ci
29308c2ecf20Sopenharmony_ci	err = tegra_sor_wakeup(sor);
29318c2ecf20Sopenharmony_ci	if (err < 0)
29328c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to wakeup SOR: %d\n", err);
29338c2ecf20Sopenharmony_ci
29348c2ecf20Sopenharmony_ci	if (output->panel)
29358c2ecf20Sopenharmony_ci		drm_panel_enable(output->panel);
29368c2ecf20Sopenharmony_ci}
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = {
29398c2ecf20Sopenharmony_ci	.disable = tegra_sor_dp_disable,
29408c2ecf20Sopenharmony_ci	.enable = tegra_sor_dp_enable,
29418c2ecf20Sopenharmony_ci	.atomic_check = tegra_sor_encoder_atomic_check,
29428c2ecf20Sopenharmony_ci};
29438c2ecf20Sopenharmony_ci
29448c2ecf20Sopenharmony_cistatic void tegra_sor_disable_regulator(void *data)
29458c2ecf20Sopenharmony_ci{
29468c2ecf20Sopenharmony_ci	struct regulator *reg = data;
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci	regulator_disable(reg);
29498c2ecf20Sopenharmony_ci}
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_cistatic int tegra_sor_enable_regulator(struct tegra_sor *sor, struct regulator *reg)
29528c2ecf20Sopenharmony_ci{
29538c2ecf20Sopenharmony_ci	int err;
29548c2ecf20Sopenharmony_ci
29558c2ecf20Sopenharmony_ci	err = regulator_enable(reg);
29568c2ecf20Sopenharmony_ci	if (err)
29578c2ecf20Sopenharmony_ci		return err;
29588c2ecf20Sopenharmony_ci
29598c2ecf20Sopenharmony_ci	return devm_add_action_or_reset(sor->dev, tegra_sor_disable_regulator, reg);
29608c2ecf20Sopenharmony_ci}
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_cistatic int tegra_sor_hdmi_probe(struct tegra_sor *sor)
29638c2ecf20Sopenharmony_ci{
29648c2ecf20Sopenharmony_ci	int err;
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	sor->avdd_io_supply = devm_regulator_get(sor->dev, "avdd-io-hdmi-dp");
29678c2ecf20Sopenharmony_ci	if (IS_ERR(sor->avdd_io_supply)) {
29688c2ecf20Sopenharmony_ci		dev_err(sor->dev, "cannot get AVDD I/O supply: %ld\n",
29698c2ecf20Sopenharmony_ci			PTR_ERR(sor->avdd_io_supply));
29708c2ecf20Sopenharmony_ci		return PTR_ERR(sor->avdd_io_supply);
29718c2ecf20Sopenharmony_ci	}
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply);
29748c2ecf20Sopenharmony_ci	if (err < 0) {
29758c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to enable AVDD I/O supply: %d\n",
29768c2ecf20Sopenharmony_ci			err);
29778c2ecf20Sopenharmony_ci		return err;
29788c2ecf20Sopenharmony_ci	}
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_ci	sor->vdd_pll_supply = devm_regulator_get(sor->dev, "vdd-hdmi-dp-pll");
29818c2ecf20Sopenharmony_ci	if (IS_ERR(sor->vdd_pll_supply)) {
29828c2ecf20Sopenharmony_ci		dev_err(sor->dev, "cannot get VDD PLL supply: %ld\n",
29838c2ecf20Sopenharmony_ci			PTR_ERR(sor->vdd_pll_supply));
29848c2ecf20Sopenharmony_ci		return PTR_ERR(sor->vdd_pll_supply);
29858c2ecf20Sopenharmony_ci	}
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply);
29888c2ecf20Sopenharmony_ci	if (err < 0) {
29898c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to enable VDD PLL supply: %d\n",
29908c2ecf20Sopenharmony_ci			err);
29918c2ecf20Sopenharmony_ci		return err;
29928c2ecf20Sopenharmony_ci	}
29938c2ecf20Sopenharmony_ci
29948c2ecf20Sopenharmony_ci	sor->hdmi_supply = devm_regulator_get(sor->dev, "hdmi");
29958c2ecf20Sopenharmony_ci	if (IS_ERR(sor->hdmi_supply)) {
29968c2ecf20Sopenharmony_ci		dev_err(sor->dev, "cannot get HDMI supply: %ld\n",
29978c2ecf20Sopenharmony_ci			PTR_ERR(sor->hdmi_supply));
29988c2ecf20Sopenharmony_ci		return PTR_ERR(sor->hdmi_supply);
29998c2ecf20Sopenharmony_ci	}
30008c2ecf20Sopenharmony_ci
30018c2ecf20Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->hdmi_supply);
30028c2ecf20Sopenharmony_ci	if (err < 0) {
30038c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to enable HDMI supply: %d\n", err);
30048c2ecf20Sopenharmony_ci		return err;
30058c2ecf20Sopenharmony_ci	}
30068c2ecf20Sopenharmony_ci
30078c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&sor->scdc, tegra_sor_hdmi_scdc_work);
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci	return 0;
30108c2ecf20Sopenharmony_ci}
30118c2ecf20Sopenharmony_ci
30128c2ecf20Sopenharmony_cistatic const struct tegra_sor_ops tegra_sor_hdmi_ops = {
30138c2ecf20Sopenharmony_ci	.name = "HDMI",
30148c2ecf20Sopenharmony_ci	.probe = tegra_sor_hdmi_probe,
30158c2ecf20Sopenharmony_ci	.audio_enable = tegra_sor_hdmi_audio_enable,
30168c2ecf20Sopenharmony_ci	.audio_disable = tegra_sor_hdmi_audio_disable,
30178c2ecf20Sopenharmony_ci};
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_cistatic int tegra_sor_dp_probe(struct tegra_sor *sor)
30208c2ecf20Sopenharmony_ci{
30218c2ecf20Sopenharmony_ci	int err;
30228c2ecf20Sopenharmony_ci
30238c2ecf20Sopenharmony_ci	sor->avdd_io_supply = devm_regulator_get(sor->dev, "avdd-io-hdmi-dp");
30248c2ecf20Sopenharmony_ci	if (IS_ERR(sor->avdd_io_supply))
30258c2ecf20Sopenharmony_ci		return PTR_ERR(sor->avdd_io_supply);
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->avdd_io_supply);
30288c2ecf20Sopenharmony_ci	if (err < 0)
30298c2ecf20Sopenharmony_ci		return err;
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci	sor->vdd_pll_supply = devm_regulator_get(sor->dev, "vdd-hdmi-dp-pll");
30328c2ecf20Sopenharmony_ci	if (IS_ERR(sor->vdd_pll_supply))
30338c2ecf20Sopenharmony_ci		return PTR_ERR(sor->vdd_pll_supply);
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_ci	err = tegra_sor_enable_regulator(sor, sor->vdd_pll_supply);
30368c2ecf20Sopenharmony_ci	if (err < 0)
30378c2ecf20Sopenharmony_ci		return err;
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci	return 0;
30408c2ecf20Sopenharmony_ci}
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_cistatic const struct tegra_sor_ops tegra_sor_dp_ops = {
30438c2ecf20Sopenharmony_ci	.name = "DP",
30448c2ecf20Sopenharmony_ci	.probe = tegra_sor_dp_probe,
30458c2ecf20Sopenharmony_ci};
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_cistatic int tegra_sor_init(struct host1x_client *client)
30488c2ecf20Sopenharmony_ci{
30498c2ecf20Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
30508c2ecf20Sopenharmony_ci	const struct drm_encoder_helper_funcs *helpers = NULL;
30518c2ecf20Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
30528c2ecf20Sopenharmony_ci	int connector = DRM_MODE_CONNECTOR_Unknown;
30538c2ecf20Sopenharmony_ci	int encoder = DRM_MODE_ENCODER_NONE;
30548c2ecf20Sopenharmony_ci	int err;
30558c2ecf20Sopenharmony_ci
30568c2ecf20Sopenharmony_ci	if (!sor->aux) {
30578c2ecf20Sopenharmony_ci		if (sor->ops == &tegra_sor_hdmi_ops) {
30588c2ecf20Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_HDMIA;
30598c2ecf20Sopenharmony_ci			encoder = DRM_MODE_ENCODER_TMDS;
30608c2ecf20Sopenharmony_ci			helpers = &tegra_sor_hdmi_helpers;
30618c2ecf20Sopenharmony_ci		} else if (sor->soc->supports_lvds) {
30628c2ecf20Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_LVDS;
30638c2ecf20Sopenharmony_ci			encoder = DRM_MODE_ENCODER_LVDS;
30648c2ecf20Sopenharmony_ci		}
30658c2ecf20Sopenharmony_ci	} else {
30668c2ecf20Sopenharmony_ci		if (sor->output.panel) {
30678c2ecf20Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_eDP;
30688c2ecf20Sopenharmony_ci			encoder = DRM_MODE_ENCODER_TMDS;
30698c2ecf20Sopenharmony_ci			helpers = &tegra_sor_dp_helpers;
30708c2ecf20Sopenharmony_ci		} else {
30718c2ecf20Sopenharmony_ci			connector = DRM_MODE_CONNECTOR_DisplayPort;
30728c2ecf20Sopenharmony_ci			encoder = DRM_MODE_ENCODER_TMDS;
30738c2ecf20Sopenharmony_ci			helpers = &tegra_sor_dp_helpers;
30748c2ecf20Sopenharmony_ci		}
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci		sor->link.ops = &tegra_sor_dp_link_ops;
30778c2ecf20Sopenharmony_ci		sor->link.aux = sor->aux;
30788c2ecf20Sopenharmony_ci	}
30798c2ecf20Sopenharmony_ci
30808c2ecf20Sopenharmony_ci	sor->output.dev = sor->dev;
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci	drm_connector_init_with_ddc(drm, &sor->output.connector,
30838c2ecf20Sopenharmony_ci				    &tegra_sor_connector_funcs,
30848c2ecf20Sopenharmony_ci				    connector,
30858c2ecf20Sopenharmony_ci				    sor->output.ddc);
30868c2ecf20Sopenharmony_ci	drm_connector_helper_add(&sor->output.connector,
30878c2ecf20Sopenharmony_ci				 &tegra_sor_connector_helper_funcs);
30888c2ecf20Sopenharmony_ci	sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci	drm_simple_encoder_init(drm, &sor->output.encoder, encoder);
30918c2ecf20Sopenharmony_ci	drm_encoder_helper_add(&sor->output.encoder, helpers);
30928c2ecf20Sopenharmony_ci
30938c2ecf20Sopenharmony_ci	drm_connector_attach_encoder(&sor->output.connector,
30948c2ecf20Sopenharmony_ci					  &sor->output.encoder);
30958c2ecf20Sopenharmony_ci	drm_connector_register(&sor->output.connector);
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_ci	err = tegra_output_init(drm, &sor->output);
30988c2ecf20Sopenharmony_ci	if (err < 0) {
30998c2ecf20Sopenharmony_ci		dev_err(client->dev, "failed to initialize output: %d\n", err);
31008c2ecf20Sopenharmony_ci		return err;
31018c2ecf20Sopenharmony_ci	}
31028c2ecf20Sopenharmony_ci
31038c2ecf20Sopenharmony_ci	tegra_output_find_possible_crtcs(&sor->output, drm);
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_ci	if (sor->aux) {
31068c2ecf20Sopenharmony_ci		err = drm_dp_aux_attach(sor->aux, &sor->output);
31078c2ecf20Sopenharmony_ci		if (err < 0) {
31088c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to attach DP: %d\n", err);
31098c2ecf20Sopenharmony_ci			return err;
31108c2ecf20Sopenharmony_ci		}
31118c2ecf20Sopenharmony_ci	}
31128c2ecf20Sopenharmony_ci
31138c2ecf20Sopenharmony_ci	/*
31148c2ecf20Sopenharmony_ci	 * XXX: Remove this reset once proper hand-over from firmware to
31158c2ecf20Sopenharmony_ci	 * kernel is possible.
31168c2ecf20Sopenharmony_ci	 */
31178c2ecf20Sopenharmony_ci	if (sor->rst) {
31188c2ecf20Sopenharmony_ci		err = pm_runtime_resume_and_get(sor->dev);
31198c2ecf20Sopenharmony_ci		if (err < 0) {
31208c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to get runtime PM: %d\n", err);
31218c2ecf20Sopenharmony_ci			return err;
31228c2ecf20Sopenharmony_ci		}
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci		err = reset_control_acquire(sor->rst);
31258c2ecf20Sopenharmony_ci		if (err < 0) {
31268c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to acquire SOR reset: %d\n",
31278c2ecf20Sopenharmony_ci				err);
31288c2ecf20Sopenharmony_ci			goto rpm_put;
31298c2ecf20Sopenharmony_ci		}
31308c2ecf20Sopenharmony_ci
31318c2ecf20Sopenharmony_ci		err = reset_control_assert(sor->rst);
31328c2ecf20Sopenharmony_ci		if (err < 0) {
31338c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to assert SOR reset: %d\n",
31348c2ecf20Sopenharmony_ci				err);
31358c2ecf20Sopenharmony_ci			goto rpm_put;
31368c2ecf20Sopenharmony_ci		}
31378c2ecf20Sopenharmony_ci	}
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	err = clk_prepare_enable(sor->clk);
31408c2ecf20Sopenharmony_ci	if (err < 0) {
31418c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to enable clock: %d\n", err);
31428c2ecf20Sopenharmony_ci		goto rpm_put;
31438c2ecf20Sopenharmony_ci	}
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	usleep_range(1000, 3000);
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_ci	if (sor->rst) {
31488c2ecf20Sopenharmony_ci		err = reset_control_deassert(sor->rst);
31498c2ecf20Sopenharmony_ci		if (err < 0) {
31508c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to deassert SOR reset: %d\n",
31518c2ecf20Sopenharmony_ci				err);
31528c2ecf20Sopenharmony_ci			clk_disable_unprepare(sor->clk);
31538c2ecf20Sopenharmony_ci			goto rpm_put;
31548c2ecf20Sopenharmony_ci		}
31558c2ecf20Sopenharmony_ci
31568c2ecf20Sopenharmony_ci		reset_control_release(sor->rst);
31578c2ecf20Sopenharmony_ci		pm_runtime_put(sor->dev);
31588c2ecf20Sopenharmony_ci	}
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ci	err = clk_prepare_enable(sor->clk_safe);
31618c2ecf20Sopenharmony_ci	if (err < 0) {
31628c2ecf20Sopenharmony_ci		clk_disable_unprepare(sor->clk);
31638c2ecf20Sopenharmony_ci		return err;
31648c2ecf20Sopenharmony_ci	}
31658c2ecf20Sopenharmony_ci
31668c2ecf20Sopenharmony_ci	err = clk_prepare_enable(sor->clk_dp);
31678c2ecf20Sopenharmony_ci	if (err < 0) {
31688c2ecf20Sopenharmony_ci		clk_disable_unprepare(sor->clk_safe);
31698c2ecf20Sopenharmony_ci		clk_disable_unprepare(sor->clk);
31708c2ecf20Sopenharmony_ci		return err;
31718c2ecf20Sopenharmony_ci	}
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci	return 0;
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_cirpm_put:
31768c2ecf20Sopenharmony_ci	if (sor->rst)
31778c2ecf20Sopenharmony_ci		pm_runtime_put(sor->dev);
31788c2ecf20Sopenharmony_ci
31798c2ecf20Sopenharmony_ci	return err;
31808c2ecf20Sopenharmony_ci}
31818c2ecf20Sopenharmony_ci
31828c2ecf20Sopenharmony_cistatic int tegra_sor_exit(struct host1x_client *client)
31838c2ecf20Sopenharmony_ci{
31848c2ecf20Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
31858c2ecf20Sopenharmony_ci	int err;
31868c2ecf20Sopenharmony_ci
31878c2ecf20Sopenharmony_ci	tegra_output_exit(&sor->output);
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ci	if (sor->aux) {
31908c2ecf20Sopenharmony_ci		err = drm_dp_aux_detach(sor->aux);
31918c2ecf20Sopenharmony_ci		if (err < 0) {
31928c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to detach DP: %d\n", err);
31938c2ecf20Sopenharmony_ci			return err;
31948c2ecf20Sopenharmony_ci		}
31958c2ecf20Sopenharmony_ci	}
31968c2ecf20Sopenharmony_ci
31978c2ecf20Sopenharmony_ci	clk_disable_unprepare(sor->clk_safe);
31988c2ecf20Sopenharmony_ci	clk_disable_unprepare(sor->clk_dp);
31998c2ecf20Sopenharmony_ci	clk_disable_unprepare(sor->clk);
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci	return 0;
32028c2ecf20Sopenharmony_ci}
32038c2ecf20Sopenharmony_ci
32048c2ecf20Sopenharmony_cistatic int tegra_sor_runtime_suspend(struct host1x_client *client)
32058c2ecf20Sopenharmony_ci{
32068c2ecf20Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
32078c2ecf20Sopenharmony_ci	struct device *dev = client->dev;
32088c2ecf20Sopenharmony_ci	int err;
32098c2ecf20Sopenharmony_ci
32108c2ecf20Sopenharmony_ci	if (sor->rst) {
32118c2ecf20Sopenharmony_ci		err = reset_control_assert(sor->rst);
32128c2ecf20Sopenharmony_ci		if (err < 0) {
32138c2ecf20Sopenharmony_ci			dev_err(dev, "failed to assert reset: %d\n", err);
32148c2ecf20Sopenharmony_ci			return err;
32158c2ecf20Sopenharmony_ci		}
32168c2ecf20Sopenharmony_ci
32178c2ecf20Sopenharmony_ci		reset_control_release(sor->rst);
32188c2ecf20Sopenharmony_ci	}
32198c2ecf20Sopenharmony_ci
32208c2ecf20Sopenharmony_ci	usleep_range(1000, 2000);
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_ci	clk_disable_unprepare(sor->clk);
32238c2ecf20Sopenharmony_ci	pm_runtime_put_sync(dev);
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ci	return 0;
32268c2ecf20Sopenharmony_ci}
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_cistatic int tegra_sor_runtime_resume(struct host1x_client *client)
32298c2ecf20Sopenharmony_ci{
32308c2ecf20Sopenharmony_ci	struct tegra_sor *sor = host1x_client_to_sor(client);
32318c2ecf20Sopenharmony_ci	struct device *dev = client->dev;
32328c2ecf20Sopenharmony_ci	int err;
32338c2ecf20Sopenharmony_ci
32348c2ecf20Sopenharmony_ci	err = pm_runtime_resume_and_get(dev);
32358c2ecf20Sopenharmony_ci	if (err < 0) {
32368c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get runtime PM: %d\n", err);
32378c2ecf20Sopenharmony_ci		return err;
32388c2ecf20Sopenharmony_ci	}
32398c2ecf20Sopenharmony_ci
32408c2ecf20Sopenharmony_ci	err = clk_prepare_enable(sor->clk);
32418c2ecf20Sopenharmony_ci	if (err < 0) {
32428c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable clock: %d\n", err);
32438c2ecf20Sopenharmony_ci		goto put_rpm;
32448c2ecf20Sopenharmony_ci	}
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci	usleep_range(1000, 2000);
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ci	if (sor->rst) {
32498c2ecf20Sopenharmony_ci		err = reset_control_acquire(sor->rst);
32508c2ecf20Sopenharmony_ci		if (err < 0) {
32518c2ecf20Sopenharmony_ci			dev_err(dev, "failed to acquire reset: %d\n", err);
32528c2ecf20Sopenharmony_ci			goto disable_clk;
32538c2ecf20Sopenharmony_ci		}
32548c2ecf20Sopenharmony_ci
32558c2ecf20Sopenharmony_ci		err = reset_control_deassert(sor->rst);
32568c2ecf20Sopenharmony_ci		if (err < 0) {
32578c2ecf20Sopenharmony_ci			dev_err(dev, "failed to deassert reset: %d\n", err);
32588c2ecf20Sopenharmony_ci			goto release_reset;
32598c2ecf20Sopenharmony_ci		}
32608c2ecf20Sopenharmony_ci	}
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci	return 0;
32638c2ecf20Sopenharmony_ci
32648c2ecf20Sopenharmony_cirelease_reset:
32658c2ecf20Sopenharmony_ci	reset_control_release(sor->rst);
32668c2ecf20Sopenharmony_cidisable_clk:
32678c2ecf20Sopenharmony_ci	clk_disable_unprepare(sor->clk);
32688c2ecf20Sopenharmony_ciput_rpm:
32698c2ecf20Sopenharmony_ci	pm_runtime_put_sync(dev);
32708c2ecf20Sopenharmony_ci	return err;
32718c2ecf20Sopenharmony_ci}
32728c2ecf20Sopenharmony_ci
32738c2ecf20Sopenharmony_cistatic const struct host1x_client_ops sor_client_ops = {
32748c2ecf20Sopenharmony_ci	.init = tegra_sor_init,
32758c2ecf20Sopenharmony_ci	.exit = tegra_sor_exit,
32768c2ecf20Sopenharmony_ci	.suspend = tegra_sor_runtime_suspend,
32778c2ecf20Sopenharmony_ci	.resume = tegra_sor_runtime_resume,
32788c2ecf20Sopenharmony_ci};
32798c2ecf20Sopenharmony_ci
32808c2ecf20Sopenharmony_cistatic const u8 tegra124_sor_xbar_cfg[5] = {
32818c2ecf20Sopenharmony_ci	0, 1, 2, 3, 4
32828c2ecf20Sopenharmony_ci};
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_cistatic const struct tegra_sor_regs tegra124_sor_regs = {
32858c2ecf20Sopenharmony_ci	.head_state0 = 0x05,
32868c2ecf20Sopenharmony_ci	.head_state1 = 0x07,
32878c2ecf20Sopenharmony_ci	.head_state2 = 0x09,
32888c2ecf20Sopenharmony_ci	.head_state3 = 0x0b,
32898c2ecf20Sopenharmony_ci	.head_state4 = 0x0d,
32908c2ecf20Sopenharmony_ci	.head_state5 = 0x0f,
32918c2ecf20Sopenharmony_ci	.pll0 = 0x17,
32928c2ecf20Sopenharmony_ci	.pll1 = 0x18,
32938c2ecf20Sopenharmony_ci	.pll2 = 0x19,
32948c2ecf20Sopenharmony_ci	.pll3 = 0x1a,
32958c2ecf20Sopenharmony_ci	.dp_padctl0 = 0x5c,
32968c2ecf20Sopenharmony_ci	.dp_padctl2 = 0x73,
32978c2ecf20Sopenharmony_ci};
32988c2ecf20Sopenharmony_ci
32998c2ecf20Sopenharmony_ci/* Tegra124 and Tegra132 have lanes 0 and 2 swapped. */
33008c2ecf20Sopenharmony_cistatic const u8 tegra124_sor_lane_map[4] = {
33018c2ecf20Sopenharmony_ci	2, 1, 0, 3,
33028c2ecf20Sopenharmony_ci};
33038c2ecf20Sopenharmony_ci
33048c2ecf20Sopenharmony_cistatic const u8 tegra124_sor_voltage_swing[4][4][4] = {
33058c2ecf20Sopenharmony_ci	{
33068c2ecf20Sopenharmony_ci		{ 0x13, 0x19, 0x1e, 0x28 },
33078c2ecf20Sopenharmony_ci		{ 0x1e, 0x25, 0x2d, },
33088c2ecf20Sopenharmony_ci		{ 0x28, 0x32, },
33098c2ecf20Sopenharmony_ci		{ 0x3c, },
33108c2ecf20Sopenharmony_ci	}, {
33118c2ecf20Sopenharmony_ci		{ 0x12, 0x17, 0x1b, 0x25 },
33128c2ecf20Sopenharmony_ci		{ 0x1c, 0x23, 0x2a, },
33138c2ecf20Sopenharmony_ci		{ 0x25, 0x2f, },
33148c2ecf20Sopenharmony_ci		{ 0x39, }
33158c2ecf20Sopenharmony_ci	}, {
33168c2ecf20Sopenharmony_ci		{ 0x12, 0x16, 0x1a, 0x22 },
33178c2ecf20Sopenharmony_ci		{ 0x1b, 0x20, 0x27, },
33188c2ecf20Sopenharmony_ci		{ 0x24, 0x2d, },
33198c2ecf20Sopenharmony_ci		{ 0x36, },
33208c2ecf20Sopenharmony_ci	}, {
33218c2ecf20Sopenharmony_ci		{ 0x11, 0x14, 0x17, 0x1f },
33228c2ecf20Sopenharmony_ci		{ 0x19, 0x1e, 0x24, },
33238c2ecf20Sopenharmony_ci		{ 0x22, 0x2a, },
33248c2ecf20Sopenharmony_ci		{ 0x32, },
33258c2ecf20Sopenharmony_ci	},
33268c2ecf20Sopenharmony_ci};
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_cistatic const u8 tegra124_sor_pre_emphasis[4][4][4] = {
33298c2ecf20Sopenharmony_ci	{
33308c2ecf20Sopenharmony_ci		{ 0x00, 0x09, 0x13, 0x25 },
33318c2ecf20Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
33328c2ecf20Sopenharmony_ci		{ 0x00, 0x14, },
33338c2ecf20Sopenharmony_ci		{ 0x00, },
33348c2ecf20Sopenharmony_ci	}, {
33358c2ecf20Sopenharmony_ci		{ 0x00, 0x0a, 0x14, 0x28 },
33368c2ecf20Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
33378c2ecf20Sopenharmony_ci		{ 0x00, 0x14, },
33388c2ecf20Sopenharmony_ci		{ 0x00 },
33398c2ecf20Sopenharmony_ci	}, {
33408c2ecf20Sopenharmony_ci		{ 0x00, 0x0a, 0x14, 0x28 },
33418c2ecf20Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
33428c2ecf20Sopenharmony_ci		{ 0x00, 0x14, },
33438c2ecf20Sopenharmony_ci		{ 0x00, },
33448c2ecf20Sopenharmony_ci	}, {
33458c2ecf20Sopenharmony_ci		{ 0x00, 0x0a, 0x14, 0x28 },
33468c2ecf20Sopenharmony_ci		{ 0x00, 0x0f, 0x1e, },
33478c2ecf20Sopenharmony_ci		{ 0x00, 0x14, },
33488c2ecf20Sopenharmony_ci		{ 0x00, },
33498c2ecf20Sopenharmony_ci	},
33508c2ecf20Sopenharmony_ci};
33518c2ecf20Sopenharmony_ci
33528c2ecf20Sopenharmony_cistatic const u8 tegra124_sor_post_cursor[4][4][4] = {
33538c2ecf20Sopenharmony_ci	{
33548c2ecf20Sopenharmony_ci		{ 0x00, 0x00, 0x00, 0x00 },
33558c2ecf20Sopenharmony_ci		{ 0x00, 0x00, 0x00, },
33568c2ecf20Sopenharmony_ci		{ 0x00, 0x00, },
33578c2ecf20Sopenharmony_ci		{ 0x00, },
33588c2ecf20Sopenharmony_ci	}, {
33598c2ecf20Sopenharmony_ci		{ 0x02, 0x02, 0x04, 0x05 },
33608c2ecf20Sopenharmony_ci		{ 0x02, 0x04, 0x05, },
33618c2ecf20Sopenharmony_ci		{ 0x04, 0x05, },
33628c2ecf20Sopenharmony_ci		{ 0x05, },
33638c2ecf20Sopenharmony_ci	}, {
33648c2ecf20Sopenharmony_ci		{ 0x04, 0x05, 0x08, 0x0b },
33658c2ecf20Sopenharmony_ci		{ 0x05, 0x09, 0x0b, },
33668c2ecf20Sopenharmony_ci		{ 0x08, 0x0a, },
33678c2ecf20Sopenharmony_ci		{ 0x0b, },
33688c2ecf20Sopenharmony_ci	}, {
33698c2ecf20Sopenharmony_ci		{ 0x05, 0x09, 0x0b, 0x12 },
33708c2ecf20Sopenharmony_ci		{ 0x09, 0x0d, 0x12, },
33718c2ecf20Sopenharmony_ci		{ 0x0b, 0x0f, },
33728c2ecf20Sopenharmony_ci		{ 0x12, },
33738c2ecf20Sopenharmony_ci	},
33748c2ecf20Sopenharmony_ci};
33758c2ecf20Sopenharmony_ci
33768c2ecf20Sopenharmony_cistatic const u8 tegra124_sor_tx_pu[4][4][4] = {
33778c2ecf20Sopenharmony_ci	{
33788c2ecf20Sopenharmony_ci		{ 0x20, 0x30, 0x40, 0x60 },
33798c2ecf20Sopenharmony_ci		{ 0x30, 0x40, 0x60, },
33808c2ecf20Sopenharmony_ci		{ 0x40, 0x60, },
33818c2ecf20Sopenharmony_ci		{ 0x60, },
33828c2ecf20Sopenharmony_ci	}, {
33838c2ecf20Sopenharmony_ci		{ 0x20, 0x20, 0x30, 0x50 },
33848c2ecf20Sopenharmony_ci		{ 0x30, 0x40, 0x50, },
33858c2ecf20Sopenharmony_ci		{ 0x40, 0x50, },
33868c2ecf20Sopenharmony_ci		{ 0x60, },
33878c2ecf20Sopenharmony_ci	}, {
33888c2ecf20Sopenharmony_ci		{ 0x20, 0x20, 0x30, 0x40, },
33898c2ecf20Sopenharmony_ci		{ 0x30, 0x30, 0x40, },
33908c2ecf20Sopenharmony_ci		{ 0x40, 0x50, },
33918c2ecf20Sopenharmony_ci		{ 0x60, },
33928c2ecf20Sopenharmony_ci	}, {
33938c2ecf20Sopenharmony_ci		{ 0x20, 0x20, 0x20, 0x40, },
33948c2ecf20Sopenharmony_ci		{ 0x30, 0x30, 0x40, },
33958c2ecf20Sopenharmony_ci		{ 0x40, 0x40, },
33968c2ecf20Sopenharmony_ci		{ 0x60, },
33978c2ecf20Sopenharmony_ci	},
33988c2ecf20Sopenharmony_ci};
33998c2ecf20Sopenharmony_ci
34008c2ecf20Sopenharmony_cistatic const struct tegra_sor_soc tegra124_sor = {
34018c2ecf20Sopenharmony_ci	.supports_lvds = true,
34028c2ecf20Sopenharmony_ci	.supports_hdmi = false,
34038c2ecf20Sopenharmony_ci	.supports_dp = true,
34048c2ecf20Sopenharmony_ci	.supports_audio = false,
34058c2ecf20Sopenharmony_ci	.supports_hdcp = false,
34068c2ecf20Sopenharmony_ci	.regs = &tegra124_sor_regs,
34078c2ecf20Sopenharmony_ci	.has_nvdisplay = false,
34088c2ecf20Sopenharmony_ci	.xbar_cfg = tegra124_sor_xbar_cfg,
34098c2ecf20Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
34108c2ecf20Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
34118c2ecf20Sopenharmony_ci	.pre_emphasis = tegra124_sor_pre_emphasis,
34128c2ecf20Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
34138c2ecf20Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
34148c2ecf20Sopenharmony_ci};
34158c2ecf20Sopenharmony_ci
34168c2ecf20Sopenharmony_cistatic const u8 tegra132_sor_pre_emphasis[4][4][4] = {
34178c2ecf20Sopenharmony_ci	{
34188c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
34198c2ecf20Sopenharmony_ci		{ 0x01, 0x0e, 0x1d, },
34208c2ecf20Sopenharmony_ci		{ 0x01, 0x13, },
34218c2ecf20Sopenharmony_ci		{ 0x00, },
34228c2ecf20Sopenharmony_ci	}, {
34238c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
34248c2ecf20Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
34258c2ecf20Sopenharmony_ci		{ 0x00, 0x13, },
34268c2ecf20Sopenharmony_ci		{ 0x00 },
34278c2ecf20Sopenharmony_ci	}, {
34288c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
34298c2ecf20Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
34308c2ecf20Sopenharmony_ci		{ 0x00, 0x13, },
34318c2ecf20Sopenharmony_ci		{ 0x00, },
34328c2ecf20Sopenharmony_ci	}, {
34338c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
34348c2ecf20Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
34358c2ecf20Sopenharmony_ci		{ 0x00, 0x13, },
34368c2ecf20Sopenharmony_ci		{ 0x00, },
34378c2ecf20Sopenharmony_ci	},
34388c2ecf20Sopenharmony_ci};
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_cistatic const struct tegra_sor_soc tegra132_sor = {
34418c2ecf20Sopenharmony_ci	.supports_lvds = true,
34428c2ecf20Sopenharmony_ci	.supports_hdmi = false,
34438c2ecf20Sopenharmony_ci	.supports_dp = true,
34448c2ecf20Sopenharmony_ci	.supports_audio = false,
34458c2ecf20Sopenharmony_ci	.supports_hdcp = false,
34468c2ecf20Sopenharmony_ci	.regs = &tegra124_sor_regs,
34478c2ecf20Sopenharmony_ci	.has_nvdisplay = false,
34488c2ecf20Sopenharmony_ci	.xbar_cfg = tegra124_sor_xbar_cfg,
34498c2ecf20Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
34508c2ecf20Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
34518c2ecf20Sopenharmony_ci	.pre_emphasis = tegra132_sor_pre_emphasis,
34528c2ecf20Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
34538c2ecf20Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
34548c2ecf20Sopenharmony_ci};
34558c2ecf20Sopenharmony_ci
34568c2ecf20Sopenharmony_cistatic const struct tegra_sor_regs tegra210_sor_regs = {
34578c2ecf20Sopenharmony_ci	.head_state0 = 0x05,
34588c2ecf20Sopenharmony_ci	.head_state1 = 0x07,
34598c2ecf20Sopenharmony_ci	.head_state2 = 0x09,
34608c2ecf20Sopenharmony_ci	.head_state3 = 0x0b,
34618c2ecf20Sopenharmony_ci	.head_state4 = 0x0d,
34628c2ecf20Sopenharmony_ci	.head_state5 = 0x0f,
34638c2ecf20Sopenharmony_ci	.pll0 = 0x17,
34648c2ecf20Sopenharmony_ci	.pll1 = 0x18,
34658c2ecf20Sopenharmony_ci	.pll2 = 0x19,
34668c2ecf20Sopenharmony_ci	.pll3 = 0x1a,
34678c2ecf20Sopenharmony_ci	.dp_padctl0 = 0x5c,
34688c2ecf20Sopenharmony_ci	.dp_padctl2 = 0x73,
34698c2ecf20Sopenharmony_ci};
34708c2ecf20Sopenharmony_ci
34718c2ecf20Sopenharmony_cistatic const u8 tegra210_sor_xbar_cfg[5] = {
34728c2ecf20Sopenharmony_ci	2, 1, 0, 3, 4
34738c2ecf20Sopenharmony_ci};
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_cistatic const u8 tegra210_sor_lane_map[4] = {
34768c2ecf20Sopenharmony_ci	0, 1, 2, 3,
34778c2ecf20Sopenharmony_ci};
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_cistatic const struct tegra_sor_soc tegra210_sor = {
34808c2ecf20Sopenharmony_ci	.supports_lvds = false,
34818c2ecf20Sopenharmony_ci	.supports_hdmi = false,
34828c2ecf20Sopenharmony_ci	.supports_dp = true,
34838c2ecf20Sopenharmony_ci	.supports_audio = false,
34848c2ecf20Sopenharmony_ci	.supports_hdcp = false,
34858c2ecf20Sopenharmony_ci
34868c2ecf20Sopenharmony_ci	.regs = &tegra210_sor_regs,
34878c2ecf20Sopenharmony_ci	.has_nvdisplay = false,
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci	.xbar_cfg = tegra210_sor_xbar_cfg,
34908c2ecf20Sopenharmony_ci	.lane_map = tegra210_sor_lane_map,
34918c2ecf20Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
34928c2ecf20Sopenharmony_ci	.pre_emphasis = tegra124_sor_pre_emphasis,
34938c2ecf20Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
34948c2ecf20Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
34958c2ecf20Sopenharmony_ci};
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_cistatic const struct tegra_sor_soc tegra210_sor1 = {
34988c2ecf20Sopenharmony_ci	.supports_lvds = false,
34998c2ecf20Sopenharmony_ci	.supports_hdmi = true,
35008c2ecf20Sopenharmony_ci	.supports_dp = true,
35018c2ecf20Sopenharmony_ci	.supports_audio = true,
35028c2ecf20Sopenharmony_ci	.supports_hdcp = true,
35038c2ecf20Sopenharmony_ci
35048c2ecf20Sopenharmony_ci	.regs = &tegra210_sor_regs,
35058c2ecf20Sopenharmony_ci	.has_nvdisplay = false,
35068c2ecf20Sopenharmony_ci
35078c2ecf20Sopenharmony_ci	.num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults),
35088c2ecf20Sopenharmony_ci	.settings = tegra210_sor_hdmi_defaults,
35098c2ecf20Sopenharmony_ci	.xbar_cfg = tegra210_sor_xbar_cfg,
35108c2ecf20Sopenharmony_ci	.lane_map = tegra210_sor_lane_map,
35118c2ecf20Sopenharmony_ci	.voltage_swing = tegra124_sor_voltage_swing,
35128c2ecf20Sopenharmony_ci	.pre_emphasis = tegra124_sor_pre_emphasis,
35138c2ecf20Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
35148c2ecf20Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
35158c2ecf20Sopenharmony_ci};
35168c2ecf20Sopenharmony_ci
35178c2ecf20Sopenharmony_cistatic const struct tegra_sor_regs tegra186_sor_regs = {
35188c2ecf20Sopenharmony_ci	.head_state0 = 0x151,
35198c2ecf20Sopenharmony_ci	.head_state1 = 0x154,
35208c2ecf20Sopenharmony_ci	.head_state2 = 0x157,
35218c2ecf20Sopenharmony_ci	.head_state3 = 0x15a,
35228c2ecf20Sopenharmony_ci	.head_state4 = 0x15d,
35238c2ecf20Sopenharmony_ci	.head_state5 = 0x160,
35248c2ecf20Sopenharmony_ci	.pll0 = 0x163,
35258c2ecf20Sopenharmony_ci	.pll1 = 0x164,
35268c2ecf20Sopenharmony_ci	.pll2 = 0x165,
35278c2ecf20Sopenharmony_ci	.pll3 = 0x166,
35288c2ecf20Sopenharmony_ci	.dp_padctl0 = 0x168,
35298c2ecf20Sopenharmony_ci	.dp_padctl2 = 0x16a,
35308c2ecf20Sopenharmony_ci};
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_cistatic const u8 tegra186_sor_voltage_swing[4][4][4] = {
35338c2ecf20Sopenharmony_ci	{
35348c2ecf20Sopenharmony_ci		{ 0x13, 0x19, 0x1e, 0x28 },
35358c2ecf20Sopenharmony_ci		{ 0x1e, 0x25, 0x2d, },
35368c2ecf20Sopenharmony_ci		{ 0x28, 0x32, },
35378c2ecf20Sopenharmony_ci		{ 0x39, },
35388c2ecf20Sopenharmony_ci	}, {
35398c2ecf20Sopenharmony_ci		{ 0x12, 0x16, 0x1b, 0x25 },
35408c2ecf20Sopenharmony_ci		{ 0x1c, 0x23, 0x2a, },
35418c2ecf20Sopenharmony_ci		{ 0x25, 0x2f, },
35428c2ecf20Sopenharmony_ci		{ 0x37, }
35438c2ecf20Sopenharmony_ci	}, {
35448c2ecf20Sopenharmony_ci		{ 0x12, 0x16, 0x1a, 0x22 },
35458c2ecf20Sopenharmony_ci		{ 0x1b, 0x20, 0x27, },
35468c2ecf20Sopenharmony_ci		{ 0x24, 0x2d, },
35478c2ecf20Sopenharmony_ci		{ 0x35, },
35488c2ecf20Sopenharmony_ci	}, {
35498c2ecf20Sopenharmony_ci		{ 0x11, 0x14, 0x17, 0x1f },
35508c2ecf20Sopenharmony_ci		{ 0x19, 0x1e, 0x24, },
35518c2ecf20Sopenharmony_ci		{ 0x22, 0x2a, },
35528c2ecf20Sopenharmony_ci		{ 0x32, },
35538c2ecf20Sopenharmony_ci	},
35548c2ecf20Sopenharmony_ci};
35558c2ecf20Sopenharmony_ci
35568c2ecf20Sopenharmony_cistatic const u8 tegra186_sor_pre_emphasis[4][4][4] = {
35578c2ecf20Sopenharmony_ci	{
35588c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
35598c2ecf20Sopenharmony_ci		{ 0x01, 0x0e, 0x1d, },
35608c2ecf20Sopenharmony_ci		{ 0x01, 0x13, },
35618c2ecf20Sopenharmony_ci		{ 0x00, },
35628c2ecf20Sopenharmony_ci	}, {
35638c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
35648c2ecf20Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
35658c2ecf20Sopenharmony_ci		{ 0x00, 0x13, },
35668c2ecf20Sopenharmony_ci		{ 0x00 },
35678c2ecf20Sopenharmony_ci	}, {
35688c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x14, 0x24 },
35698c2ecf20Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
35708c2ecf20Sopenharmony_ci		{ 0x00, 0x13, },
35718c2ecf20Sopenharmony_ci		{ 0x00, },
35728c2ecf20Sopenharmony_ci	}, {
35738c2ecf20Sopenharmony_ci		{ 0x00, 0x08, 0x12, 0x24 },
35748c2ecf20Sopenharmony_ci		{ 0x00, 0x0e, 0x1d, },
35758c2ecf20Sopenharmony_ci		{ 0x00, 0x13, },
35768c2ecf20Sopenharmony_ci		{ 0x00, },
35778c2ecf20Sopenharmony_ci	},
35788c2ecf20Sopenharmony_ci};
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_cistatic const struct tegra_sor_soc tegra186_sor = {
35818c2ecf20Sopenharmony_ci	.supports_lvds = false,
35828c2ecf20Sopenharmony_ci	.supports_hdmi = true,
35838c2ecf20Sopenharmony_ci	.supports_dp = true,
35848c2ecf20Sopenharmony_ci	.supports_audio = true,
35858c2ecf20Sopenharmony_ci	.supports_hdcp = true,
35868c2ecf20Sopenharmony_ci
35878c2ecf20Sopenharmony_ci	.regs = &tegra186_sor_regs,
35888c2ecf20Sopenharmony_ci	.has_nvdisplay = true,
35898c2ecf20Sopenharmony_ci
35908c2ecf20Sopenharmony_ci	.num_settings = ARRAY_SIZE(tegra186_sor_hdmi_defaults),
35918c2ecf20Sopenharmony_ci	.settings = tegra186_sor_hdmi_defaults,
35928c2ecf20Sopenharmony_ci	.xbar_cfg = tegra124_sor_xbar_cfg,
35938c2ecf20Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
35948c2ecf20Sopenharmony_ci	.voltage_swing = tegra186_sor_voltage_swing,
35958c2ecf20Sopenharmony_ci	.pre_emphasis = tegra186_sor_pre_emphasis,
35968c2ecf20Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
35978c2ecf20Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
35988c2ecf20Sopenharmony_ci};
35998c2ecf20Sopenharmony_ci
36008c2ecf20Sopenharmony_cistatic const struct tegra_sor_regs tegra194_sor_regs = {
36018c2ecf20Sopenharmony_ci	.head_state0 = 0x151,
36028c2ecf20Sopenharmony_ci	.head_state1 = 0x155,
36038c2ecf20Sopenharmony_ci	.head_state2 = 0x159,
36048c2ecf20Sopenharmony_ci	.head_state3 = 0x15d,
36058c2ecf20Sopenharmony_ci	.head_state4 = 0x161,
36068c2ecf20Sopenharmony_ci	.head_state5 = 0x165,
36078c2ecf20Sopenharmony_ci	.pll0 = 0x169,
36088c2ecf20Sopenharmony_ci	.pll1 = 0x16a,
36098c2ecf20Sopenharmony_ci	.pll2 = 0x16b,
36108c2ecf20Sopenharmony_ci	.pll3 = 0x16c,
36118c2ecf20Sopenharmony_ci	.dp_padctl0 = 0x16e,
36128c2ecf20Sopenharmony_ci	.dp_padctl2 = 0x16f,
36138c2ecf20Sopenharmony_ci};
36148c2ecf20Sopenharmony_ci
36158c2ecf20Sopenharmony_cistatic const struct tegra_sor_soc tegra194_sor = {
36168c2ecf20Sopenharmony_ci	.supports_lvds = false,
36178c2ecf20Sopenharmony_ci	.supports_hdmi = true,
36188c2ecf20Sopenharmony_ci	.supports_dp = true,
36198c2ecf20Sopenharmony_ci	.supports_audio = true,
36208c2ecf20Sopenharmony_ci	.supports_hdcp = true,
36218c2ecf20Sopenharmony_ci
36228c2ecf20Sopenharmony_ci	.regs = &tegra194_sor_regs,
36238c2ecf20Sopenharmony_ci	.has_nvdisplay = true,
36248c2ecf20Sopenharmony_ci
36258c2ecf20Sopenharmony_ci	.num_settings = ARRAY_SIZE(tegra194_sor_hdmi_defaults),
36268c2ecf20Sopenharmony_ci	.settings = tegra194_sor_hdmi_defaults,
36278c2ecf20Sopenharmony_ci
36288c2ecf20Sopenharmony_ci	.xbar_cfg = tegra210_sor_xbar_cfg,
36298c2ecf20Sopenharmony_ci	.lane_map = tegra124_sor_lane_map,
36308c2ecf20Sopenharmony_ci	.voltage_swing = tegra186_sor_voltage_swing,
36318c2ecf20Sopenharmony_ci	.pre_emphasis = tegra186_sor_pre_emphasis,
36328c2ecf20Sopenharmony_ci	.post_cursor = tegra124_sor_post_cursor,
36338c2ecf20Sopenharmony_ci	.tx_pu = tegra124_sor_tx_pu,
36348c2ecf20Sopenharmony_ci};
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_sor_of_match[] = {
36378c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra194-sor", .data = &tegra194_sor },
36388c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra186-sor", .data = &tegra186_sor },
36398c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra210-sor1", .data = &tegra210_sor1 },
36408c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra210-sor", .data = &tegra210_sor },
36418c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra132-sor", .data = &tegra132_sor },
36428c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra124-sor", .data = &tegra124_sor },
36438c2ecf20Sopenharmony_ci	{ },
36448c2ecf20Sopenharmony_ci};
36458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_sor_of_match);
36468c2ecf20Sopenharmony_ci
36478c2ecf20Sopenharmony_cistatic int tegra_sor_parse_dt(struct tegra_sor *sor)
36488c2ecf20Sopenharmony_ci{
36498c2ecf20Sopenharmony_ci	struct device_node *np = sor->dev->of_node;
36508c2ecf20Sopenharmony_ci	u32 xbar_cfg[5];
36518c2ecf20Sopenharmony_ci	unsigned int i;
36528c2ecf20Sopenharmony_ci	u32 value;
36538c2ecf20Sopenharmony_ci	int err;
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_ci	if (sor->soc->has_nvdisplay) {
36568c2ecf20Sopenharmony_ci		err = of_property_read_u32(np, "nvidia,interface", &value);
36578c2ecf20Sopenharmony_ci		if (err < 0)
36588c2ecf20Sopenharmony_ci			return err;
36598c2ecf20Sopenharmony_ci
36608c2ecf20Sopenharmony_ci		sor->index = value;
36618c2ecf20Sopenharmony_ci
36628c2ecf20Sopenharmony_ci		/*
36638c2ecf20Sopenharmony_ci		 * override the default that we already set for Tegra210 and
36648c2ecf20Sopenharmony_ci		 * earlier
36658c2ecf20Sopenharmony_ci		 */
36668c2ecf20Sopenharmony_ci		sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index;
36678c2ecf20Sopenharmony_ci	} else {
36688c2ecf20Sopenharmony_ci		if (!sor->soc->supports_audio)
36698c2ecf20Sopenharmony_ci			sor->index = 0;
36708c2ecf20Sopenharmony_ci		else
36718c2ecf20Sopenharmony_ci			sor->index = 1;
36728c2ecf20Sopenharmony_ci	}
36738c2ecf20Sopenharmony_ci
36748c2ecf20Sopenharmony_ci	err = of_property_read_u32_array(np, "nvidia,xbar-cfg", xbar_cfg, 5);
36758c2ecf20Sopenharmony_ci	if (err < 0) {
36768c2ecf20Sopenharmony_ci		/* fall back to default per-SoC XBAR configuration */
36778c2ecf20Sopenharmony_ci		for (i = 0; i < 5; i++)
36788c2ecf20Sopenharmony_ci			sor->xbar_cfg[i] = sor->soc->xbar_cfg[i];
36798c2ecf20Sopenharmony_ci	} else {
36808c2ecf20Sopenharmony_ci		/* copy cells to SOR XBAR configuration */
36818c2ecf20Sopenharmony_ci		for (i = 0; i < 5; i++)
36828c2ecf20Sopenharmony_ci			sor->xbar_cfg[i] = xbar_cfg[i];
36838c2ecf20Sopenharmony_ci	}
36848c2ecf20Sopenharmony_ci
36858c2ecf20Sopenharmony_ci	return 0;
36868c2ecf20Sopenharmony_ci}
36878c2ecf20Sopenharmony_ci
36888c2ecf20Sopenharmony_cistatic irqreturn_t tegra_sor_irq(int irq, void *data)
36898c2ecf20Sopenharmony_ci{
36908c2ecf20Sopenharmony_ci	struct tegra_sor *sor = data;
36918c2ecf20Sopenharmony_ci	u32 value;
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci	value = tegra_sor_readl(sor, SOR_INT_STATUS);
36948c2ecf20Sopenharmony_ci	tegra_sor_writel(sor, value, SOR_INT_STATUS);
36958c2ecf20Sopenharmony_ci
36968c2ecf20Sopenharmony_ci	if (value & SOR_INT_CODEC_SCRATCH0) {
36978c2ecf20Sopenharmony_ci		value = tegra_sor_readl(sor, SOR_AUDIO_HDA_CODEC_SCRATCH0);
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_ci		if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) {
37008c2ecf20Sopenharmony_ci			unsigned int format;
37018c2ecf20Sopenharmony_ci
37028c2ecf20Sopenharmony_ci			format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK;
37038c2ecf20Sopenharmony_ci
37048c2ecf20Sopenharmony_ci			tegra_hda_parse_format(format, &sor->format);
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci			if (sor->ops->audio_enable)
37078c2ecf20Sopenharmony_ci				sor->ops->audio_enable(sor);
37088c2ecf20Sopenharmony_ci		} else {
37098c2ecf20Sopenharmony_ci			if (sor->ops->audio_disable)
37108c2ecf20Sopenharmony_ci				sor->ops->audio_disable(sor);
37118c2ecf20Sopenharmony_ci		}
37128c2ecf20Sopenharmony_ci	}
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
37158c2ecf20Sopenharmony_ci}
37168c2ecf20Sopenharmony_ci
37178c2ecf20Sopenharmony_cistatic int tegra_sor_probe(struct platform_device *pdev)
37188c2ecf20Sopenharmony_ci{
37198c2ecf20Sopenharmony_ci	struct device_node *np;
37208c2ecf20Sopenharmony_ci	struct tegra_sor *sor;
37218c2ecf20Sopenharmony_ci	struct resource *regs;
37228c2ecf20Sopenharmony_ci	int err;
37238c2ecf20Sopenharmony_ci
37248c2ecf20Sopenharmony_ci	sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL);
37258c2ecf20Sopenharmony_ci	if (!sor)
37268c2ecf20Sopenharmony_ci		return -ENOMEM;
37278c2ecf20Sopenharmony_ci
37288c2ecf20Sopenharmony_ci	sor->soc = of_device_get_match_data(&pdev->dev);
37298c2ecf20Sopenharmony_ci	sor->output.dev = sor->dev = &pdev->dev;
37308c2ecf20Sopenharmony_ci
37318c2ecf20Sopenharmony_ci	sor->settings = devm_kmemdup(&pdev->dev, sor->soc->settings,
37328c2ecf20Sopenharmony_ci				     sor->soc->num_settings *
37338c2ecf20Sopenharmony_ci					sizeof(*sor->settings),
37348c2ecf20Sopenharmony_ci				     GFP_KERNEL);
37358c2ecf20Sopenharmony_ci	if (!sor->settings)
37368c2ecf20Sopenharmony_ci		return -ENOMEM;
37378c2ecf20Sopenharmony_ci
37388c2ecf20Sopenharmony_ci	sor->num_settings = sor->soc->num_settings;
37398c2ecf20Sopenharmony_ci
37408c2ecf20Sopenharmony_ci	np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0);
37418c2ecf20Sopenharmony_ci	if (np) {
37428c2ecf20Sopenharmony_ci		sor->aux = drm_dp_aux_find_by_of_node(np);
37438c2ecf20Sopenharmony_ci		of_node_put(np);
37448c2ecf20Sopenharmony_ci
37458c2ecf20Sopenharmony_ci		if (!sor->aux)
37468c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
37478c2ecf20Sopenharmony_ci
37488c2ecf20Sopenharmony_ci		if (get_device(&sor->aux->ddc.dev)) {
37498c2ecf20Sopenharmony_ci			if (try_module_get(sor->aux->ddc.owner))
37508c2ecf20Sopenharmony_ci				sor->output.ddc = &sor->aux->ddc;
37518c2ecf20Sopenharmony_ci			else
37528c2ecf20Sopenharmony_ci				put_device(&sor->aux->ddc.dev);
37538c2ecf20Sopenharmony_ci		}
37548c2ecf20Sopenharmony_ci	}
37558c2ecf20Sopenharmony_ci
37568c2ecf20Sopenharmony_ci	if (!sor->aux) {
37578c2ecf20Sopenharmony_ci		if (sor->soc->supports_hdmi) {
37588c2ecf20Sopenharmony_ci			sor->ops = &tegra_sor_hdmi_ops;
37598c2ecf20Sopenharmony_ci			sor->pad = TEGRA_IO_PAD_HDMI;
37608c2ecf20Sopenharmony_ci		} else if (sor->soc->supports_lvds) {
37618c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "LVDS not supported yet\n");
37628c2ecf20Sopenharmony_ci			return -ENODEV;
37638c2ecf20Sopenharmony_ci		} else {
37648c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "unknown (non-DP) support\n");
37658c2ecf20Sopenharmony_ci			return -ENODEV;
37668c2ecf20Sopenharmony_ci		}
37678c2ecf20Sopenharmony_ci	} else {
37688c2ecf20Sopenharmony_ci		np = of_parse_phandle(pdev->dev.of_node, "nvidia,panel", 0);
37698c2ecf20Sopenharmony_ci		/*
37708c2ecf20Sopenharmony_ci		 * No need to keep this around since we only use it as a check
37718c2ecf20Sopenharmony_ci		 * to see if a panel is connected (eDP) or not (DP).
37728c2ecf20Sopenharmony_ci		 */
37738c2ecf20Sopenharmony_ci		of_node_put(np);
37748c2ecf20Sopenharmony_ci
37758c2ecf20Sopenharmony_ci		sor->ops = &tegra_sor_dp_ops;
37768c2ecf20Sopenharmony_ci		sor->pad = TEGRA_IO_PAD_LVDS;
37778c2ecf20Sopenharmony_ci	}
37788c2ecf20Sopenharmony_ci
37798c2ecf20Sopenharmony_ci	err = tegra_sor_parse_dt(sor);
37808c2ecf20Sopenharmony_ci	if (err < 0)
37818c2ecf20Sopenharmony_ci		return err;
37828c2ecf20Sopenharmony_ci
37838c2ecf20Sopenharmony_ci	err = tegra_output_probe(&sor->output);
37848c2ecf20Sopenharmony_ci	if (err < 0)
37858c2ecf20Sopenharmony_ci		return dev_err_probe(&pdev->dev, err,
37868c2ecf20Sopenharmony_ci				     "failed to probe output\n");
37878c2ecf20Sopenharmony_ci
37888c2ecf20Sopenharmony_ci	if (sor->ops && sor->ops->probe) {
37898c2ecf20Sopenharmony_ci		err = sor->ops->probe(sor);
37908c2ecf20Sopenharmony_ci		if (err < 0) {
37918c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to probe %s: %d\n",
37928c2ecf20Sopenharmony_ci				sor->ops->name, err);
37938c2ecf20Sopenharmony_ci			goto remove;
37948c2ecf20Sopenharmony_ci		}
37958c2ecf20Sopenharmony_ci	}
37968c2ecf20Sopenharmony_ci
37978c2ecf20Sopenharmony_ci	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
37988c2ecf20Sopenharmony_ci	sor->regs = devm_ioremap_resource(&pdev->dev, regs);
37998c2ecf20Sopenharmony_ci	if (IS_ERR(sor->regs)) {
38008c2ecf20Sopenharmony_ci		err = PTR_ERR(sor->regs);
38018c2ecf20Sopenharmony_ci		goto remove;
38028c2ecf20Sopenharmony_ci	}
38038c2ecf20Sopenharmony_ci
38048c2ecf20Sopenharmony_ci	err = platform_get_irq(pdev, 0);
38058c2ecf20Sopenharmony_ci	if (err < 0) {
38068c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
38078c2ecf20Sopenharmony_ci		goto remove;
38088c2ecf20Sopenharmony_ci	}
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_ci	sor->irq = err;
38118c2ecf20Sopenharmony_ci
38128c2ecf20Sopenharmony_ci	err = devm_request_irq(sor->dev, sor->irq, tegra_sor_irq, 0,
38138c2ecf20Sopenharmony_ci			       dev_name(sor->dev), sor);
38148c2ecf20Sopenharmony_ci	if (err < 0) {
38158c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
38168c2ecf20Sopenharmony_ci		goto remove;
38178c2ecf20Sopenharmony_ci	}
38188c2ecf20Sopenharmony_ci
38198c2ecf20Sopenharmony_ci	sor->rst = devm_reset_control_get_exclusive_released(&pdev->dev, "sor");
38208c2ecf20Sopenharmony_ci	if (IS_ERR(sor->rst)) {
38218c2ecf20Sopenharmony_ci		err = PTR_ERR(sor->rst);
38228c2ecf20Sopenharmony_ci
38238c2ecf20Sopenharmony_ci		if (err != -EBUSY || WARN_ON(!pdev->dev.pm_domain)) {
38248c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to get reset control: %d\n",
38258c2ecf20Sopenharmony_ci				err);
38268c2ecf20Sopenharmony_ci			goto remove;
38278c2ecf20Sopenharmony_ci		}
38288c2ecf20Sopenharmony_ci
38298c2ecf20Sopenharmony_ci		/*
38308c2ecf20Sopenharmony_ci		 * At this point, the reset control is most likely being used
38318c2ecf20Sopenharmony_ci		 * by the generic power domain implementation. With any luck
38328c2ecf20Sopenharmony_ci		 * the power domain will have taken care of resetting the SOR
38338c2ecf20Sopenharmony_ci		 * and we don't have to do anything.
38348c2ecf20Sopenharmony_ci		 */
38358c2ecf20Sopenharmony_ci		sor->rst = NULL;
38368c2ecf20Sopenharmony_ci	}
38378c2ecf20Sopenharmony_ci
38388c2ecf20Sopenharmony_ci	sor->clk = devm_clk_get(&pdev->dev, NULL);
38398c2ecf20Sopenharmony_ci	if (IS_ERR(sor->clk)) {
38408c2ecf20Sopenharmony_ci		err = PTR_ERR(sor->clk);
38418c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get module clock: %d\n", err);
38428c2ecf20Sopenharmony_ci		goto remove;
38438c2ecf20Sopenharmony_ci	}
38448c2ecf20Sopenharmony_ci
38458c2ecf20Sopenharmony_ci	if (sor->soc->supports_hdmi || sor->soc->supports_dp) {
38468c2ecf20Sopenharmony_ci		struct device_node *np = pdev->dev.of_node;
38478c2ecf20Sopenharmony_ci		const char *name;
38488c2ecf20Sopenharmony_ci
38498c2ecf20Sopenharmony_ci		/*
38508c2ecf20Sopenharmony_ci		 * For backwards compatibility with Tegra210 device trees,
38518c2ecf20Sopenharmony_ci		 * fall back to the old clock name "source" if the new "out"
38528c2ecf20Sopenharmony_ci		 * clock is not available.
38538c2ecf20Sopenharmony_ci		 */
38548c2ecf20Sopenharmony_ci		if (of_property_match_string(np, "clock-names", "out") < 0)
38558c2ecf20Sopenharmony_ci			name = "source";
38568c2ecf20Sopenharmony_ci		else
38578c2ecf20Sopenharmony_ci			name = "out";
38588c2ecf20Sopenharmony_ci
38598c2ecf20Sopenharmony_ci		sor->clk_out = devm_clk_get(&pdev->dev, name);
38608c2ecf20Sopenharmony_ci		if (IS_ERR(sor->clk_out)) {
38618c2ecf20Sopenharmony_ci			err = PTR_ERR(sor->clk_out);
38628c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to get %s clock: %d\n",
38638c2ecf20Sopenharmony_ci				name, err);
38648c2ecf20Sopenharmony_ci			goto remove;
38658c2ecf20Sopenharmony_ci		}
38668c2ecf20Sopenharmony_ci	} else {
38678c2ecf20Sopenharmony_ci		/* fall back to the module clock on SOR0 (eDP/LVDS only) */
38688c2ecf20Sopenharmony_ci		sor->clk_out = sor->clk;
38698c2ecf20Sopenharmony_ci	}
38708c2ecf20Sopenharmony_ci
38718c2ecf20Sopenharmony_ci	sor->clk_parent = devm_clk_get(&pdev->dev, "parent");
38728c2ecf20Sopenharmony_ci	if (IS_ERR(sor->clk_parent)) {
38738c2ecf20Sopenharmony_ci		err = PTR_ERR(sor->clk_parent);
38748c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get parent clock: %d\n", err);
38758c2ecf20Sopenharmony_ci		goto remove;
38768c2ecf20Sopenharmony_ci	}
38778c2ecf20Sopenharmony_ci
38788c2ecf20Sopenharmony_ci	sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
38798c2ecf20Sopenharmony_ci	if (IS_ERR(sor->clk_safe)) {
38808c2ecf20Sopenharmony_ci		err = PTR_ERR(sor->clk_safe);
38818c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get safe clock: %d\n", err);
38828c2ecf20Sopenharmony_ci		goto remove;
38838c2ecf20Sopenharmony_ci	}
38848c2ecf20Sopenharmony_ci
38858c2ecf20Sopenharmony_ci	sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
38868c2ecf20Sopenharmony_ci	if (IS_ERR(sor->clk_dp)) {
38878c2ecf20Sopenharmony_ci		err = PTR_ERR(sor->clk_dp);
38888c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get DP clock: %d\n", err);
38898c2ecf20Sopenharmony_ci		goto remove;
38908c2ecf20Sopenharmony_ci	}
38918c2ecf20Sopenharmony_ci
38928c2ecf20Sopenharmony_ci	/*
38938c2ecf20Sopenharmony_ci	 * Starting with Tegra186, the BPMP provides an implementation for
38948c2ecf20Sopenharmony_ci	 * the pad output clock, so we have to look it up from device tree.
38958c2ecf20Sopenharmony_ci	 */
38968c2ecf20Sopenharmony_ci	sor->clk_pad = devm_clk_get(&pdev->dev, "pad");
38978c2ecf20Sopenharmony_ci	if (IS_ERR(sor->clk_pad)) {
38988c2ecf20Sopenharmony_ci		if (sor->clk_pad != ERR_PTR(-ENOENT)) {
38998c2ecf20Sopenharmony_ci			err = PTR_ERR(sor->clk_pad);
39008c2ecf20Sopenharmony_ci			goto remove;
39018c2ecf20Sopenharmony_ci		}
39028c2ecf20Sopenharmony_ci
39038c2ecf20Sopenharmony_ci		/*
39048c2ecf20Sopenharmony_ci		 * If the pad output clock is not available, then we assume
39058c2ecf20Sopenharmony_ci		 * we're on Tegra210 or earlier and have to provide our own
39068c2ecf20Sopenharmony_ci		 * implementation.
39078c2ecf20Sopenharmony_ci		 */
39088c2ecf20Sopenharmony_ci		sor->clk_pad = NULL;
39098c2ecf20Sopenharmony_ci	}
39108c2ecf20Sopenharmony_ci
39118c2ecf20Sopenharmony_ci	/*
39128c2ecf20Sopenharmony_ci	 * The bootloader may have set up the SOR such that it's module clock
39138c2ecf20Sopenharmony_ci	 * is sourced by one of the display PLLs. However, that doesn't work
39148c2ecf20Sopenharmony_ci	 * without properly having set up other bits of the SOR.
39158c2ecf20Sopenharmony_ci	 */
39168c2ecf20Sopenharmony_ci	err = clk_set_parent(sor->clk_out, sor->clk_safe);
39178c2ecf20Sopenharmony_ci	if (err < 0) {
39188c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to use safe clock: %d\n", err);
39198c2ecf20Sopenharmony_ci		goto remove;
39208c2ecf20Sopenharmony_ci	}
39218c2ecf20Sopenharmony_ci
39228c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, sor);
39238c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_ci	host1x_client_init(&sor->client);
39268c2ecf20Sopenharmony_ci	sor->client.ops = &sor_client_ops;
39278c2ecf20Sopenharmony_ci	sor->client.dev = &pdev->dev;
39288c2ecf20Sopenharmony_ci
39298c2ecf20Sopenharmony_ci	/*
39308c2ecf20Sopenharmony_ci	 * On Tegra210 and earlier, provide our own implementation for the
39318c2ecf20Sopenharmony_ci	 * pad output clock.
39328c2ecf20Sopenharmony_ci	 */
39338c2ecf20Sopenharmony_ci	if (!sor->clk_pad) {
39348c2ecf20Sopenharmony_ci		char *name;
39358c2ecf20Sopenharmony_ci
39368c2ecf20Sopenharmony_ci		name = devm_kasprintf(sor->dev, GFP_KERNEL, "sor%u_pad_clkout",
39378c2ecf20Sopenharmony_ci				      sor->index);
39388c2ecf20Sopenharmony_ci		if (!name) {
39398c2ecf20Sopenharmony_ci			err = -ENOMEM;
39408c2ecf20Sopenharmony_ci			goto uninit;
39418c2ecf20Sopenharmony_ci		}
39428c2ecf20Sopenharmony_ci
39438c2ecf20Sopenharmony_ci		err = host1x_client_resume(&sor->client);
39448c2ecf20Sopenharmony_ci		if (err < 0) {
39458c2ecf20Sopenharmony_ci			dev_err(sor->dev, "failed to resume: %d\n", err);
39468c2ecf20Sopenharmony_ci			goto uninit;
39478c2ecf20Sopenharmony_ci		}
39488c2ecf20Sopenharmony_ci
39498c2ecf20Sopenharmony_ci		sor->clk_pad = tegra_clk_sor_pad_register(sor, name);
39508c2ecf20Sopenharmony_ci		host1x_client_suspend(&sor->client);
39518c2ecf20Sopenharmony_ci	}
39528c2ecf20Sopenharmony_ci
39538c2ecf20Sopenharmony_ci	if (IS_ERR(sor->clk_pad)) {
39548c2ecf20Sopenharmony_ci		err = PTR_ERR(sor->clk_pad);
39558c2ecf20Sopenharmony_ci		dev_err(sor->dev, "failed to register SOR pad clock: %d\n",
39568c2ecf20Sopenharmony_ci			err);
39578c2ecf20Sopenharmony_ci		goto uninit;
39588c2ecf20Sopenharmony_ci	}
39598c2ecf20Sopenharmony_ci
39608c2ecf20Sopenharmony_ci	err = __host1x_client_register(&sor->client);
39618c2ecf20Sopenharmony_ci	if (err < 0) {
39628c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
39638c2ecf20Sopenharmony_ci			err);
39648c2ecf20Sopenharmony_ci		goto uninit;
39658c2ecf20Sopenharmony_ci	}
39668c2ecf20Sopenharmony_ci
39678c2ecf20Sopenharmony_ci	return 0;
39688c2ecf20Sopenharmony_ci
39698c2ecf20Sopenharmony_ciuninit:
39708c2ecf20Sopenharmony_ci	host1x_client_exit(&sor->client);
39718c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
39728c2ecf20Sopenharmony_ciremove:
39738c2ecf20Sopenharmony_ci	tegra_output_remove(&sor->output);
39748c2ecf20Sopenharmony_ci	return err;
39758c2ecf20Sopenharmony_ci}
39768c2ecf20Sopenharmony_ci
39778c2ecf20Sopenharmony_cistatic int tegra_sor_remove(struct platform_device *pdev)
39788c2ecf20Sopenharmony_ci{
39798c2ecf20Sopenharmony_ci	struct tegra_sor *sor = platform_get_drvdata(pdev);
39808c2ecf20Sopenharmony_ci	int err;
39818c2ecf20Sopenharmony_ci
39828c2ecf20Sopenharmony_ci	err = host1x_client_unregister(&sor->client);
39838c2ecf20Sopenharmony_ci	if (err < 0) {
39848c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
39858c2ecf20Sopenharmony_ci			err);
39868c2ecf20Sopenharmony_ci		return err;
39878c2ecf20Sopenharmony_ci	}
39888c2ecf20Sopenharmony_ci
39898c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
39908c2ecf20Sopenharmony_ci
39918c2ecf20Sopenharmony_ci	tegra_output_remove(&sor->output);
39928c2ecf20Sopenharmony_ci
39938c2ecf20Sopenharmony_ci	return 0;
39948c2ecf20Sopenharmony_ci}
39958c2ecf20Sopenharmony_ci
39968c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_sor_suspend(struct device *dev)
39978c2ecf20Sopenharmony_ci{
39988c2ecf20Sopenharmony_ci	struct tegra_sor *sor = dev_get_drvdata(dev);
39998c2ecf20Sopenharmony_ci	int err;
40008c2ecf20Sopenharmony_ci
40018c2ecf20Sopenharmony_ci	err = tegra_output_suspend(&sor->output);
40028c2ecf20Sopenharmony_ci	if (err < 0) {
40038c2ecf20Sopenharmony_ci		dev_err(dev, "failed to suspend output: %d\n", err);
40048c2ecf20Sopenharmony_ci		return err;
40058c2ecf20Sopenharmony_ci	}
40068c2ecf20Sopenharmony_ci
40078c2ecf20Sopenharmony_ci	if (sor->hdmi_supply) {
40088c2ecf20Sopenharmony_ci		err = regulator_disable(sor->hdmi_supply);
40098c2ecf20Sopenharmony_ci		if (err < 0) {
40108c2ecf20Sopenharmony_ci			tegra_output_resume(&sor->output);
40118c2ecf20Sopenharmony_ci			return err;
40128c2ecf20Sopenharmony_ci		}
40138c2ecf20Sopenharmony_ci	}
40148c2ecf20Sopenharmony_ci
40158c2ecf20Sopenharmony_ci	return 0;
40168c2ecf20Sopenharmony_ci}
40178c2ecf20Sopenharmony_ci
40188c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_sor_resume(struct device *dev)
40198c2ecf20Sopenharmony_ci{
40208c2ecf20Sopenharmony_ci	struct tegra_sor *sor = dev_get_drvdata(dev);
40218c2ecf20Sopenharmony_ci	int err;
40228c2ecf20Sopenharmony_ci
40238c2ecf20Sopenharmony_ci	if (sor->hdmi_supply) {
40248c2ecf20Sopenharmony_ci		err = regulator_enable(sor->hdmi_supply);
40258c2ecf20Sopenharmony_ci		if (err < 0)
40268c2ecf20Sopenharmony_ci			return err;
40278c2ecf20Sopenharmony_ci	}
40288c2ecf20Sopenharmony_ci
40298c2ecf20Sopenharmony_ci	err = tegra_output_resume(&sor->output);
40308c2ecf20Sopenharmony_ci	if (err < 0) {
40318c2ecf20Sopenharmony_ci		dev_err(dev, "failed to resume output: %d\n", err);
40328c2ecf20Sopenharmony_ci
40338c2ecf20Sopenharmony_ci		if (sor->hdmi_supply)
40348c2ecf20Sopenharmony_ci			regulator_disable(sor->hdmi_supply);
40358c2ecf20Sopenharmony_ci
40368c2ecf20Sopenharmony_ci		return err;
40378c2ecf20Sopenharmony_ci	}
40388c2ecf20Sopenharmony_ci
40398c2ecf20Sopenharmony_ci	return 0;
40408c2ecf20Sopenharmony_ci}
40418c2ecf20Sopenharmony_ci
40428c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra_sor_pm_ops = {
40438c2ecf20Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(tegra_sor_suspend, tegra_sor_resume)
40448c2ecf20Sopenharmony_ci};
40458c2ecf20Sopenharmony_ci
40468c2ecf20Sopenharmony_cistruct platform_driver tegra_sor_driver = {
40478c2ecf20Sopenharmony_ci	.driver = {
40488c2ecf20Sopenharmony_ci		.name = "tegra-sor",
40498c2ecf20Sopenharmony_ci		.of_match_table = tegra_sor_of_match,
40508c2ecf20Sopenharmony_ci		.pm = &tegra_sor_pm_ops,
40518c2ecf20Sopenharmony_ci	},
40528c2ecf20Sopenharmony_ci	.probe = tegra_sor_probe,
40538c2ecf20Sopenharmony_ci	.remove = tegra_sor_remove,
40548c2ecf20Sopenharmony_ci};
4055