Commit 6fad8f66 authored by Thierry Reding's avatar Thierry Reding

drm/tegra: sor: Demidlayer

Implement encoder and connector within the eDP driver itself using the
Tegra output helpers rather than using the Tegra output as midlayer. By
doing so one level of indirection is removed and output drivers become
more flexible while keeping the majority of the advantages provided by
the common output helpers.
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 5b901e78
...@@ -192,16 +192,11 @@ struct tegra_output_ops { ...@@ -192,16 +192,11 @@ struct tegra_output_ops {
enum drm_connector_status (*detect)(struct tegra_output *output); enum drm_connector_status (*detect)(struct tegra_output *output);
}; };
enum tegra_output_type {
TEGRA_OUTPUT_EDP,
};
struct tegra_output { struct tegra_output {
struct device_node *of_node; struct device_node *of_node;
struct device *dev; struct device *dev;
const struct tegra_output_ops *ops; const struct tegra_output_ops *ops;
enum tegra_output_type type;
struct drm_panel *panel; struct drm_panel *panel;
struct i2c_adapter *ddc; struct i2c_adapter *ddc;
......
...@@ -271,19 +271,8 @@ int tegra_output_remove(struct tegra_output *output) ...@@ -271,19 +271,8 @@ int tegra_output_remove(struct tegra_output *output)
int tegra_output_init(struct drm_device *drm, struct tegra_output *output) int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
{ {
int connector, encoder; int connector = DRM_MODE_CONNECTOR_Unknown;
int encoder = DRM_MODE_ENCODER_NONE;
switch (output->type) {
case TEGRA_OUTPUT_EDP:
connector = DRM_MODE_CONNECTOR_eDP;
encoder = DRM_MODE_ENCODER_TMDS;
break;
default:
connector = DRM_MODE_CONNECTOR_Unknown;
encoder = DRM_MODE_ENCODER_NONE;
break;
}
drm_connector_init(drm, &output->connector, &connector_funcs, drm_connector_init(drm, &output->connector, &connector_funcs,
connector); connector);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
#include <soc/tegra/pmc.h> #include <soc/tegra/pmc.h>
#include <drm/drm_dp_helper.h> #include <drm/drm_dp_helper.h>
#include <drm/drm_panel.h>
#include "dc.h" #include "dc.h"
#include "drm.h" #include "drm.h"
...@@ -481,10 +483,342 @@ static int tegra_sor_calc_config(struct tegra_sor *sor, ...@@ -481,10 +483,342 @@ static int tegra_sor_calc_config(struct tegra_sor *sor,
return 0; return 0;
} }
static int tegra_output_sor_enable(struct tegra_output *output) static int tegra_sor_detach(struct tegra_sor *sor)
{
unsigned long value, timeout;
/* switch to safe mode */
value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
value &= ~SOR_SUPER_STATE_MODE_NORMAL;
tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
tegra_sor_super_update(sor);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_PWR);
if (value & SOR_PWR_MODE_SAFE)
break;
}
if ((value & SOR_PWR_MODE_SAFE) == 0)
return -ETIMEDOUT;
/* go to sleep */
value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
tegra_sor_super_update(sor);
/* detach */
value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
value &= ~SOR_SUPER_STATE_ATTACHED;
tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
tegra_sor_super_update(sor);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_TEST);
if ((value & SOR_TEST_ATTACHED) == 0)
break;
usleep_range(25, 100);
}
if ((value & SOR_TEST_ATTACHED) != 0)
return -ETIMEDOUT;
return 0;
}
static int tegra_sor_power_down(struct tegra_sor *sor)
{
unsigned long value, timeout;
int err;
value = tegra_sor_readl(sor, SOR_PWR);
value &= ~SOR_PWR_NORMAL_STATE_PU;
value |= SOR_PWR_TRIGGER;
tegra_sor_writel(sor, value, SOR_PWR);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_PWR);
if ((value & SOR_PWR_TRIGGER) == 0)
return 0;
usleep_range(25, 100);
}
if ((value & SOR_PWR_TRIGGER) != 0)
return -ETIMEDOUT;
err = clk_set_parent(sor->clk, sor->clk_safe);
if (err < 0)
dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
/* stop lane sequencer */
value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
break;
usleep_range(25, 100);
}
if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0)
return -ETIMEDOUT;
value = tegra_sor_readl(sor, SOR_PLL_2);
value |= SOR_PLL_2_PORT_POWERDOWN;
tegra_sor_writel(sor, value, SOR_PLL_2);
usleep_range(20, 100);
value = tegra_sor_readl(sor, SOR_PLL_0);
value |= SOR_PLL_0_POWER_OFF;
value |= SOR_PLL_0_VCOPD;
tegra_sor_writel(sor, value, SOR_PLL_0);
value = tegra_sor_readl(sor, SOR_PLL_2);
value |= SOR_PLL_2_SEQ_PLLCAPPD;
value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
tegra_sor_writel(sor, value, SOR_PLL_2);
usleep_range(20, 100);
return 0;
}
static int tegra_sor_crc_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static int tegra_sor_crc_release(struct inode *inode, struct file *file)
{
return 0;
}
static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
{
u32 value;
timeout = jiffies + msecs_to_jiffies(timeout);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_CRC_A);
if (value & SOR_CRC_A_VALID)
return 0;
usleep_range(100, 200);
}
return -ETIMEDOUT;
}
static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
size_t size, loff_t *ppos)
{
struct tegra_sor *sor = file->private_data;
ssize_t num, err;
char buf[10];
u32 value;
mutex_lock(&sor->lock);
if (!sor->enabled) {
err = -EAGAIN;
goto unlock;
}
value = tegra_sor_readl(sor, SOR_STATE_1);
value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
tegra_sor_writel(sor, value, SOR_STATE_1);
value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
value |= SOR_CRC_CNTRL_ENABLE;
tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
value = tegra_sor_readl(sor, SOR_TEST);
value &= ~SOR_TEST_CRC_POST_SERIALIZE;
tegra_sor_writel(sor, value, SOR_TEST);
err = tegra_sor_crc_wait(sor, 100);
if (err < 0)
goto unlock;
tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
value = tegra_sor_readl(sor, SOR_CRC_B);
num = scnprintf(buf, sizeof(buf), "%08x\n", value);
err = simple_read_from_buffer(buffer, size, ppos, buf, num);
unlock:
mutex_unlock(&sor->lock);
return err;
}
static const struct file_operations tegra_sor_crc_fops = {
.owner = THIS_MODULE,
.open = tegra_sor_crc_open,
.read = tegra_sor_crc_read,
.release = tegra_sor_crc_release,
};
static int tegra_sor_debugfs_init(struct tegra_sor *sor,
struct drm_minor *minor)
{
struct dentry *entry;
int err = 0;
sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
if (!sor->debugfs)
return -ENOMEM;
entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
&tegra_sor_crc_fops);
if (!entry) {
dev_err(sor->dev,
"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
minor->debugfs_root->d_name.name);
err = -ENOMEM;
goto remove;
}
return err;
remove:
debugfs_remove(sor->debugfs);
sor->debugfs = NULL;
return err;
}
static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
{
debugfs_remove_recursive(sor->debugfs);
sor->debugfs = NULL;
return 0;
}
static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode)
{
}
static enum drm_connector_status
tegra_sor_connector_detect(struct drm_connector *connector, bool force)
{
struct tegra_output *output = connector_to_output(connector);
struct tegra_sor *sor = to_sor(output);
if (sor->dpaux)
return tegra_dpaux_detect(sor->dpaux);
return connector_status_unknown;
}
static const struct drm_connector_funcs tegra_sor_connector_funcs = {
.dpms = tegra_sor_connector_dpms,
.detect = tegra_sor_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = tegra_output_connector_destroy,
};
static int tegra_sor_connector_get_modes(struct drm_connector *connector)
{
struct tegra_output *output = connector_to_output(connector);
struct tegra_sor *sor = to_sor(output);
int err;
if (sor->dpaux)
tegra_dpaux_enable(sor->dpaux);
err = tegra_output_connector_get_modes(connector);
if (sor->dpaux)
tegra_dpaux_disable(sor->dpaux);
return err;
}
static enum drm_mode_status
tegra_sor_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
.get_modes = tegra_sor_connector_get_modes,
.mode_valid = tegra_sor_connector_mode_valid,
.best_encoder = tegra_output_connector_best_encoder,
};
static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
.destroy = tegra_output_encoder_destroy,
};
static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode)
{
}
static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
unsigned long pclk = mode->clock * 1000;
struct tegra_sor *sor = to_sor(output);
int err;
err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0);
if (err < 0) {
dev_err(output->dev, "failed to setup DC clock: %d\n", err);
return false;
}
err = clk_set_rate(sor->clk_parent, pclk);
if (err < 0) {
dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
pclk);
return false;
}
return true;
}
static void tegra_sor_encoder_prepare(struct drm_encoder *encoder)
{
}
static void tegra_sor_encoder_commit(struct drm_encoder *encoder)
{ {
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); }
struct drm_display_mode *mode = &dc->base.mode;
static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
unsigned int vbe, vse, hbe, hse, vbs, hbs, i; unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
struct tegra_sor *sor = to_sor(output); struct tegra_sor *sor = to_sor(output);
struct tegra_sor_config config; struct tegra_sor_config config;
...@@ -504,6 +838,9 @@ static int tegra_output_sor_enable(struct tegra_output *output) ...@@ -504,6 +838,9 @@ static int tegra_output_sor_enable(struct tegra_output *output)
reset_control_deassert(sor->rst); reset_control_deassert(sor->rst);
if (output->panel)
drm_panel_prepare(output->panel);
/* FIXME: properly convert to struct drm_dp_aux */ /* FIXME: properly convert to struct drm_dp_aux */
aux = (struct drm_dp_aux *)sor->dpaux; aux = (struct drm_dp_aux *)sor->dpaux;
...@@ -878,170 +1215,56 @@ static int tegra_output_sor_enable(struct tegra_output *output) ...@@ -878,170 +1215,56 @@ static int tegra_output_sor_enable(struct tegra_output *output)
SOR_CSTM_UPPER; SOR_CSTM_UPPER;
tegra_sor_writel(sor, value, SOR_CSTM); tegra_sor_writel(sor, value, SOR_CSTM);
/* PWM setup */ /* PWM setup */
err = tegra_sor_setup_pwm(sor, 250); err = tegra_sor_setup_pwm(sor, 250);
if (err < 0) { if (err < 0) {
dev_err(sor->dev, "failed to setup PWM: %d\n", err); dev_err(sor->dev, "failed to setup PWM: %d\n", err);
goto unlock; goto unlock;
}
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
value |= SOR_ENABLE;
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
tegra_sor_update(sor);
err = tegra_sor_attach(sor);
if (err < 0) {
dev_err(sor->dev, "failed to attach SOR: %d\n", err);
goto unlock;
}
err = tegra_sor_wakeup(sor);
if (err < 0) {
dev_err(sor->dev, "failed to enable DC: %d\n", err);
goto unlock;
}
sor->enabled = true;
unlock:
mutex_unlock(&sor->lock);
return err;
}
static int tegra_sor_detach(struct tegra_sor *sor)
{
unsigned long value, timeout;
/* switch to safe mode */
value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
value &= ~SOR_SUPER_STATE_MODE_NORMAL;
tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
tegra_sor_super_update(sor);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_PWR);
if (value & SOR_PWR_MODE_SAFE)
break;
}
if ((value & SOR_PWR_MODE_SAFE) == 0)
return -ETIMEDOUT;
/* go to sleep */
value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
tegra_sor_super_update(sor);
/* detach */
value = tegra_sor_readl(sor, SOR_SUPER_STATE_1);
value &= ~SOR_SUPER_STATE_ATTACHED;
tegra_sor_writel(sor, value, SOR_SUPER_STATE_1);
tegra_sor_super_update(sor);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_TEST);
if ((value & SOR_TEST_ATTACHED) == 0)
break;
usleep_range(25, 100);
}
if ((value & SOR_TEST_ATTACHED) != 0)
return -ETIMEDOUT;
return 0;
}
static int tegra_sor_power_down(struct tegra_sor *sor)
{
unsigned long value, timeout;
int err;
value = tegra_sor_readl(sor, SOR_PWR);
value &= ~SOR_PWR_NORMAL_STATE_PU;
value |= SOR_PWR_TRIGGER;
tegra_sor_writel(sor, value, SOR_PWR);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_PWR);
if ((value & SOR_PWR_TRIGGER) == 0)
return 0;
usleep_range(25, 100);
}
if ((value & SOR_PWR_TRIGGER) != 0)
return -ETIMEDOUT;
err = clk_set_parent(sor->clk, sor->clk_safe);
if (err < 0)
dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2);
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
/* stop lane sequencer */
value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL);
if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0)
break;
usleep_range(25, 100);
} }
if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
return -ETIMEDOUT; value |= SOR_ENABLE;
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
value = tegra_sor_readl(sor, SOR_PLL_2); tegra_sor_update(sor);
value |= SOR_PLL_2_PORT_POWERDOWN;
tegra_sor_writel(sor, value, SOR_PLL_2);
usleep_range(20, 100); err = tegra_sor_attach(sor);
if (err < 0) {
dev_err(sor->dev, "failed to attach SOR: %d\n", err);
goto unlock;
}
value = tegra_sor_readl(sor, SOR_PLL_0); err = tegra_sor_wakeup(sor);
value |= SOR_PLL_0_POWER_OFF; if (err < 0) {
value |= SOR_PLL_0_VCOPD; dev_err(sor->dev, "failed to enable DC: %d\n", err);
tegra_sor_writel(sor, value, SOR_PLL_0); goto unlock;
}
value = tegra_sor_readl(sor, SOR_PLL_2); if (output->panel)
value |= SOR_PLL_2_SEQ_PLLCAPPD; drm_panel_enable(output->panel);
value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE;
tegra_sor_writel(sor, value, SOR_PLL_2);
usleep_range(20, 100); sor->enabled = true;
return 0; unlock:
mutex_unlock(&sor->lock);
} }
static int tegra_output_sor_disable(struct tegra_output *output) static void tegra_sor_encoder_disable(struct drm_encoder *encoder)
{ {
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
struct tegra_sor *sor = to_sor(output); struct tegra_sor *sor = to_sor(output);
unsigned long value; u32 value;
int err = 0; int err;
mutex_lock(&sor->lock); mutex_lock(&sor->lock);
if (!sor->enabled) if (!sor->enabled)
goto unlock; goto unlock;
if (output->panel)
drm_panel_disable(output->panel);
err = tegra_sor_detach(sor); err = tegra_sor_detach(sor);
if (err < 0) { if (err < 0) {
dev_err(sor->dev, "failed to detach SOR: %d\n", err); dev_err(sor->dev, "failed to detach SOR: %d\n", err);
...@@ -1056,21 +1279,6 @@ static int tegra_output_sor_disable(struct tegra_output *output) ...@@ -1056,21 +1279,6 @@ static int tegra_output_sor_disable(struct tegra_output *output)
* sure it's only executed when the output is attached to one. * sure it's only executed when the output is attached to one.
*/ */
if (dc) { if (dc) {
/*
* XXX: We can't do this here because it causes the SOR to go
* into an erroneous state and the output will look scrambled
* the next time it is enabled. Presumably this is because we
* should be doing this only on the next VBLANK. A possible
* solution would be to queue a "power-off" event to trigger
* this code to be run during the next VBLANK.
*/
/*
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
*/
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
value &= ~SOR_ENABLE; value &= ~SOR_ENABLE;
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
...@@ -1098,187 +1306,27 @@ static int tegra_output_sor_disable(struct tegra_output *output) ...@@ -1098,187 +1306,27 @@ static int tegra_output_sor_disable(struct tegra_output *output)
goto unlock; goto unlock;
} }
reset_control_assert(sor->rst); if (output->panel)
drm_panel_unprepare(output->panel);
clk_disable_unprepare(sor->clk); clk_disable_unprepare(sor->clk);
reset_control_assert(sor->rst);
sor->enabled = false; sor->enabled = false;
unlock: unlock:
mutex_unlock(&sor->lock); mutex_unlock(&sor->lock);
return err;
}
static int tegra_output_sor_setup_clock(struct tegra_output *output,
struct clk *clk, unsigned long pclk,
unsigned int *div)
{
struct tegra_sor *sor = to_sor(output);
int err;
err = clk_set_parent(clk, sor->clk_parent);
if (err < 0) {
dev_err(sor->dev, "failed to set parent clock: %d\n", err);
return err;
}
err = clk_set_rate(sor->clk_parent, pclk);
if (err < 0) {
dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk);
return err;
}
*div = 0;
return 0;
}
static int tegra_output_sor_check_mode(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status)
{
/*
* FIXME: For now, always assume that the mode is okay.
*/
*status = MODE_OK;
return 0;
}
static enum drm_connector_status
tegra_output_sor_detect(struct tegra_output *output)
{
struct tegra_sor *sor = to_sor(output);
if (sor->dpaux)
return tegra_dpaux_detect(sor->dpaux);
return connector_status_unknown;
}
static const struct tegra_output_ops sor_ops = {
.enable = tegra_output_sor_enable,
.disable = tegra_output_sor_disable,
.setup_clock = tegra_output_sor_setup_clock,
.check_mode = tegra_output_sor_check_mode,
.detect = tegra_output_sor_detect,
};
static int tegra_sor_crc_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static int tegra_sor_crc_release(struct inode *inode, struct file *file)
{
return 0;
}
static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
{
u32 value;
timeout = jiffies + msecs_to_jiffies(timeout);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_CRC_A);
if (value & SOR_CRC_A_VALID)
return 0;
usleep_range(100, 200);
}
return -ETIMEDOUT;
}
static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
size_t size, loff_t *ppos)
{
struct tegra_sor *sor = file->private_data;
ssize_t num, err;
char buf[10];
u32 value;
mutex_lock(&sor->lock);
if (!sor->enabled) {
err = -EAGAIN;
goto unlock;
}
value = tegra_sor_readl(sor, SOR_STATE_1);
value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
tegra_sor_writel(sor, value, SOR_STATE_1);
value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
value |= SOR_CRC_CNTRL_ENABLE;
tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
value = tegra_sor_readl(sor, SOR_TEST);
value &= ~SOR_TEST_CRC_POST_SERIALIZE;
tegra_sor_writel(sor, value, SOR_TEST);
err = tegra_sor_crc_wait(sor, 100);
if (err < 0)
goto unlock;
tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
value = tegra_sor_readl(sor, SOR_CRC_B);
num = scnprintf(buf, sizeof(buf), "%08x\n", value);
err = simple_read_from_buffer(buffer, size, ppos, buf, num);
unlock:
mutex_unlock(&sor->lock);
return err;
} }
static const struct file_operations tegra_sor_crc_fops = { static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
.owner = THIS_MODULE, .dpms = tegra_sor_encoder_dpms,
.open = tegra_sor_crc_open, .mode_fixup = tegra_sor_encoder_mode_fixup,
.read = tegra_sor_crc_read, .prepare = tegra_sor_encoder_prepare,
.release = tegra_sor_crc_release, .commit = tegra_sor_encoder_commit,
.mode_set = tegra_sor_encoder_mode_set,
.disable = tegra_sor_encoder_disable,
}; };
static int tegra_sor_debugfs_init(struct tegra_sor *sor,
struct drm_minor *minor)
{
struct dentry *entry;
int err = 0;
sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
if (!sor->debugfs)
return -ENOMEM;
entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
&tegra_sor_crc_fops);
if (!entry) {
dev_err(sor->dev,
"cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
minor->debugfs_root->d_name.name);
err = -ENOMEM;
goto remove;
}
return err;
remove:
debugfs_remove(sor->debugfs);
sor->debugfs = NULL;
return err;
}
static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
{
debugfs_remove_recursive(sor->debugfs);
sor->debugfs = NULL;
return 0;
}
static int tegra_sor_init(struct host1x_client *client) static int tegra_sor_init(struct host1x_client *client)
{ {
struct drm_device *drm = dev_get_drvdata(client->parent); struct drm_device *drm = dev_get_drvdata(client->parent);
...@@ -1288,16 +1336,31 @@ static int tegra_sor_init(struct host1x_client *client) ...@@ -1288,16 +1336,31 @@ static int tegra_sor_init(struct host1x_client *client)
if (!sor->dpaux) if (!sor->dpaux)
return -ENODEV; return -ENODEV;
sor->output.type = TEGRA_OUTPUT_EDP;
sor->output.dev = sor->dev; sor->output.dev = sor->dev;
sor->output.ops = &sor_ops;
err = tegra_output_init(drm, &sor->output); drm_connector_init(drm, &sor->output.connector,
if (err < 0) { &tegra_sor_connector_funcs,
dev_err(sor->dev, "output setup failed: %d\n", err); DRM_MODE_CONNECTOR_eDP);
return err; drm_connector_helper_add(&sor->output.connector,
} &tegra_sor_connector_helper_funcs);
sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
if (sor->output.panel)
drm_panel_attach(sor->output.panel, &sor->output.connector);
drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs,
DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(&sor->output.encoder,
&tegra_sor_encoder_helper_funcs);
drm_mode_connector_attach_encoder(&sor->output.connector,
&sor->output.encoder);
drm_connector_register(&sor->output.connector);
sor->output.encoder.possible_crtcs = 0x3;
if (gpio_is_valid(sor->output.hpd_gpio))
enable_irq(sor->output.hpd_irq);
if (IS_ENABLED(CONFIG_DEBUG_FS)) { if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_sor_debugfs_init(sor, drm->primary); err = tegra_sor_debugfs_init(sor, drm->primary);
...@@ -1313,6 +1376,20 @@ static int tegra_sor_init(struct host1x_client *client) ...@@ -1313,6 +1376,20 @@ static int tegra_sor_init(struct host1x_client *client)
} }
} }
err = clk_prepare_enable(sor->clk);
if (err < 0) {
dev_err(sor->dev, "failed to enable clock: %d\n", err);
return err;
}
err = clk_prepare_enable(sor->clk_safe);
if (err < 0)
return err;
err = clk_prepare_enable(sor->clk_dp);
if (err < 0)
return err;
return 0; return 0;
} }
...@@ -1321,12 +1398,6 @@ static int tegra_sor_exit(struct host1x_client *client) ...@@ -1321,12 +1398,6 @@ static int tegra_sor_exit(struct host1x_client *client)
struct tegra_sor *sor = host1x_client_to_sor(client); struct tegra_sor *sor = host1x_client_to_sor(client);
int err; int err;
err = tegra_output_disable(&sor->output);
if (err < 0) {
dev_err(sor->dev, "output failed to disable: %d\n", err);
return err;
}
if (sor->dpaux) { if (sor->dpaux) {
err = tegra_dpaux_detach(sor->dpaux); err = tegra_dpaux_detach(sor->dpaux);
if (err < 0) { if (err < 0) {
...@@ -1335,18 +1406,16 @@ static int tegra_sor_exit(struct host1x_client *client) ...@@ -1335,18 +1406,16 @@ static int tegra_sor_exit(struct host1x_client *client)
} }
} }
clk_disable_unprepare(sor->clk_safe);
clk_disable_unprepare(sor->clk_dp);
clk_disable_unprepare(sor->clk);
if (IS_ENABLED(CONFIG_DEBUG_FS)) { if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_sor_debugfs_exit(sor); err = tegra_sor_debugfs_exit(sor);
if (err < 0) if (err < 0)
dev_err(sor->dev, "debugfs cleanup failed: %d\n", err); dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
} }
err = tegra_output_exit(&sor->output);
if (err < 0) {
dev_err(sor->dev, "output cleanup failed: %d\n", err);
return err;
}
return 0; return 0;
} }
...@@ -1398,26 +1467,14 @@ static int tegra_sor_probe(struct platform_device *pdev) ...@@ -1398,26 +1467,14 @@ static int tegra_sor_probe(struct platform_device *pdev)
if (IS_ERR(sor->clk_parent)) if (IS_ERR(sor->clk_parent))
return PTR_ERR(sor->clk_parent); return PTR_ERR(sor->clk_parent);
err = clk_prepare_enable(sor->clk_parent);
if (err < 0)
return err;
sor->clk_safe = devm_clk_get(&pdev->dev, "safe"); sor->clk_safe = devm_clk_get(&pdev->dev, "safe");
if (IS_ERR(sor->clk_safe)) if (IS_ERR(sor->clk_safe))
return PTR_ERR(sor->clk_safe); return PTR_ERR(sor->clk_safe);
err = clk_prepare_enable(sor->clk_safe);
if (err < 0)
return err;
sor->clk_dp = devm_clk_get(&pdev->dev, "dp"); sor->clk_dp = devm_clk_get(&pdev->dev, "dp");
if (IS_ERR(sor->clk_dp)) if (IS_ERR(sor->clk_dp))
return PTR_ERR(sor->clk_dp); return PTR_ERR(sor->clk_dp);
err = clk_prepare_enable(sor->clk_dp);
if (err < 0)
return err;
INIT_LIST_HEAD(&sor->client.list); INIT_LIST_HEAD(&sor->client.list);
sor->client.ops = &sor_client_ops; sor->client.ops = &sor_client_ops;
sor->client.dev = &pdev->dev; sor->client.dev = &pdev->dev;
...@@ -1448,10 +1505,11 @@ static int tegra_sor_remove(struct platform_device *pdev) ...@@ -1448,10 +1505,11 @@ static int tegra_sor_remove(struct platform_device *pdev)
return err; return err;
} }
clk_disable_unprepare(sor->clk_parent); err = tegra_output_remove(&sor->output);
clk_disable_unprepare(sor->clk_safe); if (err < 0) {
clk_disable_unprepare(sor->clk_dp); dev_err(&pdev->dev, "failed to remove output: %d\n", err);
clk_disable_unprepare(sor->clk); return err;
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment