162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH 462306a36Sopenharmony_ci * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/debugfs.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/hdmi.h> 1162306a36Sopenharmony_ci#include <linux/math64.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/pm_opp.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1862306a36Sopenharmony_ci#include <linux/reset.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <soc/tegra/common.h> 2162306a36Sopenharmony_ci#include <sound/hdmi-codec.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <drm/drm_bridge_connector.h> 2462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2562306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2662306a36Sopenharmony_ci#include <drm/drm_debugfs.h> 2762306a36Sopenharmony_ci#include <drm/drm_file.h> 2862306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2962306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3062306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "hda.h" 3362306a36Sopenharmony_ci#include "hdmi.h" 3462306a36Sopenharmony_ci#include "drm.h" 3562306a36Sopenharmony_ci#include "dc.h" 3662306a36Sopenharmony_ci#include "trace.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define HDMI_ELD_BUFFER_SIZE 96 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct tmds_config { 4162306a36Sopenharmony_ci unsigned int pclk; 4262306a36Sopenharmony_ci u32 pll0; 4362306a36Sopenharmony_ci u32 pll1; 4462306a36Sopenharmony_ci u32 pe_current; 4562306a36Sopenharmony_ci u32 drive_current; 4662306a36Sopenharmony_ci u32 peak_current; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct tegra_hdmi_config { 5062306a36Sopenharmony_ci const struct tmds_config *tmds; 5162306a36Sopenharmony_ci unsigned int num_tmds; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci unsigned long fuse_override_offset; 5462306a36Sopenharmony_ci u32 fuse_override_value; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci bool has_sor_io_peak_current; 5762306a36Sopenharmony_ci bool has_hda; 5862306a36Sopenharmony_ci bool has_hbr; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct tegra_hdmi { 6262306a36Sopenharmony_ci struct host1x_client client; 6362306a36Sopenharmony_ci struct tegra_output output; 6462306a36Sopenharmony_ci struct device *dev; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci struct regulator *hdmi; 6762306a36Sopenharmony_ci struct regulator *pll; 6862306a36Sopenharmony_ci struct regulator *vdd; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci void __iomem *regs; 7162306a36Sopenharmony_ci unsigned int irq; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci struct clk *clk_parent; 7462306a36Sopenharmony_ci struct clk *clk; 7562306a36Sopenharmony_ci struct reset_control *rst; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci const struct tegra_hdmi_config *config; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci unsigned int audio_source; 8062306a36Sopenharmony_ci struct tegra_hda_format format; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci unsigned int pixel_clock; 8362306a36Sopenharmony_ci bool stereo; 8462306a36Sopenharmony_ci bool dvi; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci struct drm_info_list *debugfs_files; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci struct platform_device *audio_pdev; 8962306a36Sopenharmony_ci struct mutex audio_lock; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic inline struct tegra_hdmi * 9362306a36Sopenharmony_cihost1x_client_to_hdmi(struct host1x_client *client) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci return container_of(client, struct tegra_hdmi, client); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic inline struct tegra_hdmi *to_hdmi(struct tegra_output *output) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci return container_of(output, struct tegra_hdmi, output); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define HDMI_AUDIOCLK_FREQ 216000000 10462306a36Sopenharmony_ci#define HDMI_REKEY_DEFAULT 56 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cienum { 10762306a36Sopenharmony_ci AUTO = 0, 10862306a36Sopenharmony_ci SPDIF, 10962306a36Sopenharmony_ci HDA, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline u32 tegra_hdmi_readl(struct tegra_hdmi *hdmi, 11362306a36Sopenharmony_ci unsigned int offset) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u32 value = readl(hdmi->regs + (offset << 2)); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci trace_hdmi_readl(hdmi->dev, offset, value); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return value; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, u32 value, 12362306a36Sopenharmony_ci unsigned int offset) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci trace_hdmi_writel(hdmi->dev, offset, value); 12662306a36Sopenharmony_ci writel(value, hdmi->regs + (offset << 2)); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistruct tegra_hdmi_audio_config { 13062306a36Sopenharmony_ci unsigned int n; 13162306a36Sopenharmony_ci unsigned int cts; 13262306a36Sopenharmony_ci unsigned int aval; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic const struct tmds_config tegra20_tmds_config[] = { 13662306a36Sopenharmony_ci { /* slow pixel clock modes */ 13762306a36Sopenharmony_ci .pclk = 27000000, 13862306a36Sopenharmony_ci .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | 13962306a36Sopenharmony_ci SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | 14062306a36Sopenharmony_ci SOR_PLL_TX_REG_LOAD(3), 14162306a36Sopenharmony_ci .pll1 = SOR_PLL_TMDS_TERM_ENABLE, 14262306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | 14362306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_0_0_mA) | 14462306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_0_0_mA) | 14562306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_0_0_mA), 14662306a36Sopenharmony_ci .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | 14762306a36Sopenharmony_ci DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | 14862306a36Sopenharmony_ci DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | 14962306a36Sopenharmony_ci DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), 15062306a36Sopenharmony_ci }, 15162306a36Sopenharmony_ci { /* high pixel clock modes */ 15262306a36Sopenharmony_ci .pclk = UINT_MAX, 15362306a36Sopenharmony_ci .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | 15462306a36Sopenharmony_ci SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | 15562306a36Sopenharmony_ci SOR_PLL_TX_REG_LOAD(3), 15662306a36Sopenharmony_ci .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, 15762306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) | 15862306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_6_0_mA) | 15962306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_6_0_mA) | 16062306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_6_0_mA), 16162306a36Sopenharmony_ci .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | 16262306a36Sopenharmony_ci DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | 16362306a36Sopenharmony_ci DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | 16462306a36Sopenharmony_ci DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), 16562306a36Sopenharmony_ci }, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic const struct tmds_config tegra30_tmds_config[] = { 16962306a36Sopenharmony_ci { /* 480p modes */ 17062306a36Sopenharmony_ci .pclk = 27000000, 17162306a36Sopenharmony_ci .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | 17262306a36Sopenharmony_ci SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | 17362306a36Sopenharmony_ci SOR_PLL_TX_REG_LOAD(0), 17462306a36Sopenharmony_ci .pll1 = SOR_PLL_TMDS_TERM_ENABLE, 17562306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | 17662306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_0_0_mA) | 17762306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_0_0_mA) | 17862306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_0_0_mA), 17962306a36Sopenharmony_ci .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | 18062306a36Sopenharmony_ci DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | 18162306a36Sopenharmony_ci DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | 18262306a36Sopenharmony_ci DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), 18362306a36Sopenharmony_ci }, { /* 720p modes */ 18462306a36Sopenharmony_ci .pclk = 74250000, 18562306a36Sopenharmony_ci .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | 18662306a36Sopenharmony_ci SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | 18762306a36Sopenharmony_ci SOR_PLL_TX_REG_LOAD(0), 18862306a36Sopenharmony_ci .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, 18962306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | 19062306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_5_0_mA) | 19162306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_5_0_mA) | 19262306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_5_0_mA), 19362306a36Sopenharmony_ci .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | 19462306a36Sopenharmony_ci DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | 19562306a36Sopenharmony_ci DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | 19662306a36Sopenharmony_ci DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), 19762306a36Sopenharmony_ci }, { /* 1080p modes */ 19862306a36Sopenharmony_ci .pclk = UINT_MAX, 19962306a36Sopenharmony_ci .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | 20062306a36Sopenharmony_ci SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(3) | 20162306a36Sopenharmony_ci SOR_PLL_TX_REG_LOAD(0), 20262306a36Sopenharmony_ci .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, 20362306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | 20462306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_5_0_mA) | 20562306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_5_0_mA) | 20662306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_5_0_mA), 20762306a36Sopenharmony_ci .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | 20862306a36Sopenharmony_ci DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | 20962306a36Sopenharmony_ci DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | 21062306a36Sopenharmony_ci DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), 21162306a36Sopenharmony_ci }, 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic const struct tmds_config tegra114_tmds_config[] = { 21562306a36Sopenharmony_ci { /* 480p/576p / 25.2MHz/27MHz modes */ 21662306a36Sopenharmony_ci .pclk = 27000000, 21762306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 21862306a36Sopenharmony_ci SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL, 21962306a36Sopenharmony_ci .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0), 22062306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) | 22162306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_0_mA_T114) | 22262306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_0_mA_T114) | 22362306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_0_mA_T114), 22462306a36Sopenharmony_ci .drive_current = 22562306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) | 22662306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) | 22762306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) | 22862306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114), 22962306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | 23062306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | 23162306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | 23262306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), 23362306a36Sopenharmony_ci }, { /* 720p / 74.25MHz modes */ 23462306a36Sopenharmony_ci .pclk = 74250000, 23562306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 23662306a36Sopenharmony_ci SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL, 23762306a36Sopenharmony_ci .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) | 23862306a36Sopenharmony_ci SOR_PLL_TMDS_TERMADJ(0), 23962306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) | 24062306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_15_mA_T114) | 24162306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_15_mA_T114) | 24262306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_15_mA_T114), 24362306a36Sopenharmony_ci .drive_current = 24462306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) | 24562306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) | 24662306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) | 24762306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114), 24862306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | 24962306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | 25062306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | 25162306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), 25262306a36Sopenharmony_ci }, { /* 1080p / 148.5MHz modes */ 25362306a36Sopenharmony_ci .pclk = 148500000, 25462306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 25562306a36Sopenharmony_ci SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL, 25662306a36Sopenharmony_ci .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) | 25762306a36Sopenharmony_ci SOR_PLL_TMDS_TERMADJ(0), 25862306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) | 25962306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_10_mA_T114) | 26062306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_10_mA_T114) | 26162306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_10_mA_T114), 26262306a36Sopenharmony_ci .drive_current = 26362306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) | 26462306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) | 26562306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) | 26662306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114), 26762306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | 26862306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | 26962306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | 27062306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), 27162306a36Sopenharmony_ci }, { /* 225/297MHz modes */ 27262306a36Sopenharmony_ci .pclk = UINT_MAX, 27362306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 27462306a36Sopenharmony_ci SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL, 27562306a36Sopenharmony_ci .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7) 27662306a36Sopenharmony_ci | SOR_PLL_TMDS_TERM_ENABLE, 27762306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) | 27862306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_0_mA_T114) | 27962306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_0_mA_T114) | 28062306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_0_mA_T114), 28162306a36Sopenharmony_ci .drive_current = 28262306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) | 28362306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) | 28462306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) | 28562306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114), 28662306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) | 28762306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) | 28862306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) | 28962306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA), 29062306a36Sopenharmony_ci }, 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const struct tmds_config tegra124_tmds_config[] = { 29462306a36Sopenharmony_ci { /* 480p/576p / 25.2MHz/27MHz modes */ 29562306a36Sopenharmony_ci .pclk = 27000000, 29662306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 29762306a36Sopenharmony_ci SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL, 29862306a36Sopenharmony_ci .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0), 29962306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) | 30062306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_0_mA_T114) | 30162306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_0_mA_T114) | 30262306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_0_mA_T114), 30362306a36Sopenharmony_ci .drive_current = 30462306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) | 30562306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) | 30662306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) | 30762306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114), 30862306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | 30962306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | 31062306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | 31162306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), 31262306a36Sopenharmony_ci }, { /* 720p / 74.25MHz modes */ 31362306a36Sopenharmony_ci .pclk = 74250000, 31462306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 31562306a36Sopenharmony_ci SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL, 31662306a36Sopenharmony_ci .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) | 31762306a36Sopenharmony_ci SOR_PLL_TMDS_TERMADJ(0), 31862306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) | 31962306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_15_mA_T114) | 32062306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_15_mA_T114) | 32162306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_15_mA_T114), 32262306a36Sopenharmony_ci .drive_current = 32362306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) | 32462306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) | 32562306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) | 32662306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114), 32762306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | 32862306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | 32962306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | 33062306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), 33162306a36Sopenharmony_ci }, { /* 1080p / 148.5MHz modes */ 33262306a36Sopenharmony_ci .pclk = 148500000, 33362306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 33462306a36Sopenharmony_ci SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL, 33562306a36Sopenharmony_ci .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) | 33662306a36Sopenharmony_ci SOR_PLL_TMDS_TERMADJ(0), 33762306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) | 33862306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_10_mA_T114) | 33962306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_10_mA_T114) | 34062306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_10_mA_T114), 34162306a36Sopenharmony_ci .drive_current = 34262306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) | 34362306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) | 34462306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) | 34562306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114), 34662306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) | 34762306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) | 34862306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) | 34962306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA), 35062306a36Sopenharmony_ci }, { /* 225/297MHz modes */ 35162306a36Sopenharmony_ci .pclk = UINT_MAX, 35262306a36Sopenharmony_ci .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) | 35362306a36Sopenharmony_ci SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL, 35462306a36Sopenharmony_ci .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7) 35562306a36Sopenharmony_ci | SOR_PLL_TMDS_TERM_ENABLE, 35662306a36Sopenharmony_ci .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) | 35762306a36Sopenharmony_ci PE_CURRENT1(PE_CURRENT_0_mA_T114) | 35862306a36Sopenharmony_ci PE_CURRENT2(PE_CURRENT_0_mA_T114) | 35962306a36Sopenharmony_ci PE_CURRENT3(PE_CURRENT_0_mA_T114), 36062306a36Sopenharmony_ci .drive_current = 36162306a36Sopenharmony_ci DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) | 36262306a36Sopenharmony_ci DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) | 36362306a36Sopenharmony_ci DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) | 36462306a36Sopenharmony_ci DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114), 36562306a36Sopenharmony_ci .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) | 36662306a36Sopenharmony_ci PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) | 36762306a36Sopenharmony_ci PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) | 36862306a36Sopenharmony_ci PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA), 36962306a36Sopenharmony_ci }, 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void tegra_hdmi_audio_lock(struct tegra_hdmi *hdmi) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci mutex_lock(&hdmi->audio_lock); 37562306a36Sopenharmony_ci disable_irq(hdmi->irq); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void tegra_hdmi_audio_unlock(struct tegra_hdmi *hdmi) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci enable_irq(hdmi->irq); 38162306a36Sopenharmony_ci mutex_unlock(&hdmi->audio_lock); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int 38562306a36Sopenharmony_citegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pix_clock, 38662306a36Sopenharmony_ci struct tegra_hdmi_audio_config *config) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci const unsigned int afreq = 128 * audio_freq; 38962306a36Sopenharmony_ci const unsigned int min_n = afreq / 1500; 39062306a36Sopenharmony_ci const unsigned int max_n = afreq / 300; 39162306a36Sopenharmony_ci const unsigned int ideal_n = afreq / 1000; 39262306a36Sopenharmony_ci int64_t min_err = (uint64_t)-1 >> 1; 39362306a36Sopenharmony_ci unsigned int min_delta = -1; 39462306a36Sopenharmony_ci int n; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci memset(config, 0, sizeof(*config)); 39762306a36Sopenharmony_ci config->n = -1; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci for (n = min_n; n <= max_n; n++) { 40062306a36Sopenharmony_ci uint64_t cts_f, aval_f; 40162306a36Sopenharmony_ci unsigned int delta; 40262306a36Sopenharmony_ci int64_t cts, err; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* compute aval in 48.16 fixed point */ 40562306a36Sopenharmony_ci aval_f = ((int64_t)24000000 << 16) * n; 40662306a36Sopenharmony_ci do_div(aval_f, afreq); 40762306a36Sopenharmony_ci /* It should round without any rest */ 40862306a36Sopenharmony_ci if (aval_f & 0xFFFF) 40962306a36Sopenharmony_ci continue; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Compute cts in 48.16 fixed point */ 41262306a36Sopenharmony_ci cts_f = ((int64_t)pix_clock << 16) * n; 41362306a36Sopenharmony_ci do_div(cts_f, afreq); 41462306a36Sopenharmony_ci /* Round it to the nearest integer */ 41562306a36Sopenharmony_ci cts = (cts_f & ~0xFFFF) + ((cts_f & BIT(15)) << 1); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci delta = abs(n - ideal_n); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Compute the absolute error */ 42062306a36Sopenharmony_ci err = abs((int64_t)cts_f - cts); 42162306a36Sopenharmony_ci if (err < min_err || (err == min_err && delta < min_delta)) { 42262306a36Sopenharmony_ci config->n = n; 42362306a36Sopenharmony_ci config->cts = cts >> 16; 42462306a36Sopenharmony_ci config->aval = aval_f >> 16; 42562306a36Sopenharmony_ci min_delta = delta; 42662306a36Sopenharmony_ci min_err = err; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return config->n != -1 ? 0 : -EINVAL; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci const unsigned int freqs[] = { 43662306a36Sopenharmony_ci 32000, 44100, 48000, 88200, 96000, 176400, 192000 43762306a36Sopenharmony_ci }; 43862306a36Sopenharmony_ci unsigned int i; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(freqs); i++) { 44162306a36Sopenharmony_ci unsigned int f = freqs[i]; 44262306a36Sopenharmony_ci unsigned int eight_half; 44362306a36Sopenharmony_ci unsigned int delta; 44462306a36Sopenharmony_ci u32 value; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (f > 96000) 44762306a36Sopenharmony_ci delta = 2; 44862306a36Sopenharmony_ci else if (f > 48000) 44962306a36Sopenharmony_ci delta = 6; 45062306a36Sopenharmony_ci else 45162306a36Sopenharmony_ci delta = 9; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci eight_half = (8 * HDMI_AUDIOCLK_FREQ) / (f * 128); 45462306a36Sopenharmony_ci value = AUDIO_FS_LOW(eight_half - delta) | 45562306a36Sopenharmony_ci AUDIO_FS_HIGH(eight_half + delta); 45662306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_FS(i)); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci static const struct { 46362306a36Sopenharmony_ci unsigned int sample_rate; 46462306a36Sopenharmony_ci unsigned int offset; 46562306a36Sopenharmony_ci } regs[] = { 46662306a36Sopenharmony_ci { 32000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320 }, 46762306a36Sopenharmony_ci { 44100, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441 }, 46862306a36Sopenharmony_ci { 48000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480 }, 46962306a36Sopenharmony_ci { 88200, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882 }, 47062306a36Sopenharmony_ci { 96000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960 }, 47162306a36Sopenharmony_ci { 176400, HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764 }, 47262306a36Sopenharmony_ci { 192000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 }, 47362306a36Sopenharmony_ci }; 47462306a36Sopenharmony_ci unsigned int i; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(regs); i++) { 47762306a36Sopenharmony_ci if (regs[i].sample_rate == hdmi->format.sample_rate) { 47862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, regs[i].offset); 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct tegra_hdmi_audio_config config; 48762306a36Sopenharmony_ci u32 source, value; 48862306a36Sopenharmony_ci int err; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci switch (hdmi->audio_source) { 49162306a36Sopenharmony_ci case HDA: 49262306a36Sopenharmony_ci if (hdmi->config->has_hda) 49362306a36Sopenharmony_ci source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL; 49462306a36Sopenharmony_ci else 49562306a36Sopenharmony_ci return -EINVAL; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci break; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci case SPDIF: 50062306a36Sopenharmony_ci if (hdmi->config->has_hda) 50162306a36Sopenharmony_ci source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF; 50262306a36Sopenharmony_ci else 50362306a36Sopenharmony_ci source = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF; 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci default: 50762306a36Sopenharmony_ci if (hdmi->config->has_hda) 50862306a36Sopenharmony_ci source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO; 50962306a36Sopenharmony_ci else 51062306a36Sopenharmony_ci source = AUDIO_CNTRL0_SOURCE_SELECT_AUTO; 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* 51562306a36Sopenharmony_ci * Tegra30 and later use a slightly modified version of the register 51662306a36Sopenharmony_ci * layout to accomodate for changes related to supporting HDA as the 51762306a36Sopenharmony_ci * audio input source for HDMI. The source select field has moved to 51862306a36Sopenharmony_ci * the SOR_AUDIO_CNTRL0 register, but the error tolerance and frames 51962306a36Sopenharmony_ci * per block fields remain in the AUDIO_CNTRL0 register. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci if (hdmi->config->has_hda) { 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * Inject null samples into the audio FIFO for every frame in 52462306a36Sopenharmony_ci * which the codec did not receive any samples. This applies 52562306a36Sopenharmony_ci * to stereo LPCM only. 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * XXX: This seems to be a remnant of MCP days when this was 52862306a36Sopenharmony_ci * used to work around issues with monitors not being able to 52962306a36Sopenharmony_ci * play back system startup sounds early. It is possibly not 53062306a36Sopenharmony_ci * needed on Linux at all. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ci if (hdmi->format.channels == 2) 53362306a36Sopenharmony_ci value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL; 53462306a36Sopenharmony_ci else 53562306a36Sopenharmony_ci value = 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci value |= source; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * On Tegra20, HDA is not a supported audio source and the source 54462306a36Sopenharmony_ci * select field is part of the AUDIO_CNTRL0 register. 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci value = AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0) | 54762306a36Sopenharmony_ci AUDIO_CNTRL0_ERROR_TOLERANCE(6); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!hdmi->config->has_hda) 55062306a36Sopenharmony_ci value |= source; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_CNTRL0); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* 55562306a36Sopenharmony_ci * Advertise support for High Bit-Rate on Tegra114 and later. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci if (hdmi->config->has_hbr) { 55862306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); 55962306a36Sopenharmony_ci value |= SOR_AUDIO_SPARE0_HBR_ENABLE; 56062306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci err = tegra_hdmi_get_audio_config(hdmi->format.sample_rate, 56462306a36Sopenharmony_ci hdmi->pixel_clock, &config); 56562306a36Sopenharmony_ci if (err < 0) { 56662306a36Sopenharmony_ci dev_err(hdmi->dev, 56762306a36Sopenharmony_ci "cannot set audio to %u Hz at %u Hz pixel clock\n", 56862306a36Sopenharmony_ci hdmi->format.sample_rate, hdmi->pixel_clock); 56962306a36Sopenharmony_ci return err; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci dev_dbg(hdmi->dev, "audio: pixclk=%u, n=%u, cts=%u, aval=%u\n", 57362306a36Sopenharmony_ci hdmi->pixel_clock, config.n, config.cts, config.aval); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_HDMI_ACR_CTRL); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci value = AUDIO_N_RESETF | AUDIO_N_GENERATE_ALTERNATE | 57862306a36Sopenharmony_ci AUDIO_N_VALUE(config.n - 1); 57962306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config.n) | ACR_ENABLE, 58262306a36Sopenharmony_ci HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config.cts), 58562306a36Sopenharmony_ci HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1); 58862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_SPARE); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_AUDIO_N); 59162306a36Sopenharmony_ci value &= ~AUDIO_N_RESETF; 59262306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (hdmi->config->has_hda) 59562306a36Sopenharmony_ci tegra_hdmi_write_aval(hdmi, config.aval); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci tegra_hdmi_setup_audio_fs_tables(hdmi); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void tegra_hdmi_disable_audio(struct tegra_hdmi *hdmi) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci u32 value; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 60762306a36Sopenharmony_ci value &= ~GENERIC_CTRL_AUDIO; 60862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic void tegra_hdmi_enable_audio(struct tegra_hdmi *hdmi) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci u32 value; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 61662306a36Sopenharmony_ci value |= GENERIC_CTRL_AUDIO; 61762306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic void tegra_hdmi_write_eld(struct tegra_hdmi *hdmi) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci size_t length = drm_eld_size(hdmi->output.connector.eld), i; 62362306a36Sopenharmony_ci u32 value; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci for (i = 0; i < length; i++) 62662306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, i << 8 | hdmi->output.connector.eld[i], 62762306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* 63062306a36Sopenharmony_ci * The HDA codec will always report an ELD buffer size of 96 bytes and 63162306a36Sopenharmony_ci * the HDA codec driver will check that each byte read from the buffer 63262306a36Sopenharmony_ci * is valid. Therefore every byte must be written, even if no 96 bytes 63362306a36Sopenharmony_ci * were parsed from EDID. 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci for (i = length; i < HDMI_ELD_BUFFER_SIZE; i++) 63662306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, i << 8 | 0, 63762306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci value = SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT; 64062306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic inline u32 tegra_hdmi_subpack(const u8 *ptr, size_t size) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci u32 value = 0; 64662306a36Sopenharmony_ci size_t i; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci for (i = size; i > 0; i--) 64962306a36Sopenharmony_ci value = (value << 8) | ptr[i - 1]; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return value; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data, 65562306a36Sopenharmony_ci size_t size) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci const u8 *ptr = data; 65862306a36Sopenharmony_ci unsigned long offset; 65962306a36Sopenharmony_ci size_t i, j; 66062306a36Sopenharmony_ci u32 value; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci switch (ptr[0]) { 66362306a36Sopenharmony_ci case HDMI_INFOFRAME_TYPE_AVI: 66462306a36Sopenharmony_ci offset = HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER; 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci case HDMI_INFOFRAME_TYPE_AUDIO: 66862306a36Sopenharmony_ci offset = HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci case HDMI_INFOFRAME_TYPE_VENDOR: 67262306a36Sopenharmony_ci offset = HDMI_NV_PDISP_HDMI_GENERIC_HEADER; 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci default: 67662306a36Sopenharmony_ci dev_err(hdmi->dev, "unsupported infoframe type: %02x\n", 67762306a36Sopenharmony_ci ptr[0]); 67862306a36Sopenharmony_ci return; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci value = INFOFRAME_HEADER_TYPE(ptr[0]) | 68262306a36Sopenharmony_ci INFOFRAME_HEADER_VERSION(ptr[1]) | 68362306a36Sopenharmony_ci INFOFRAME_HEADER_LEN(ptr[2]); 68462306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, offset); 68562306a36Sopenharmony_ci offset++; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* 68862306a36Sopenharmony_ci * Each subpack contains 7 bytes, divided into: 68962306a36Sopenharmony_ci * - subpack_low: bytes 0 - 3 69062306a36Sopenharmony_ci * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00) 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci for (i = 3, j = 0; i < size; i += 7, j += 8) { 69362306a36Sopenharmony_ci size_t rem = size - i, num = min_t(size_t, rem, 4); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci value = tegra_hdmi_subpack(&ptr[i], num); 69662306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, offset++); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci num = min_t(size_t, rem - num, 3); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci value = tegra_hdmi_subpack(&ptr[i + 4], num); 70162306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, offset++); 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, 70662306a36Sopenharmony_ci struct drm_display_mode *mode) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct hdmi_avi_infoframe frame; 70962306a36Sopenharmony_ci u8 buffer[17]; 71062306a36Sopenharmony_ci ssize_t err; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci err = drm_hdmi_avi_infoframe_from_display_mode(&frame, 71362306a36Sopenharmony_ci &hdmi->output.connector, mode); 71462306a36Sopenharmony_ci if (err < 0) { 71562306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err); 71662306a36Sopenharmony_ci return; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); 72062306a36Sopenharmony_ci if (err < 0) { 72162306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to pack AVI infoframe: %zd\n", err); 72262306a36Sopenharmony_ci return; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci tegra_hdmi_write_infopack(hdmi, buffer, err); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void tegra_hdmi_disable_avi_infoframe(struct tegra_hdmi *hdmi) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci u32 value; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); 73362306a36Sopenharmony_ci value &= ~INFOFRAME_CTRL_ENABLE; 73462306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic void tegra_hdmi_enable_avi_infoframe(struct tegra_hdmi *hdmi) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci u32 value; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); 74262306a36Sopenharmony_ci value |= INFOFRAME_CTRL_ENABLE; 74362306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct hdmi_audio_infoframe frame; 74962306a36Sopenharmony_ci u8 buffer[14]; 75062306a36Sopenharmony_ci ssize_t err; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci err = hdmi_audio_infoframe_init(&frame); 75362306a36Sopenharmony_ci if (err < 0) { 75462306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to setup audio infoframe: %zd\n", 75562306a36Sopenharmony_ci err); 75662306a36Sopenharmony_ci return; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci frame.channels = hdmi->format.channels; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); 76262306a36Sopenharmony_ci if (err < 0) { 76362306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to pack audio infoframe: %zd\n", 76462306a36Sopenharmony_ci err); 76562306a36Sopenharmony_ci return; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* 76962306a36Sopenharmony_ci * The audio infoframe has only one set of subpack registers, so the 77062306a36Sopenharmony_ci * infoframe needs to be truncated. One set of subpack registers can 77162306a36Sopenharmony_ci * contain 7 bytes. Including the 3 byte header only the first 10 77262306a36Sopenharmony_ci * bytes can be programmed. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ci tegra_hdmi_write_infopack(hdmi, buffer, min_t(size_t, 10, err)); 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic void tegra_hdmi_disable_audio_infoframe(struct tegra_hdmi *hdmi) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci u32 value; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); 78262306a36Sopenharmony_ci value &= ~INFOFRAME_CTRL_ENABLE; 78362306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic void tegra_hdmi_enable_audio_infoframe(struct tegra_hdmi *hdmi) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci u32 value; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); 79162306a36Sopenharmony_ci value |= INFOFRAME_CTRL_ENABLE; 79262306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct hdmi_vendor_infoframe frame; 79862306a36Sopenharmony_ci u8 buffer[10]; 79962306a36Sopenharmony_ci ssize_t err; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci hdmi_vendor_infoframe_init(&frame); 80262306a36Sopenharmony_ci frame.s3d_struct = HDMI_3D_STRUCTURE_FRAME_PACKING; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); 80562306a36Sopenharmony_ci if (err < 0) { 80662306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to pack vendor infoframe: %zd\n", 80762306a36Sopenharmony_ci err); 80862306a36Sopenharmony_ci return; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci tegra_hdmi_write_infopack(hdmi, buffer, err); 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic void tegra_hdmi_disable_stereo_infoframe(struct tegra_hdmi *hdmi) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci u32 value; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 81962306a36Sopenharmony_ci value &= ~GENERIC_CTRL_ENABLE; 82062306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void tegra_hdmi_enable_stereo_infoframe(struct tegra_hdmi *hdmi) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci u32 value; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 82862306a36Sopenharmony_ci value |= GENERIC_CTRL_ENABLE; 82962306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic void tegra_hdmi_setup_tmds(struct tegra_hdmi *hdmi, 83362306a36Sopenharmony_ci const struct tmds_config *tmds) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci u32 value; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, tmds->pll0, HDMI_NV_PDISP_SOR_PLL0); 83862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, tmds->pll1, HDMI_NV_PDISP_SOR_PLL1); 83962306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, tmds->pe_current, HDMI_NV_PDISP_PE_CURRENT); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, tmds->drive_current, 84262306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, hdmi->config->fuse_override_offset); 84562306a36Sopenharmony_ci value |= hdmi->config->fuse_override_value; 84662306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, hdmi->config->fuse_override_offset); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (hdmi->config->has_sor_io_peak_current) 84962306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, tmds->peak_current, 85062306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic int tegra_hdmi_reconfigure_audio(struct tegra_hdmi *hdmi) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci int err; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci err = tegra_hdmi_setup_audio(hdmi); 85862306a36Sopenharmony_ci if (err < 0) { 85962306a36Sopenharmony_ci tegra_hdmi_disable_audio_infoframe(hdmi); 86062306a36Sopenharmony_ci tegra_hdmi_disable_audio(hdmi); 86162306a36Sopenharmony_ci } else { 86262306a36Sopenharmony_ci tegra_hdmi_setup_audio_infoframe(hdmi); 86362306a36Sopenharmony_ci tegra_hdmi_enable_audio_infoframe(hdmi); 86462306a36Sopenharmony_ci tegra_hdmi_enable_audio(hdmi); 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return err; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic bool tegra_output_is_hdmi(struct tegra_output *output) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci return output->connector.display_info.is_hdmi; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic enum drm_connector_status 87662306a36Sopenharmony_citegra_hdmi_connector_detect(struct drm_connector *connector, bool force) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct tegra_output *output = connector_to_output(connector); 87962306a36Sopenharmony_ci struct tegra_hdmi *hdmi = to_hdmi(output); 88062306a36Sopenharmony_ci enum drm_connector_status status; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci status = tegra_output_connector_detect(connector, force); 88362306a36Sopenharmony_ci if (status == connector_status_connected) 88462306a36Sopenharmony_ci return status; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); 88762306a36Sopenharmony_ci return status; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic const struct debugfs_reg32 tegra_hdmi_regs[] = { 89362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_CTXSW), 89462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE0), 89562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE1), 89662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_STATE2), 89762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_MSB), 89862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AN_LSB), 89962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_MSB), 90062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CN_LSB), 90162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_MSB), 90262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_AKSV_LSB), 90362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_MSB), 90462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_BKSV_LSB), 90562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_MSB), 90662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CKSV_LSB), 90762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_MSB), 90862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_DKSV_LSB), 90962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CTRL), 91062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CMODE), 91162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB), 91262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB), 91362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB), 91462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2), 91562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1), 91662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_RI), 91762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_MSB), 91862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_RG_HDCP_CS_LSB), 91962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU0), 92062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU_RDATA0), 92162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU1), 92262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_EMU2), 92362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL), 92462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS), 92562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER), 92662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW), 92762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH), 92862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL), 92962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_STATUS), 93062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER), 93162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW), 93262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH), 93362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW), 93462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH), 93562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_CTRL), 93662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_STATUS), 93762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_HEADER), 93862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW), 93962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH), 94062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW), 94162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH), 94262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW), 94362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH), 94462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW), 94562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH), 94662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_CTRL), 94762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW), 94862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH), 94962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW), 95062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH), 95162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW), 95262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH), 95362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW), 95462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH), 95562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW), 95662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH), 95762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW), 95862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH), 95962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW), 96062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH), 96162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CTRL), 96262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_KEEPOUT), 96362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_VSYNC_WINDOW), 96462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_CTRL), 96562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_STATUS), 96662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_GCP_SUBPACK), 96762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS1), 96862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_CHANNEL_STATUS2), 96962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU0), 97062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1), 97162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_EMU1_RDATA), 97262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPARE), 97362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS1), 97462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_SPDIF_CHN_STATUS2), 97562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDMI_HDCPRIF_ROM_CTRL), 97662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CAP), 97762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PWR), 97862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TEST), 97962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL0), 98062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL1), 98162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_PLL2), 98262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CSTM), 98362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LVDS), 98462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCA), 98562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CRCB), 98662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_BLANK), 98762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_CTL), 98862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(0)), 98962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(1)), 99062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(2)), 99162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(3)), 99262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(4)), 99362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(5)), 99462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(6)), 99562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(7)), 99662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(8)), 99762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(9)), 99862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(10)), 99962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(11)), 100062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(12)), 100162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(13)), 100262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(14)), 100362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_SEQ_INST(15)), 100462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA0), 100562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_VCRCA1), 100662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA0), 100762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_CCRCA1), 100862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA0), 100962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_EDATAA1), 101062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA0), 101162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_COUNTA1), 101262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA0), 101362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_DEBUGA1), 101462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_TRIG), 101562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_MSCHECK), 101662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT), 101762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG0), 101862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG1), 101962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_DEBUG2), 102062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(0)), 102162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(1)), 102262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(2)), 102362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(3)), 102462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(4)), 102562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(5)), 102662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_FS(6)), 102762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_PULSE_WIDTH), 102862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_THRESHOLD), 102962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_CNTRL0), 103062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_AUDIO_N), 103162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_HDCPRIF_ROM_TIMING), 103262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_REFCLK), 103362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_CRC_CONTROL), 103462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_INPUT_CONTROL), 103562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SCRATCH), 103662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_PE_CURRENT), 103762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_CTRL), 103862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG0), 103962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG1), 104062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_DEBUG2), 104162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_0), 104262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_1), 104362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_2), 104462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_3), 104562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG), 104662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_KEY_SKEY_INDEX), 104762306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0), 104862306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_SPARE0), 104962306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0), 105062306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1), 105162306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR), 105262306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE), 105362306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_INT_STATUS), 105462306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_INT_MASK), 105562306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_INT_ENABLE), 105662306a36Sopenharmony_ci DEBUGFS_REG32(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT), 105762306a36Sopenharmony_ci}; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int tegra_hdmi_show_regs(struct seq_file *s, void *data) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct drm_info_node *node = s->private; 106262306a36Sopenharmony_ci struct tegra_hdmi *hdmi = node->info_ent->data; 106362306a36Sopenharmony_ci struct drm_crtc *crtc = hdmi->output.encoder.crtc; 106462306a36Sopenharmony_ci struct drm_device *drm = node->minor->dev; 106562306a36Sopenharmony_ci unsigned int i; 106662306a36Sopenharmony_ci int err = 0; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci drm_modeset_lock_all(drm); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (!crtc || !crtc->state->active) { 107162306a36Sopenharmony_ci err = -EBUSY; 107262306a36Sopenharmony_ci goto unlock; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tegra_hdmi_regs); i++) { 107662306a36Sopenharmony_ci unsigned int offset = tegra_hdmi_regs[i].offset; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci seq_printf(s, "%-56s %#05x %08x\n", tegra_hdmi_regs[i].name, 107962306a36Sopenharmony_ci offset, tegra_hdmi_readl(hdmi, offset)); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ciunlock: 108362306a36Sopenharmony_ci drm_modeset_unlock_all(drm); 108462306a36Sopenharmony_ci return err; 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic struct drm_info_list debugfs_files[] = { 108862306a36Sopenharmony_ci { "regs", tegra_hdmi_show_regs, 0, NULL }, 108962306a36Sopenharmony_ci}; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic int tegra_hdmi_late_register(struct drm_connector *connector) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci struct tegra_output *output = connector_to_output(connector); 109462306a36Sopenharmony_ci unsigned int i, count = ARRAY_SIZE(debugfs_files); 109562306a36Sopenharmony_ci struct drm_minor *minor = connector->dev->primary; 109662306a36Sopenharmony_ci struct dentry *root = connector->debugfs_entry; 109762306a36Sopenharmony_ci struct tegra_hdmi *hdmi = to_hdmi(output); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci hdmi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), 110062306a36Sopenharmony_ci GFP_KERNEL); 110162306a36Sopenharmony_ci if (!hdmi->debugfs_files) 110262306a36Sopenharmony_ci return -ENOMEM; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci for (i = 0; i < count; i++) 110562306a36Sopenharmony_ci hdmi->debugfs_files[i].data = hdmi; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci drm_debugfs_create_files(hdmi->debugfs_files, count, root, minor); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic void tegra_hdmi_early_unregister(struct drm_connector *connector) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci struct tegra_output *output = connector_to_output(connector); 111562306a36Sopenharmony_ci struct drm_minor *minor = connector->dev->primary; 111662306a36Sopenharmony_ci unsigned int count = ARRAY_SIZE(debugfs_files); 111762306a36Sopenharmony_ci struct tegra_hdmi *hdmi = to_hdmi(output); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci drm_debugfs_remove_files(hdmi->debugfs_files, count, minor); 112062306a36Sopenharmony_ci kfree(hdmi->debugfs_files); 112162306a36Sopenharmony_ci hdmi->debugfs_files = NULL; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic const struct drm_connector_funcs tegra_hdmi_connector_funcs = { 112562306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 112662306a36Sopenharmony_ci .detect = tegra_hdmi_connector_detect, 112762306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 112862306a36Sopenharmony_ci .destroy = tegra_output_connector_destroy, 112962306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 113062306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 113162306a36Sopenharmony_ci .late_register = tegra_hdmi_late_register, 113262306a36Sopenharmony_ci .early_unregister = tegra_hdmi_early_unregister, 113362306a36Sopenharmony_ci}; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic enum drm_mode_status 113662306a36Sopenharmony_citegra_hdmi_connector_mode_valid(struct drm_connector *connector, 113762306a36Sopenharmony_ci struct drm_display_mode *mode) 113862306a36Sopenharmony_ci{ 113962306a36Sopenharmony_ci struct tegra_output *output = connector_to_output(connector); 114062306a36Sopenharmony_ci struct tegra_hdmi *hdmi = to_hdmi(output); 114162306a36Sopenharmony_ci unsigned long pclk = mode->clock * 1000; 114262306a36Sopenharmony_ci enum drm_mode_status status = MODE_OK; 114362306a36Sopenharmony_ci struct clk *parent; 114462306a36Sopenharmony_ci long err; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci parent = clk_get_parent(hdmi->clk_parent); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci err = clk_round_rate(parent, pclk * 4); 114962306a36Sopenharmony_ci if (err <= 0) 115062306a36Sopenharmony_ci status = MODE_NOCLOCK; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci return status; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs 115662306a36Sopenharmony_citegra_hdmi_connector_helper_funcs = { 115762306a36Sopenharmony_ci .get_modes = tegra_output_connector_get_modes, 115862306a36Sopenharmony_ci .mode_valid = tegra_hdmi_connector_mode_valid, 115962306a36Sopenharmony_ci}; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct tegra_output *output = encoder_to_output(encoder); 116462306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(encoder->crtc); 116562306a36Sopenharmony_ci struct tegra_hdmi *hdmi = to_hdmi(output); 116662306a36Sopenharmony_ci u32 value; 116762306a36Sopenharmony_ci int err; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci tegra_hdmi_audio_lock(hdmi); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* 117262306a36Sopenharmony_ci * The following accesses registers of the display controller, so make 117362306a36Sopenharmony_ci * sure it's only executed when the output is attached to one. 117462306a36Sopenharmony_ci */ 117562306a36Sopenharmony_ci if (dc) { 117662306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 117762306a36Sopenharmony_ci value &= ~HDMI_ENABLE; 117862306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci tegra_dc_commit(dc); 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (!hdmi->dvi) { 118462306a36Sopenharmony_ci if (hdmi->stereo) 118562306a36Sopenharmony_ci tegra_hdmi_disable_stereo_infoframe(hdmi); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci tegra_hdmi_disable_audio_infoframe(hdmi); 118862306a36Sopenharmony_ci tegra_hdmi_disable_avi_infoframe(hdmi); 118962306a36Sopenharmony_ci tegra_hdmi_disable_audio(hdmi); 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_ENABLE); 119362306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_MASK); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci hdmi->pixel_clock = 0; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci tegra_hdmi_audio_unlock(hdmi); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci err = host1x_client_suspend(&hdmi->client); 120062306a36Sopenharmony_ci if (err < 0) 120162306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to suspend: %d\n", err); 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 120762306a36Sopenharmony_ci unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey; 120862306a36Sopenharmony_ci struct tegra_output *output = encoder_to_output(encoder); 120962306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(encoder->crtc); 121062306a36Sopenharmony_ci struct tegra_hdmi *hdmi = to_hdmi(output); 121162306a36Sopenharmony_ci unsigned int pulse_start, div82; 121262306a36Sopenharmony_ci int retries = 1000; 121362306a36Sopenharmony_ci u32 value; 121462306a36Sopenharmony_ci int err; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci err = host1x_client_resume(&hdmi->client); 121762306a36Sopenharmony_ci if (err < 0) { 121862306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to resume: %d\n", err); 121962306a36Sopenharmony_ci return; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci tegra_hdmi_audio_lock(hdmi); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* 122562306a36Sopenharmony_ci * Enable and unmask the HDA codec SCRATCH0 register interrupt. This 122662306a36Sopenharmony_ci * is used for interoperability between the HDA codec driver and the 122762306a36Sopenharmony_ci * HDMI driver. 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_ENABLE); 123062306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_MASK); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci hdmi->pixel_clock = mode->clock * 1000; 123362306a36Sopenharmony_ci h_sync_width = mode->hsync_end - mode->hsync_start; 123462306a36Sopenharmony_ci h_back_porch = mode->htotal - mode->hsync_end; 123562306a36Sopenharmony_ci h_front_porch = mode->hsync_start - mode->hdisplay; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci err = dev_pm_opp_set_rate(hdmi->dev, hdmi->pixel_clock); 123862306a36Sopenharmony_ci if (err < 0) { 123962306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to set HDMI clock frequency: %d\n", 124062306a36Sopenharmony_ci err); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci DRM_DEBUG_KMS("HDMI clock rate: %lu Hz\n", clk_get_rate(hdmi->clk)); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci /* power up sequence */ 124662306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0); 124762306a36Sopenharmony_ci value &= ~SOR_PLL_PDBG; 124862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci usleep_range(10, 20); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0); 125362306a36Sopenharmony_ci value &= ~SOR_PLL_PWR; 125462306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci tegra_dc_writel(dc, VSYNC_H_POSITION(1), 125762306a36Sopenharmony_ci DC_DISP_DISP_TIMING_OPTIONS); 125862306a36Sopenharmony_ci tegra_dc_writel(dc, DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE_888, 125962306a36Sopenharmony_ci DC_DISP_DISP_COLOR_CONTROL); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* video_preamble uses h_pulse2 */ 126262306a36Sopenharmony_ci pulse_start = 1 + h_sync_width + h_back_porch - 10; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci tegra_dc_writel(dc, H_PULSE2_ENABLE, DC_DISP_DISP_SIGNAL_OPTIONS0); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci value = PULSE_MODE_NORMAL | PULSE_POLARITY_HIGH | PULSE_QUAL_VACTIVE | 126762306a36Sopenharmony_ci PULSE_LAST_END_A; 126862306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci value = PULSE_START(pulse_start) | PULSE_END(pulse_start + 8); 127162306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci value = VSYNC_WINDOW_END(0x210) | VSYNC_WINDOW_START(0x200) | 127462306a36Sopenharmony_ci VSYNC_WINDOW_ENABLE; 127562306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (dc->pipe) 127862306a36Sopenharmony_ci value = HDMI_SRC_DISPLAYB; 127962306a36Sopenharmony_ci else 128062306a36Sopenharmony_ci value = HDMI_SRC_DISPLAYA; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if ((mode->hdisplay == 720) && ((mode->vdisplay == 480) || 128362306a36Sopenharmony_ci (mode->vdisplay == 576))) 128462306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 128562306a36Sopenharmony_ci value | ARM_VIDEO_RANGE_FULL, 128662306a36Sopenharmony_ci HDMI_NV_PDISP_INPUT_CONTROL); 128762306a36Sopenharmony_ci else 128862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 128962306a36Sopenharmony_ci value | ARM_VIDEO_RANGE_LIMITED, 129062306a36Sopenharmony_ci HDMI_NV_PDISP_INPUT_CONTROL); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci div82 = clk_get_rate(hdmi->clk) / 1000000 * 4; 129362306a36Sopenharmony_ci value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82); 129462306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_REFCLK); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci hdmi->dvi = !tegra_output_is_hdmi(output); 129762306a36Sopenharmony_ci if (!hdmi->dvi) { 129862306a36Sopenharmony_ci /* 129962306a36Sopenharmony_ci * Make sure that the audio format has been configured before 130062306a36Sopenharmony_ci * enabling audio, otherwise we may try to divide by zero. 130162306a36Sopenharmony_ci */ 130262306a36Sopenharmony_ci if (hdmi->format.sample_rate > 0) { 130362306a36Sopenharmony_ci err = tegra_hdmi_setup_audio(hdmi); 130462306a36Sopenharmony_ci if (err < 0) 130562306a36Sopenharmony_ci hdmi->dvi = true; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (hdmi->config->has_hda) 131062306a36Sopenharmony_ci tegra_hdmi_write_eld(hdmi); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci rekey = HDMI_REKEY_DEFAULT; 131362306a36Sopenharmony_ci value = HDMI_CTRL_REKEY(rekey); 131462306a36Sopenharmony_ci value |= HDMI_CTRL_MAX_AC_PACKET((h_sync_width + h_back_porch + 131562306a36Sopenharmony_ci h_front_porch - rekey - 18) / 32); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (!hdmi->dvi) 131862306a36Sopenharmony_ci value |= HDMI_CTRL_ENABLE; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_CTRL); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (!hdmi->dvi) { 132362306a36Sopenharmony_ci tegra_hdmi_setup_avi_infoframe(hdmi, mode); 132462306a36Sopenharmony_ci tegra_hdmi_setup_audio_infoframe(hdmi); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (hdmi->stereo) 132762306a36Sopenharmony_ci tegra_hdmi_setup_stereo_infoframe(hdmi); 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* TMDS CONFIG */ 133162306a36Sopenharmony_ci for (i = 0; i < hdmi->config->num_tmds; i++) { 133262306a36Sopenharmony_ci if (hdmi->pixel_clock <= hdmi->config->tmds[i].pclk) { 133362306a36Sopenharmony_ci tegra_hdmi_setup_tmds(hdmi, &hdmi->config->tmds[i]); 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 133962306a36Sopenharmony_ci SOR_SEQ_PU_PC(0) | 134062306a36Sopenharmony_ci SOR_SEQ_PU_PC_ALT(0) | 134162306a36Sopenharmony_ci SOR_SEQ_PD_PC(8) | 134262306a36Sopenharmony_ci SOR_SEQ_PD_PC_ALT(8), 134362306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_SEQ_CTL); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci value = SOR_SEQ_INST_WAIT_TIME(1) | 134662306a36Sopenharmony_ci SOR_SEQ_INST_WAIT_UNITS_VSYNC | 134762306a36Sopenharmony_ci SOR_SEQ_INST_HALT | 134862306a36Sopenharmony_ci SOR_SEQ_INST_PIN_A_LOW | 134962306a36Sopenharmony_ci SOR_SEQ_INST_PIN_B_LOW | 135062306a36Sopenharmony_ci SOR_SEQ_INST_DRIVE_PWM_OUT_LO; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(0)); 135362306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(8)); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_CSTM); 135662306a36Sopenharmony_ci value &= ~SOR_CSTM_ROTCLK(~0); 135762306a36Sopenharmony_ci value |= SOR_CSTM_ROTCLK(2); 135862306a36Sopenharmony_ci value |= SOR_CSTM_PLLDIV; 135962306a36Sopenharmony_ci value &= ~SOR_CSTM_LVDS_ENABLE; 136062306a36Sopenharmony_ci value &= ~SOR_CSTM_MODE_MASK; 136162306a36Sopenharmony_ci value |= SOR_CSTM_MODE_TMDS; 136262306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_CSTM); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* start SOR */ 136562306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 136662306a36Sopenharmony_ci SOR_PWR_NORMAL_STATE_PU | 136762306a36Sopenharmony_ci SOR_PWR_NORMAL_START_NORMAL | 136862306a36Sopenharmony_ci SOR_PWR_SAFE_STATE_PD | 136962306a36Sopenharmony_ci SOR_PWR_SETTING_NEW_TRIGGER, 137062306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_PWR); 137162306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 137262306a36Sopenharmony_ci SOR_PWR_NORMAL_STATE_PU | 137362306a36Sopenharmony_ci SOR_PWR_NORMAL_START_NORMAL | 137462306a36Sopenharmony_ci SOR_PWR_SAFE_STATE_PD | 137562306a36Sopenharmony_ci SOR_PWR_SETTING_NEW_DONE, 137662306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_PWR); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci do { 137962306a36Sopenharmony_ci BUG_ON(--retries < 0); 138062306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PWR); 138162306a36Sopenharmony_ci } while (value & SOR_PWR_SETTING_NEW_PENDING); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci value = SOR_STATE_ASY_CRCMODE_COMPLETE | 138462306a36Sopenharmony_ci SOR_STATE_ASY_OWNER_HEAD0 | 138562306a36Sopenharmony_ci SOR_STATE_ASY_SUBOWNER_BOTH | 138662306a36Sopenharmony_ci SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A | 138762306a36Sopenharmony_ci SOR_STATE_ASY_DEPOL_POS; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* setup sync polarities */ 139062306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PHSYNC) 139162306a36Sopenharmony_ci value |= SOR_STATE_ASY_HSYNCPOL_POS; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NHSYNC) 139462306a36Sopenharmony_ci value |= SOR_STATE_ASY_HSYNCPOL_NEG; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PVSYNC) 139762306a36Sopenharmony_ci value |= SOR_STATE_ASY_VSYNCPOL_POS; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NVSYNC) 140062306a36Sopenharmony_ci value |= SOR_STATE_ASY_VSYNCPOL_NEG; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_STATE2); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci value = SOR_STATE_ASY_HEAD_OPMODE_AWAKE | SOR_STATE_ASY_ORMODE_NORMAL; 140562306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_STATE1); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_STATE0); 140862306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, SOR_STATE_UPDATE, HDMI_NV_PDISP_SOR_STATE0); 140962306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value | SOR_STATE_ATTACHED, 141062306a36Sopenharmony_ci HDMI_NV_PDISP_SOR_STATE1); 141162306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_STATE0); 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); 141462306a36Sopenharmony_ci value |= HDMI_ENABLE; 141562306a36Sopenharmony_ci tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci tegra_dc_commit(dc); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (!hdmi->dvi) { 142062306a36Sopenharmony_ci tegra_hdmi_enable_avi_infoframe(hdmi); 142162306a36Sopenharmony_ci tegra_hdmi_enable_audio_infoframe(hdmi); 142262306a36Sopenharmony_ci tegra_hdmi_enable_audio(hdmi); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci if (hdmi->stereo) 142562306a36Sopenharmony_ci tegra_hdmi_enable_stereo_infoframe(hdmi); 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci /* TODO: add HDCP support */ 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci tegra_hdmi_audio_unlock(hdmi); 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cistatic int 143462306a36Sopenharmony_citegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder, 143562306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 143662306a36Sopenharmony_ci struct drm_connector_state *conn_state) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci struct tegra_output *output = encoder_to_output(encoder); 143962306a36Sopenharmony_ci struct tegra_dc *dc = to_tegra_dc(conn_state->crtc); 144062306a36Sopenharmony_ci unsigned long pclk = crtc_state->mode.clock * 1000; 144162306a36Sopenharmony_ci struct tegra_hdmi *hdmi = to_hdmi(output); 144262306a36Sopenharmony_ci int err; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci err = tegra_dc_state_setup_clock(dc, crtc_state, hdmi->clk_parent, 144562306a36Sopenharmony_ci pclk, 0); 144662306a36Sopenharmony_ci if (err < 0) { 144762306a36Sopenharmony_ci dev_err(output->dev, "failed to setup CRTC state: %d\n", err); 144862306a36Sopenharmony_ci return err; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return err; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { 145562306a36Sopenharmony_ci .disable = tegra_hdmi_encoder_disable, 145662306a36Sopenharmony_ci .enable = tegra_hdmi_encoder_enable, 145762306a36Sopenharmony_ci .atomic_check = tegra_hdmi_encoder_atomic_check, 145862306a36Sopenharmony_ci}; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cistatic int tegra_hdmi_hw_params(struct device *dev, void *data, 146162306a36Sopenharmony_ci struct hdmi_codec_daifmt *fmt, 146262306a36Sopenharmony_ci struct hdmi_codec_params *hparms) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci struct tegra_hdmi *hdmi = data; 146562306a36Sopenharmony_ci int ret = 0; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci tegra_hdmi_audio_lock(hdmi); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci hdmi->format.sample_rate = hparms->sample_rate; 147062306a36Sopenharmony_ci hdmi->format.channels = hparms->channels; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (hdmi->pixel_clock && !hdmi->dvi) 147362306a36Sopenharmony_ci ret = tegra_hdmi_reconfigure_audio(hdmi); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci tegra_hdmi_audio_unlock(hdmi); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci return ret; 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_cistatic int tegra_hdmi_audio_startup(struct device *dev, void *data) 148162306a36Sopenharmony_ci{ 148262306a36Sopenharmony_ci struct tegra_hdmi *hdmi = data; 148362306a36Sopenharmony_ci int ret; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci ret = host1x_client_resume(&hdmi->client); 148662306a36Sopenharmony_ci if (ret < 0) 148762306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to resume: %d\n", ret); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci return ret; 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_cistatic void tegra_hdmi_audio_shutdown(struct device *dev, void *data) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct tegra_hdmi *hdmi = data; 149562306a36Sopenharmony_ci int ret; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci tegra_hdmi_audio_lock(hdmi); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci hdmi->format.sample_rate = 0; 150062306a36Sopenharmony_ci hdmi->format.channels = 0; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci tegra_hdmi_audio_unlock(hdmi); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci ret = host1x_client_suspend(&hdmi->client); 150562306a36Sopenharmony_ci if (ret < 0) 150662306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to suspend: %d\n", ret); 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cistatic const struct hdmi_codec_ops tegra_hdmi_codec_ops = { 151062306a36Sopenharmony_ci .hw_params = tegra_hdmi_hw_params, 151162306a36Sopenharmony_ci .audio_startup = tegra_hdmi_audio_startup, 151262306a36Sopenharmony_ci .audio_shutdown = tegra_hdmi_audio_shutdown, 151362306a36Sopenharmony_ci}; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic int tegra_hdmi_codec_register(struct tegra_hdmi *hdmi) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct hdmi_codec_pdata codec_data = {}; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (hdmi->config->has_hda) 152062306a36Sopenharmony_ci return 0; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci codec_data.ops = &tegra_hdmi_codec_ops; 152362306a36Sopenharmony_ci codec_data.data = hdmi; 152462306a36Sopenharmony_ci codec_data.spdif = 1; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci hdmi->audio_pdev = platform_device_register_data(hdmi->dev, 152762306a36Sopenharmony_ci HDMI_CODEC_DRV_NAME, 152862306a36Sopenharmony_ci PLATFORM_DEVID_AUTO, 152962306a36Sopenharmony_ci &codec_data, 153062306a36Sopenharmony_ci sizeof(codec_data)); 153162306a36Sopenharmony_ci if (IS_ERR(hdmi->audio_pdev)) 153262306a36Sopenharmony_ci return PTR_ERR(hdmi->audio_pdev); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci hdmi->format.channels = 2; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci return 0; 153762306a36Sopenharmony_ci} 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cistatic void tegra_hdmi_codec_unregister(struct tegra_hdmi *hdmi) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci if (hdmi->audio_pdev) 154262306a36Sopenharmony_ci platform_device_unregister(hdmi->audio_pdev); 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic int tegra_hdmi_init(struct host1x_client *client) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); 154862306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(client->host); 154962306a36Sopenharmony_ci struct drm_connector *connector; 155062306a36Sopenharmony_ci int err; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci hdmi->output.dev = client->dev; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci drm_simple_encoder_init(drm, &hdmi->output.encoder, 155562306a36Sopenharmony_ci DRM_MODE_ENCODER_TMDS); 155662306a36Sopenharmony_ci drm_encoder_helper_add(&hdmi->output.encoder, 155762306a36Sopenharmony_ci &tegra_hdmi_encoder_helper_funcs); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (hdmi->output.bridge) { 156062306a36Sopenharmony_ci err = drm_bridge_attach(&hdmi->output.encoder, hdmi->output.bridge, 156162306a36Sopenharmony_ci NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); 156262306a36Sopenharmony_ci if (err) { 156362306a36Sopenharmony_ci dev_err(client->dev, "failed to attach bridge: %d\n", 156462306a36Sopenharmony_ci err); 156562306a36Sopenharmony_ci return err; 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci connector = drm_bridge_connector_init(drm, &hdmi->output.encoder); 156962306a36Sopenharmony_ci if (IS_ERR(connector)) { 157062306a36Sopenharmony_ci dev_err(client->dev, 157162306a36Sopenharmony_ci "failed to initialize bridge connector: %pe\n", 157262306a36Sopenharmony_ci connector); 157362306a36Sopenharmony_ci return PTR_ERR(connector); 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci drm_connector_attach_encoder(connector, &hdmi->output.encoder); 157762306a36Sopenharmony_ci } else { 157862306a36Sopenharmony_ci drm_connector_init_with_ddc(drm, &hdmi->output.connector, 157962306a36Sopenharmony_ci &tegra_hdmi_connector_funcs, 158062306a36Sopenharmony_ci DRM_MODE_CONNECTOR_HDMIA, 158162306a36Sopenharmony_ci hdmi->output.ddc); 158262306a36Sopenharmony_ci drm_connector_helper_add(&hdmi->output.connector, 158362306a36Sopenharmony_ci &tegra_hdmi_connector_helper_funcs); 158462306a36Sopenharmony_ci hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci drm_connector_attach_encoder(&hdmi->output.connector, 158762306a36Sopenharmony_ci &hdmi->output.encoder); 158862306a36Sopenharmony_ci drm_connector_register(&hdmi->output.connector); 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci err = tegra_output_init(drm, &hdmi->output); 159262306a36Sopenharmony_ci if (err < 0) { 159362306a36Sopenharmony_ci dev_err(client->dev, "failed to initialize output: %d\n", err); 159462306a36Sopenharmony_ci return err; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci hdmi->output.encoder.possible_crtcs = 0x3; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci err = regulator_enable(hdmi->hdmi); 160062306a36Sopenharmony_ci if (err < 0) { 160162306a36Sopenharmony_ci dev_err(client->dev, "failed to enable HDMI regulator: %d\n", 160262306a36Sopenharmony_ci err); 160362306a36Sopenharmony_ci goto output_exit; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci err = regulator_enable(hdmi->pll); 160762306a36Sopenharmony_ci if (err < 0) { 160862306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err); 160962306a36Sopenharmony_ci goto disable_hdmi; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci err = regulator_enable(hdmi->vdd); 161362306a36Sopenharmony_ci if (err < 0) { 161462306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err); 161562306a36Sopenharmony_ci goto disable_pll; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci err = tegra_hdmi_codec_register(hdmi); 161962306a36Sopenharmony_ci if (err < 0) { 162062306a36Sopenharmony_ci dev_err(hdmi->dev, "failed to register audio codec: %d\n", err); 162162306a36Sopenharmony_ci goto disable_vdd; 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci return 0; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cidisable_vdd: 162762306a36Sopenharmony_ci regulator_disable(hdmi->vdd); 162862306a36Sopenharmony_cidisable_pll: 162962306a36Sopenharmony_ci regulator_disable(hdmi->pll); 163062306a36Sopenharmony_cidisable_hdmi: 163162306a36Sopenharmony_ci regulator_disable(hdmi->hdmi); 163262306a36Sopenharmony_cioutput_exit: 163362306a36Sopenharmony_ci tegra_output_exit(&hdmi->output); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci return err; 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cistatic int tegra_hdmi_exit(struct host1x_client *client) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci tegra_hdmi_codec_unregister(hdmi); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci tegra_output_exit(&hdmi->output); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci regulator_disable(hdmi->vdd); 164762306a36Sopenharmony_ci regulator_disable(hdmi->pll); 164862306a36Sopenharmony_ci regulator_disable(hdmi->hdmi); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci return 0; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cistatic int tegra_hdmi_runtime_suspend(struct host1x_client *client) 165462306a36Sopenharmony_ci{ 165562306a36Sopenharmony_ci struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); 165662306a36Sopenharmony_ci struct device *dev = client->dev; 165762306a36Sopenharmony_ci int err; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci err = reset_control_assert(hdmi->rst); 166062306a36Sopenharmony_ci if (err < 0) { 166162306a36Sopenharmony_ci dev_err(dev, "failed to assert reset: %d\n", err); 166262306a36Sopenharmony_ci return err; 166362306a36Sopenharmony_ci } 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci usleep_range(1000, 2000); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci clk_disable_unprepare(hdmi->clk); 166862306a36Sopenharmony_ci pm_runtime_put_sync(dev); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return 0; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic int tegra_hdmi_runtime_resume(struct host1x_client *client) 167462306a36Sopenharmony_ci{ 167562306a36Sopenharmony_ci struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); 167662306a36Sopenharmony_ci struct device *dev = client->dev; 167762306a36Sopenharmony_ci int err; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci err = pm_runtime_resume_and_get(dev); 168062306a36Sopenharmony_ci if (err < 0) { 168162306a36Sopenharmony_ci dev_err(dev, "failed to get runtime PM: %d\n", err); 168262306a36Sopenharmony_ci return err; 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci err = clk_prepare_enable(hdmi->clk); 168662306a36Sopenharmony_ci if (err < 0) { 168762306a36Sopenharmony_ci dev_err(dev, "failed to enable clock: %d\n", err); 168862306a36Sopenharmony_ci goto put_rpm; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci usleep_range(1000, 2000); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci err = reset_control_deassert(hdmi->rst); 169462306a36Sopenharmony_ci if (err < 0) { 169562306a36Sopenharmony_ci dev_err(dev, "failed to deassert reset: %d\n", err); 169662306a36Sopenharmony_ci goto disable_clk; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci return 0; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_cidisable_clk: 170262306a36Sopenharmony_ci clk_disable_unprepare(hdmi->clk); 170362306a36Sopenharmony_ciput_rpm: 170462306a36Sopenharmony_ci pm_runtime_put_sync(dev); 170562306a36Sopenharmony_ci return err; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic const struct host1x_client_ops hdmi_client_ops = { 170962306a36Sopenharmony_ci .init = tegra_hdmi_init, 171062306a36Sopenharmony_ci .exit = tegra_hdmi_exit, 171162306a36Sopenharmony_ci .suspend = tegra_hdmi_runtime_suspend, 171262306a36Sopenharmony_ci .resume = tegra_hdmi_runtime_resume, 171362306a36Sopenharmony_ci}; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic const struct tegra_hdmi_config tegra20_hdmi_config = { 171662306a36Sopenharmony_ci .tmds = tegra20_tmds_config, 171762306a36Sopenharmony_ci .num_tmds = ARRAY_SIZE(tegra20_tmds_config), 171862306a36Sopenharmony_ci .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, 171962306a36Sopenharmony_ci .fuse_override_value = 1 << 31, 172062306a36Sopenharmony_ci .has_sor_io_peak_current = false, 172162306a36Sopenharmony_ci .has_hda = false, 172262306a36Sopenharmony_ci .has_hbr = false, 172362306a36Sopenharmony_ci}; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic const struct tegra_hdmi_config tegra30_hdmi_config = { 172662306a36Sopenharmony_ci .tmds = tegra30_tmds_config, 172762306a36Sopenharmony_ci .num_tmds = ARRAY_SIZE(tegra30_tmds_config), 172862306a36Sopenharmony_ci .fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, 172962306a36Sopenharmony_ci .fuse_override_value = 1 << 31, 173062306a36Sopenharmony_ci .has_sor_io_peak_current = false, 173162306a36Sopenharmony_ci .has_hda = true, 173262306a36Sopenharmony_ci .has_hbr = false, 173362306a36Sopenharmony_ci}; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic const struct tegra_hdmi_config tegra114_hdmi_config = { 173662306a36Sopenharmony_ci .tmds = tegra114_tmds_config, 173762306a36Sopenharmony_ci .num_tmds = ARRAY_SIZE(tegra114_tmds_config), 173862306a36Sopenharmony_ci .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0, 173962306a36Sopenharmony_ci .fuse_override_value = 1 << 31, 174062306a36Sopenharmony_ci .has_sor_io_peak_current = true, 174162306a36Sopenharmony_ci .has_hda = true, 174262306a36Sopenharmony_ci .has_hbr = true, 174362306a36Sopenharmony_ci}; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_cistatic const struct tegra_hdmi_config tegra124_hdmi_config = { 174662306a36Sopenharmony_ci .tmds = tegra124_tmds_config, 174762306a36Sopenharmony_ci .num_tmds = ARRAY_SIZE(tegra124_tmds_config), 174862306a36Sopenharmony_ci .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0, 174962306a36Sopenharmony_ci .fuse_override_value = 1 << 31, 175062306a36Sopenharmony_ci .has_sor_io_peak_current = true, 175162306a36Sopenharmony_ci .has_hda = true, 175262306a36Sopenharmony_ci .has_hbr = true, 175362306a36Sopenharmony_ci}; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_cistatic const struct of_device_id tegra_hdmi_of_match[] = { 175662306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-hdmi", .data = &tegra124_hdmi_config }, 175762306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-hdmi", .data = &tegra114_hdmi_config }, 175862306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-hdmi", .data = &tegra30_hdmi_config }, 175962306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config }, 176062306a36Sopenharmony_ci { }, 176162306a36Sopenharmony_ci}; 176262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_hdmi_of_match); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic irqreturn_t tegra_hdmi_irq(int irq, void *data) 176562306a36Sopenharmony_ci{ 176662306a36Sopenharmony_ci struct tegra_hdmi *hdmi = data; 176762306a36Sopenharmony_ci u32 value; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_INT_STATUS); 177062306a36Sopenharmony_ci tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_INT_STATUS); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (value & INT_CODEC_SCRATCH0) { 177362306a36Sopenharmony_ci unsigned int format; 177462306a36Sopenharmony_ci u32 value; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) { 177962306a36Sopenharmony_ci format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci tegra_hda_parse_format(format, &hdmi->format); 178262306a36Sopenharmony_ci tegra_hdmi_reconfigure_audio(hdmi); 178362306a36Sopenharmony_ci } else { 178462306a36Sopenharmony_ci tegra_hdmi_disable_audio_infoframe(hdmi); 178562306a36Sopenharmony_ci tegra_hdmi_disable_audio(hdmi); 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci return IRQ_HANDLED; 179062306a36Sopenharmony_ci} 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_cistatic int tegra_hdmi_probe(struct platform_device *pdev) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci struct tegra_hdmi *hdmi; 179562306a36Sopenharmony_ci int err; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); 179862306a36Sopenharmony_ci if (!hdmi) 179962306a36Sopenharmony_ci return -ENOMEM; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci hdmi->config = of_device_get_match_data(&pdev->dev); 180262306a36Sopenharmony_ci hdmi->dev = &pdev->dev; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci hdmi->audio_source = AUTO; 180562306a36Sopenharmony_ci hdmi->stereo = false; 180662306a36Sopenharmony_ci hdmi->dvi = false; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci mutex_init(&hdmi->audio_lock); 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci hdmi->clk = devm_clk_get(&pdev->dev, NULL); 181162306a36Sopenharmony_ci if (IS_ERR(hdmi->clk)) { 181262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock\n"); 181362306a36Sopenharmony_ci return PTR_ERR(hdmi->clk); 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci hdmi->rst = devm_reset_control_get(&pdev->dev, "hdmi"); 181762306a36Sopenharmony_ci if (IS_ERR(hdmi->rst)) { 181862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get reset\n"); 181962306a36Sopenharmony_ci return PTR_ERR(hdmi->rst); 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci hdmi->clk_parent = devm_clk_get(&pdev->dev, "parent"); 182362306a36Sopenharmony_ci if (IS_ERR(hdmi->clk_parent)) 182462306a36Sopenharmony_ci return PTR_ERR(hdmi->clk_parent); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci err = clk_set_parent(hdmi->clk, hdmi->clk_parent); 182762306a36Sopenharmony_ci if (err < 0) { 182862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to setup clocks: %d\n", err); 182962306a36Sopenharmony_ci return err; 183062306a36Sopenharmony_ci } 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci hdmi->hdmi = devm_regulator_get(&pdev->dev, "hdmi"); 183362306a36Sopenharmony_ci err = PTR_ERR_OR_ZERO(hdmi->hdmi); 183462306a36Sopenharmony_ci if (err) 183562306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, 183662306a36Sopenharmony_ci "failed to get HDMI regulator\n"); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci hdmi->pll = devm_regulator_get(&pdev->dev, "pll"); 183962306a36Sopenharmony_ci err = PTR_ERR_OR_ZERO(hdmi->pll); 184062306a36Sopenharmony_ci if (err) 184162306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, 184262306a36Sopenharmony_ci "failed to get PLL regulator\n"); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd"); 184562306a36Sopenharmony_ci err = PTR_ERR_OR_ZERO(hdmi->vdd); 184662306a36Sopenharmony_ci if (err) 184762306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, err, 184862306a36Sopenharmony_ci "failed to get VDD regulator\n"); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci hdmi->output.dev = &pdev->dev; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci err = tegra_output_probe(&hdmi->output); 185362306a36Sopenharmony_ci if (err < 0) 185462306a36Sopenharmony_ci return err; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci hdmi->regs = devm_platform_ioremap_resource(pdev, 0); 185762306a36Sopenharmony_ci if (IS_ERR(hdmi->regs)) { 185862306a36Sopenharmony_ci err = PTR_ERR(hdmi->regs); 185962306a36Sopenharmony_ci goto remove; 186062306a36Sopenharmony_ci } 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci err = platform_get_irq(pdev, 0); 186362306a36Sopenharmony_ci if (err < 0) 186462306a36Sopenharmony_ci goto remove; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci hdmi->irq = err; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci err = devm_request_irq(hdmi->dev, hdmi->irq, tegra_hdmi_irq, 0, 186962306a36Sopenharmony_ci dev_name(hdmi->dev), hdmi); 187062306a36Sopenharmony_ci if (err < 0) { 187162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", 187262306a36Sopenharmony_ci hdmi->irq, err); 187362306a36Sopenharmony_ci goto remove; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci platform_set_drvdata(pdev, hdmi); 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci err = devm_pm_runtime_enable(&pdev->dev); 187962306a36Sopenharmony_ci if (err) 188062306a36Sopenharmony_ci goto remove; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); 188362306a36Sopenharmony_ci if (err) 188462306a36Sopenharmony_ci goto remove; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci INIT_LIST_HEAD(&hdmi->client.list); 188762306a36Sopenharmony_ci hdmi->client.ops = &hdmi_client_ops; 188862306a36Sopenharmony_ci hdmi->client.dev = &pdev->dev; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci err = host1x_client_register(&hdmi->client); 189162306a36Sopenharmony_ci if (err < 0) { 189262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register host1x client: %d\n", 189362306a36Sopenharmony_ci err); 189462306a36Sopenharmony_ci goto remove; 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci return 0; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ciremove: 190062306a36Sopenharmony_ci tegra_output_remove(&hdmi->output); 190162306a36Sopenharmony_ci return err; 190262306a36Sopenharmony_ci} 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_cistatic void tegra_hdmi_remove(struct platform_device *pdev) 190562306a36Sopenharmony_ci{ 190662306a36Sopenharmony_ci struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci host1x_client_unregister(&hdmi->client); 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci tegra_output_remove(&hdmi->output); 191162306a36Sopenharmony_ci} 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_cistruct platform_driver tegra_hdmi_driver = { 191462306a36Sopenharmony_ci .driver = { 191562306a36Sopenharmony_ci .name = "tegra-hdmi", 191662306a36Sopenharmony_ci .of_match_table = tegra_hdmi_of_match, 191762306a36Sopenharmony_ci }, 191862306a36Sopenharmony_ci .probe = tegra_hdmi_probe, 191962306a36Sopenharmony_ci .remove_new = tegra_hdmi_remove, 192062306a36Sopenharmony_ci}; 1921