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