/*
* Copyright (C) 2013 NVIDIA Corporation
*
* 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/io.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/tegra-powergate.h>
#include <drm/drm_dp_helper.h>
#include "dc.h"
#include "drm.h"
#include "sor.h"
struct tegra_sor {
struct host1x_client client;
struct tegra_output output;
struct device *dev;
void __iomem *regs;
struct reset_control *rst;
struct clk *clk_parent;
struct clk *clk_safe;
struct clk *clk_dp;
struct clk *clk;
struct tegra_dpaux *dpaux;
bool enabled;
};
static inline struct tegra_sor *
host1x_client_to_sor(struct host1x_client *client)
{
return container_of(client, struct tegra_sor, client);
}
static inline struct tegra_sor *to_sor(struct tegra_output *output)
{
return container_of(output, struct tegra_sor, output);
}
static inline unsigned long tegra_sor_readl(struct tegra_sor *sor,
unsigned long offset)
{
return readl(sor->regs + (offset << 2));
}
static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value,
unsigned long offset)
{
writel(value, sor->regs + (offset << 2));
}
static int tegra_sor_dp_train_fast(struct tegra_sor *sor,
struct drm_dp_link *link)
{
unsigned long value;
unsigned int i;
u8 pattern;
int err;
/* setup lane parameters */
value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) |
SOR_LANE_DRIVE_CURRENT_LANE2(0x40) |
SOR_LANE_DRIVE_CURRENT_LANE1(0x40) |
SOR_LANE_DRIVE_CURRENT_LANE0(0x40);
tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0);
value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) |
SOR_LANE_PREEMPHASIS_LANE2(0x0f) |
SOR_LANE_PREEMPHASIS_LANE1(0x0f) |
SOR_LANE_PREEMPHASIS_LANE0(0x0f);
tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0);
value = SOR_LANE_POST_CURSOR_LANE3(0x00) |
SOR_LANE_POST_CURSOR_LANE2(0x00) |
SOR_LANE_POST_CURSOR_LANE1(0x00) |
SOR_LANE_POST_CURSOR_LANE0(0x00);
tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0);
/* disable LVDS mode */
tegra_sor_writel(sor, 0, SOR_LVDS);
value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
value |= SOR_DP_PADCTL_TX_PU_ENABLE;
value &= ~SOR_DP_PADCTL_TX_PU_MASK;
value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0;
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
usleep_range(10, 100);
value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0);
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B);
if (err < 0)
return err;
for (i = 0, value = 0; i < link->num_lanes; i++) {
unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
SOR_DP_TPG_SCRAMBLER_NONE |
SOR_DP_TPG_PATTERN_TRAIN1;
value = (value << 8) | lane;