/*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/hdmi.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include "hdmi.h"
#include "drm.h"
#include "dc.h"
struct tmds_config {
unsigned int pclk;
u32 pll0;
u32 pll1;
u32 pe_current;
u32 drive_current;
u32 peak_current;
};
struct tegra_hdmi_config {
const struct tmds_config *tmds;
unsigned int num_tmds;
unsigned long fuse_override_offset;
u32 fuse_override_value;
bool has_sor_io_peak_current;
};
struct tegra_hdmi {
struct host1x_client client;
struct tegra_output output;
struct device *dev;
struct regulator *hdmi;
struct regulator *pll;
struct regulator *vdd;
void __iomem *regs;
unsigned int irq;
struct clk *clk_parent;
struct clk *clk;
struct reset_control *rst;
const struct tegra_hdmi_config *config;
unsigned int audio_source;
unsigned int audio_freq;
bool stereo;
bool dvi;
struct drm_info_list *debugfs_files;
struct drm_minor *minor;
struct dentry *debugfs;
};
static inline struct tegra_hdmi *
host1x_client_to_hdmi(struct host1x_client *client)
{
return container_of(client, struct tegra_hdmi, client);
}
static inline struct tegra_hdmi *to_hdmi(struct tegra_output *output)
{
return container_of(output, struct tegra_hdmi, output);
}
#define HDMI_AUDIOCLK_FREQ 216000000
#define HDMI_REKEY_DEFAULT 56
enum {
AUTO = 0,
SPDIF,
HDA,
};
static inline u32 tegra_hdmi_readl(struct tegra_hdmi *hdmi,
unsigned long offset)
{
return readl(hdmi->regs + (offset << 2));
}
static inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, u32 value,
unsigned long offset)
{
writel(value, hdmi->regs + (offset << 2));
}
struct tegra_hdmi_audio_config {
unsigned int pclk;
unsigned int n;
unsigned int cts;
unsigned int aval;
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_32k[] = {
{ 25200000, 4096, 25200, 24000 },
{ 27000000, 4096, 27000, 24000 },
{ 74250000, 4096, 74250, 24000 },
{ 148500000, 4096, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_44_1k[] = {
{ 25200000, 5880, 26250, 25000 },
{ 27000000, 5880, 28125, 25000 },
{ 74250000, 4704, 61875, 20000 },
{ 148500000, 4704, 123750, 20000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_48k[] = {
{ 25200000, 6144, 25200, 24000 },
{ 27000000, 6144, 27000, 24000 },
{ 74250000, 6144, 74250, 24000 },
{ 148500000, 6144, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_88_2k[] = {
{ 25200000, 11760, 26250, 25000 },
{ 27000000, 11760, 28125, 25000 },
{ 74250000, 9408, 61875, 20000 },
{ 148500000, 9408, 123750, 20000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_96k[] = {
{ 25200000, 12288, 25200, 24000 },
{ 27000000, 12288, 27000, 24000 },
{ 74250000, 12288, 74250, 24000 },
{ 148500000, 12288, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_176_4k[] = {
{ 25200000, 23520, 26250, 25000 },
{ 27000000, 23520, 28125, 25000 },
{ 74250000, 18816, 61875, 20000 },
{ 148500000, 18816, 123750, 20000 },
{ 0, 0, 0, 0 },
};
static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = {
{ 25200000, 24576, 25200, 24000 },
{ 27000000, 24576, 27000, 24000 },
{ 74250000, 24576, 74250, 24000 },
{ 148500000, 24576, 148500, 24000 },
{ 0, 0, 0, 0 },
};
static const struct tmds_config tegra20_tmds_config[] = {
{ /* slow pixel clock modes */
.pclk = 27000000,
.pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) |
SOR_PLL_TX_REG_LOAD(3),
.pll1 = SOR_PLL_TMDS_TERM_ENABLE,
.pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) |
PE_CURRENT1(PE_CURRENT_0_0_mA) |
PE_CURRENT2(PE_CURRENT_0_0_mA) |
PE_CURRENT3(PE_CURRENT_0_0_mA),
.drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) |
DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) |
DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) |
DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA),
},
{ /* high pixel clock modes */
.pclk = UINT_MAX,
.pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) |
SOR_PLL_TX_REG_LOAD(3),
.pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN,
.pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) |
PE_CURRENT1(PE_CURRENT_6_0_mA) |
PE_CURRENT2(PE_CURRENT_6_0_mA) |
PE_CURRENT3(PE_CURRENT_6_0_mA),
.drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) |
DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) |
DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) |
DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA),
},
};
static const struct tmds_config tegra30_tmds_config[] = {
{ /* 480p modes */
.pclk = 27000000,
.pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) |
SOR_PLL_TX_REG_LOAD(0),
.pll1 = SOR_PLL_TMDS_TERM_ENABLE,
.pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) |
PE_CURRENT1(PE_CURRENT_0_0_mA) |
PE_CURRENT2(PE_CURRENT_0_0_mA) |
PE_CURRENT3(PE_CURRENT_0_0_mA),
.drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) |
DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) |
DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) |
DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA),
}, { /* 720p modes */
.pclk = 74250000,
.pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
SOR_PLL_R
|