* Copyright (C) 2010 Google, Inc.
* Author: Erik Gilling <konkers@android.com>
*
- * Copyright (C) 2010-2011 NVIDIA Corporation
+ * Copyright (C) 2010-2012 NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/backlight.h>
+#include <linux/gpio.h>
#include <video/tegrafb.h>
#include <drm/drm_fixed.h>
+#ifdef CONFIG_SWITCH
+#include <linux/switch.h>
+#endif
+
#include <mach/clk.h>
#include <mach/dc.h>
#include <mach/latency_allowance.h>
#include "dc_reg.h"
+#include "dc_config.h"
#include "dc_priv.h"
-#include "overlay.h"
#include "nvsd.h"
#define TEGRA_CRC_LATCHED_DELAY 34
+#define DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL 0x01000000
+#define DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL 0x0
+
#ifndef CONFIG_TEGRA_FPGA_PLATFORM
#define ALL_UF_INT (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)
#else
#endif
static int no_vsync;
+static struct fb_videomode tegra_dc_hdmi_fallback_mode = {
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = KHZ2PICOS(25200),
+ .hsync_len = 96, /* h_sync_width */
+ .vsync_len = 2, /* v_sync_width */
+ .left_margin = 48, /* h_back_porch */
+ .upper_margin = 33, /* v_back_porch */
+ .right_margin = 16, /* h_front_porch */
+ .lower_margin = 10, /* v_front_porch */
+ .vmode = 0,
+ .sync = 0,
+};
+
+static void _tegra_dc_controller_disable(struct tegra_dc *dc);
module_param_named(no_vsync, no_vsync, int, S_IRUGO | S_IWUSR);
DEFINE_MUTEX(tegra_dc_lock);
DEFINE_MUTEX(shared_lock);
-static const struct {
- bool h;
- bool v;
-} can_filter[] = {
- /* Window A has no filtering */
- { false, false },
- /* Window B has both H and V filtering */
- { true, true },
- /* Window C has only H filtering */
- { false, true },
-};
-static inline bool win_use_v_filter(const struct tegra_dc_win *win)
+static inline bool win_use_v_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
{
- return can_filter[win->idx].v &&
+ return tegra_dc_feature_has_filter(dc, win->idx, HAS_V_FILTER) &&
win->h.full != dfixed_const(win->out_h);
}
-static inline bool win_use_h_filter(const struct tegra_dc_win *win)
+static inline bool win_use_h_filter(struct tegra_dc *dc, const struct tegra_dc_win *win)
{
- return can_filter[win->idx].h &&
+ return tegra_dc_feature_has_filter(dc, win->idx, HAS_H_FILTER) &&
win->w.full != dfixed_const(win->out_w);
}
case TEGRA_WIN_FMT_YUV420P:
case TEGRA_WIN_FMT_YCbCr422P:
case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
return 8;
+ /* YUYV packed into 32-bits */
+ case TEGRA_WIN_FMT_YCbCr422:
+ case TEGRA_WIN_FMT_YUV422:
+ return 16;
+ }
+ return 0;
+}
+
+static inline bool tegra_dc_is_yuv(int fmt)
+{
+ switch (fmt) {
+ case TEGRA_WIN_FMT_YUV420P:
+ case TEGRA_WIN_FMT_YCbCr420P:
+ case TEGRA_WIN_FMT_YCbCr422P:
+ case TEGRA_WIN_FMT_YUV422P:
case TEGRA_WIN_FMT_YCbCr422:
case TEGRA_WIN_FMT_YUV422:
case TEGRA_WIN_FMT_YCbCr422R:
case TEGRA_WIN_FMT_YUV422R:
case TEGRA_WIN_FMT_YCbCr422RA:
case TEGRA_WIN_FMT_YUV422RA:
- /* FIXME: need to know the bpp of these formats */
- return 0;
+ return true;
}
- return 0;
+ return false;
}
static inline bool tegra_dc_is_yuv_planar(int fmt)
case TEGRA_WIN_FMT_YCbCr420P:
case TEGRA_WIN_FMT_YCbCr422P:
case TEGRA_WIN_FMT_YUV422P:
+ case TEGRA_WIN_FMT_YCbCr422R:
+ case TEGRA_WIN_FMT_YUV422R:
+ case TEGRA_WIN_FMT_YCbCr422RA:
+ case TEGRA_WIN_FMT_YUV422RA:
return true;
}
return false;
print(data, buff); \
} while (0)
+#define print_mode_info(dc, mode) do { \
+ trace_printk("%s:Mode settings: " \
+ "ref_to_sync: H = %d V = %d, " \
+ "sync_width: H = %d V = %d, " \
+ "back_porch: H = %d V = %d, " \
+ "active: H = %d V = %d, " \
+ "front_porch: H = %d V = %d, " \
+ "pclk = %d, stereo mode = %d\n", \
+ dc->ndev->name, \
+ mode.h_ref_to_sync, mode.v_ref_to_sync, \
+ mode.h_sync_width, mode.v_sync_width, \
+ mode.h_back_porch, mode.v_back_porch, \
+ mode.h_active, mode.v_active, \
+ mode.h_front_porch, mode.v_front_porch, \
+ mode.pclk, mode.stereo_mode); \
+ } while (0)
+
+#define print_underflow_info(dc) do { \
+ trace_printk("%s:Underflow stats: underflows : %llu, " \
+ "undeflows_a : %llu, " \
+ "underflows_b : %llu, " \
+ "underflows_c : %llu\n", \
+ dc->ndev->name, \
+ dc->stats.underflows, \
+ dc->stats.underflows_a, dc->stats.underflows_b, \
+ dc->stats.underflows_c); \
+ } while (0)
+
static void _dump_regs(struct tegra_dc *dc, void *data,
void (* print)(void *data, const char *str))
{
return ret;
}
-unsigned int tegra_dc_has_multiple_dc(void)
+static unsigned int tegra_dc_has_multiple_dc(void)
{
unsigned int idx;
unsigned int cnt = 0;
return (cnt > 1);
}
+/* get the stride size of a window.
+ * return: stride size in bytes for window win. or 0 if unavailble. */
+int tegra_dc_get_stride(struct tegra_dc *dc, unsigned win)
+{
+ u32 tmp;
+ u32 stride;
+
+ if (!dc->enabled)
+ return 0;
+ BUG_ON(win > DC_N_WINDOWS);
+ tegra_dc_writel(dc, WINDOW_A_SELECT << win,
+ DC_CMD_DISPLAY_WINDOW_HEADER);
+ tmp = tegra_dc_readl(dc, DC_WIN_LINE_STRIDE);
+ return GET_LINE_STRIDE(tmp);
+}
+EXPORT_SYMBOL(tegra_dc_get_stride);
+
struct tegra_dc *tegra_dc_get_dc(unsigned idx)
{
if (idx < TEGRA_MAX_DC)
}
EXPORT_SYMBOL(tegra_dc_get_connected);
+bool tegra_dc_hpd(struct tegra_dc *dc)
+{
+ int sense;
+ int level;
+
+ level = gpio_get_value(dc->out->hotplug_gpio);
+
+ sense = dc->out->flags & TEGRA_DC_OUT_HOTPLUG_MASK;
+
+ return (sense == TEGRA_DC_OUT_HOTPLUG_HIGH && level) ||
+ (sense == TEGRA_DC_OUT_HOTPLUG_LOW && !level);
+}
+EXPORT_SYMBOL(tegra_dc_hpd);
+
static u32 blend_topwin(u32 flags)
{
if (flags & TEGRA_WIN_FLAG_BLEND_COVERAGE)
mutex_unlock(&dc->lock);
+ tegra_dc_update_windows(&win, 1);
+
return 0;
}
/* tegra_dc_get_bandwidth() treats V filter windows as double
* bandwidth, but LA has a seperate client for V filter */
- if (w->idx == 1 && win_use_v_filter(w))
+ if (w->idx == 1 && win_use_v_filter(dc, w))
bw /= 2;
- /* our bandwidth is in bytes/sec, but LA takes MBps.
- * round up bandwidth to 1MBps */
- bw = bw / 1000000 + 1;
+ /* our bandwidth is in kbytes/sec, but LA takes MBps.
+ * round up bandwidth to next 1MBps */
+ bw = bw / 1000 + 1;
#ifdef CONFIG_TEGRA_SILICON_PLATFORM
tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw);
* pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor *
* (windows_tiling ? 2 : 1)
*
- *
* note:
* (*) We use 2 tap V filter, so need double BW if use V filter
* (*) Tiling mode on T30 and DDR3 requires double BW
+ *
+ * return:
+ * bandwidth in kBps
*/
static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
struct tegra_dc_win *w)
* is of the luma plane's size only. */
bpp = tegra_dc_is_yuv_planar(w->fmt) ?
2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt);
- /* perform calculations with most significant bits of pixel clock
- * to prevent overflow of long. */
- ret = (unsigned long)(dc->pixel_clk >> 16) *
- bpp / 8 *
- (win_use_v_filter(w) ? 2 : 1) * dfixed_trunc(w->w) / w->out_w *
+ ret = dc->mode.pclk / 1000UL * bpp / 8 * (win_use_v_filter(dc, w) ? 2 : 1)
+ * dfixed_trunc(w->w) / w->out_w *
(WIN_IS_TILED(w) ? tiled_windows_bw_multiplier : 1);
-/*
- * Assuming 48% efficiency: i.e. if we calculate we need 70MBps, we
- * will request 147MBps from EMC.
- */
- ret = ret * 2 + ret / 10;
-
- /* if overflowed */
- if (ret > (1UL << 31))
- return ULONG_MAX;
-
- return ret << 16; /* restore the scaling we did above */
+ return ret;
}
-unsigned long tegra_dc_get_bandwidth(struct tegra_dc_win *windows[], int n)
+static unsigned long tegra_dc_get_bandwidth(
+ struct tegra_dc_win *windows[], int n)
{
int i;
* bandwidths */
for (i = 0; i < n; i++) {
struct tegra_dc_win *w = windows[i];
+
if (w)
- w->new_bandwidth = tegra_dc_calc_win_bandwidth(w->dc, w);
+ w->new_bandwidth =
+ tegra_dc_calc_win_bandwidth(w->dc, w);
}
return tegra_dc_find_max_bandwidth(windows, n);
}
+/* to save power, call when display memory clients would be idle */
+static void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
+{
+ trace_printk("%s:%s rate=%d\n", dc->ndev->name, __func__,
+ dc->emc_clk_rate);
+ if (tegra_is_clk_enabled(dc->emc_clk))
+ clk_disable(dc->emc_clk);
+ dc->emc_clk_rate = 0;
+}
+
static void tegra_dc_program_bandwidth(struct tegra_dc *dc)
{
unsigned i;
if (dc->emc_clk_rate != dc->new_emc_clk_rate) {
+ /* going from 0 to non-zero */
+ if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk))
+ clk_enable(dc->emc_clk);
+
dc->emc_clk_rate = dc->new_emc_clk_rate;
clk_set_rate(dc->emc_clk, dc->emc_clk_rate);
+
+ if (!dc->new_emc_clk_rate) /* going from non-zero to 0 */
+ clk_disable(dc->emc_clk);
}
for (i = 0; i < DC_N_WINDOWS; i++) {
struct tegra_dc_win *w = &dc->windows[i];
- if (w->bandwidth != w->new_bandwidth)
+
+ if (w->bandwidth != w->new_bandwidth && w->new_bandwidth != 0)
tegra_dc_set_latency_allowance(dc, w);
+ trace_printk("%s:win%u bandwidth=%d\n", dc->ndev->name, w->idx,
+ w->bandwidth);
}
}
/* calculate the new rate based on this POST */
new_rate = tegra_dc_get_bandwidth(windows, n);
- new_rate = EMC_BW_TO_FREQ(new_rate);
+ if (WARN_ONCE(new_rate > (ULONG_MAX / 1000), "bandwidth maxed out\n"))
+ new_rate = ULONG_MAX;
+ else
+ new_rate = EMC_BW_TO_FREQ(new_rate * 1000);
if (tegra_dc_has_multiple_dc())
new_rate = ULONG_MAX;
+ trace_printk("%s:new_emc_clk_rate=%ld\n", dc->ndev->name, new_rate);
dc->new_emc_clk_rate = new_rate;
return 0;
dc = windows[0]->dc;
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
+ /* Acquire one_shot_lock to avoid race condition between
+ * cancellation of old delayed work and schedule of new
+ * delayed work. */
+ mutex_lock(&dc->one_shot_lock);
+ cancel_delayed_work_sync(&dc->one_shot_work);
+ }
mutex_lock(&dc->lock);
if (!dc->enabled) {
mutex_unlock(&dc->lock);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ mutex_unlock(&dc->one_shot_lock);
return -EFAULT;
}
fixed20_12 h_offset, v_offset;
bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0;
bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0;
+ bool yuv = tegra_dc_is_yuv(win->fmt);
bool yuvp = tegra_dc_is_yuv_planar(win->fmt);
unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8;
/* Bytes per pixel of bandwidth, used for dda_inc calculation */
unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1);
- const bool filter_h = win_use_h_filter(win);
- const bool filter_v = win_use_v_filter(win);
+ const bool filter_h = win_use_h_filter(dc, win);
+ const bool filter_v = win_use_v_filter(dc, win);
if (win->z != dc->blend.z[win->idx]) {
dc->blend.z[win->idx] = win->z;
continue;
}
- tegra_dc_writel(dc, win->fmt, DC_WIN_COLOR_DEPTH);
- tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
+ tegra_dc_writel(dc, win->fmt & 0x1f, DC_WIN_COLOR_DEPTH);
+ tegra_dc_writel(dc, win->fmt >> 6, DC_WIN_BYTE_SWAP);
tegra_dc_writel(dc,
V_POSITION(win->out_y) | H_POSITION(win->out_x),
tegra_dc_writel(dc,
V_SIZE(win->out_h) | H_SIZE(win->out_w),
DC_WIN_SIZE);
- tegra_dc_writel(dc,
- V_PRESCALED_SIZE(dfixed_trunc(win->h)) |
- H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp),
- DC_WIN_PRESCALED_SIZE);
-
- h_dda = compute_dda_inc(win->w, win->out_w, false, Bpp_bw);
- v_dda = compute_dda_inc(win->h, win->out_h, true, Bpp_bw);
- tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda),
- DC_WIN_DDA_INCREMENT);
- h_dda = compute_initial_dda(win->x);
- v_dda = compute_initial_dda(win->y);
- tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
- tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
+
+ if (tegra_dc_feature_has_scaling(dc, win->idx)) {
+ tegra_dc_writel(dc,
+ V_PRESCALED_SIZE(dfixed_trunc(win->h)) |
+ H_PRESCALED_SIZE(dfixed_trunc(win->w) * Bpp),
+ DC_WIN_PRESCALED_SIZE);
+
+ h_dda = compute_dda_inc(win->w, win->out_w, false, Bpp_bw);
+ v_dda = compute_dda_inc(win->h, win->out_h, true, Bpp_bw);
+ tegra_dc_writel(dc, V_DDA_INC(v_dda) | H_DDA_INC(h_dda),
+ DC_WIN_DDA_INCREMENT);
+ h_dda = compute_initial_dda(win->x);
+ v_dda = compute_initial_dda(win->y);
+ tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
+ tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
+ }
tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
tegra_dc_writel(dc, dfixed_trunc(v_offset),
DC_WINBUF_ADDR_V_OFFSET);
- if (WIN_IS_TILED(win))
- tegra_dc_writel(dc,
- DC_WIN_BUFFER_ADDR_MODE_TILE |
- DC_WIN_BUFFER_ADDR_MODE_TILE_UV,
- DC_WIN_BUFFER_ADDR_MODE);
- else
- tegra_dc_writel(dc,
- DC_WIN_BUFFER_ADDR_MODE_LINEAR |
- DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV,
- DC_WIN_BUFFER_ADDR_MODE);
+ if (tegra_dc_feature_has_tiling(dc, win->idx)) {
+ if (WIN_IS_TILED(win))
+ tegra_dc_writel(dc,
+ DC_WIN_BUFFER_ADDR_MODE_TILE |
+ DC_WIN_BUFFER_ADDR_MODE_TILE_UV,
+ DC_WIN_BUFFER_ADDR_MODE);
+ else
+ tegra_dc_writel(dc,
+ DC_WIN_BUFFER_ADDR_MODE_LINEAR |
+ DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV,
+ DC_WIN_BUFFER_ADDR_MODE);
+ }
val = WIN_ENABLE;
- if (yuvp)
+ if (yuv)
val |= CSC_ENABLE;
else if (tegra_dc_fmt_bpp(win->fmt) < 24)
val |= COLOR_EXPAND;
tegra_dc_writel(dc, val, DC_WIN_WIN_OPTIONS);
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ if (win->global_alpha == 255)
+ tegra_dc_writel(dc, 0, DC_WIN_GLOBAL_ALPHA);
+ else
+ tegra_dc_writel(dc, GLOBAL_ALPHA_ENABLE |
+ win->global_alpha, DC_WIN_GLOBAL_ALPHA);
+#endif
+
win->dirty = no_vsync ? 0 : 1;
dev_dbg(&dc->ndev->dev, "%s():idx=%d z=%d x=%d y=%d w=%d h=%d "
dfixed_trunc(win->w), dfixed_trunc(win->h),
win->out_x, win->out_y, win->out_w, win->out_h,
win->fmt, yuvp, Bpp, filter_h, filter_v);
+ trace_printk("%s:win%u in:%ux%u out:%ux%u fmt=%d\n",
+ dc->ndev->name, win->idx, dfixed_trunc(win->w),
+ dfixed_trunc(win->h), win->out_w, win->out_h, win->fmt);
}
if (update_blend) {
tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS);
if (!no_vsync) {
- val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
val |= (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
- tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+ tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
} else {
- val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
val &= ~(FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
-
- tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+ tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
}
- tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ schedule_delayed_work(&dc->one_shot_work,
+ msecs_to_jiffies(dc->one_shot_delay_ms));
+
+ /* update EMC clock if calculated bandwidth has changed */
+ tegra_dc_program_bandwidth(dc);
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
- tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL);
+ update_mask |= NC_HOST_TRIG;
+
+ tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
+ trace_printk("%s:update_mask=%#lx\n", dc->ndev->name, update_mask);
mutex_unlock(&dc->lock);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ mutex_unlock(&dc->one_shot_lock);
return 0;
}
u32 max;
mutex_lock(&dc->lock);
- max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt,
+ max = nvhost_syncpt_incr_max(&nvhost_get_host(dc->ndev)->syncpt,
dc->syncpt[i].id, ((dc->enabled) ? 1 : 0));
dc->syncpt[i].max = max;
mutex_unlock(&dc->lock);
if ( dc->enabled )
while (dc->syncpt[i].min < val) {
dc->syncpt[i].min++;
- nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt,
+ nvhost_syncpt_cpu_incr(
+ &nvhost_get_host(dc->ndev)->syncpt,
dc->syncpt[i].id);
}
mutex_unlock(&dc->lock);
/* does not support syncing windows on multiple dcs in one call */
int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
{
+ int ret;
if (n < 1 || n > DC_N_WINDOWS)
return -EINVAL;
#ifdef CONFIG_TEGRA_SIMULATION_PLATFORM
/* Don't want to timeout on simulator */
- return wait_event_interruptible(windows[0]->dc->wq,
+ ret = wait_event_interruptible(windows[0]->dc->wq,
tegra_dc_windows_are_clean(windows, n));
#else
- return wait_event_interruptible_timeout(windows[0]->dc->wq,
+ trace_printk("%s:Before wait_event_interruptible_timeout\n",
+ windows[0]->dc->ndev->name);
+ ret = wait_event_interruptible_timeout(windows[0]->dc->wq,
tegra_dc_windows_are_clean(windows, n),
HZ);
+ trace_printk("%s:After wait_event_interruptible_timeout\n",
+ windows[0]->dc->ndev->name);
#endif
+ return ret;
}
EXPORT_SYMBOL(tegra_dc_sync_windows);
return rate * 2 / div;
}
+static unsigned long tegra_dc_pclk_predict_rate(struct clk *parent, int pclk)
+{
+ unsigned long rate;
+ unsigned long div;
+
+ rate = clk_get_rate(parent);
+
+ div = DIV_ROUND_CLOSEST(rate * 2, pclk);
+
+ if (div < 2)
+ return 0;
+
+ return rate * 2 / div;
+}
+
void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
{
int pclk;
struct clk *parent_clk =
clk_get_sys(NULL, dc->out->parent_clk ? : "pll_p");
+ if (dc->out->parent_clk_backup &&
+ (parent_clk == clk_get_sys(NULL, "pll_p"))) {
+ rate = tegra_dc_pclk_predict_rate(
+ parent_clk, dc->mode.pclk);
+ /* use pll_d as last resort */
+ if (rate < (dc->mode.pclk / 100 * 99) ||
+ rate > (dc->mode.pclk / 100 * 109))
+ parent_clk = clk_get_sys(
+ NULL, dc->out->parent_clk_backup);
+ }
+
if (clk_get_parent(clk) != parent_clk)
clk_set_parent(clk, parent_clk);
clk_get_sys(NULL, dc->out->parent_clk ? : "pll_d_out0");
struct clk *base_clk = clk_get_parent(parent_clk);
- /* needs to match tegra_dc_hdmi_supported_modes[]
- and tegra_pll_d_freq_table[] */
- if (dc->mode.pclk > 70000000)
- rate = 594000000;
- else if (dc->mode.pclk > 25200000)
- rate = 216000000;
- else
- rate = 504000000;
+ /*
+ * Providing dynamic frequency rate setting for T20/T30 HDMI.
+ * The required rate needs to be setup at 4x multiplier,
+ * as out0 is 1/2 of the actual PLL output.
+ */
+ rate = dc->mode.pclk * 4;
if (rate != clk_get_rate(base_clk))
clk_set_rate(base_clk, rate);
}
}
- rate = dc->mode.pclk;
+ rate = dc->mode.pclk * dc->shift_clk_div * 2;
if (rate != clk_get_rate(base_clk))
clk_set_rate(base_clk, rate);
#ifdef DEBUG
/* return in 1000ths of a Hertz */
-static int calc_refresh(struct tegra_dc *dc, const struct tegra_dc_mode *m)
+static int calc_refresh(const struct tegra_dc_mode *m)
{
long h_total, v_total, refresh;
h_total = m->h_active + m->h_front_porch + m->h_back_porch +
m->h_sync_width;
v_total = m->v_active + m->v_front_porch + m->v_back_porch +
m->v_sync_width;
- refresh = dc->pixel_clk / h_total;
+ refresh = m->pclk / h_total;
refresh *= 1000;
refresh /= v_total;
return refresh;
tegra_dc_writel(dc, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL,
DC_DISP_DATA_ENABLE_OPTIONS);
- val = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY1);
- if (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC)
- val |= PIN1_LVS_OUTPUT;
- else
- val &= ~PIN1_LVS_OUTPUT;
-
- if (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC)
- val |= PIN1_LHS_OUTPUT;
- else
- val &= ~PIN1_LHS_OUTPUT;
- tegra_dc_writel(dc, val, DC_COM_PIN_OUTPUT_POLARITY1);
-
/* TODO: MIPI/CRT/HDMI clock cals */
val = DISP_DATA_FORMAT_DF1P1C;
rate = tegra_dc_clk_get_rate(dc);
pclk = tegra_dc_pclk_round_rate(dc, mode->pclk);
+ trace_printk("%s:pclk=%ld\n", dc->ndev->name, pclk);
if (pclk < (mode->pclk / 100 * 99) ||
pclk > (mode->pclk / 100 * 109)) {
dev_err(&dc->ndev->dev,
}
div = (rate * 2 / pclk) - 2;
+ trace_printk("%s:div=%ld\n", dc->ndev->name, div);
tegra_dc_writel(dc, 0x00010001,
DC_DISP_SHIFT_CLOCK_OPTIONS);
tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div),
DC_DISP_DISP_CLOCK_CONTROL);
- dc->pixel_clk = dc->mode.pclk;
+#ifdef CONFIG_SWITCH
+ switch_set_state(&dc->modeset_switch,
+ (mode->h_active << 16) | mode->v_active);
+#endif
+ tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ print_mode_info(dc, dc->mode);
return 0;
}
mode.h_ref_to_sync, mode.v_ref_to_sync
);
+#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
+ /* Double the pixel clock and update v_active only for frame packed mode */
if (mode.stereo_mode) {
mode.pclk *= 2;
/* total v_active = yres*2 + activespace */
fbmode->upper_margin +
fbmode->lower_margin;
}
+#endif
mode.flags = 0;
}
EXPORT_SYMBOL(tegra_dc_config_pwm);
-static void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
+void tegra_dc_set_out_pin_polars(struct tegra_dc *dc,
const struct tegra_dc_out_pin *pins,
const unsigned int n_pins)
{
}
}
- pol1 = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY1);
- pol3 = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY3);
+ pol1 = DC_COM_PIN_OUTPUT_POLARITY1_INIT_VAL;
+ pol3 = DC_COM_PIN_OUTPUT_POLARITY3_INIT_VAL;
pol1 |= set1;
pol1 &= ~unset1;
{
int crc = 0;
- if(!dc) {
+ if (!dc) {
dev_err(&dc->ndev->dev, "Failed to get dc.\n");
goto crc_error;
}
mutex_lock(&dc->lock);
- /* update EMC clock if calculated bandwidth has changed */
- tegra_dc_program_bandwidth(dc);
-
/* Update the SD brightness */
if (dc->enabled && dc->out->sd_settings)
nvsd_updated = nvsd_update_brightness(dc);
}
}
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
+/* Must acquire dc lock and dc one-shot lock before invoking this function.
+ * Acquire dc one-shot lock first and then dc lock. */
+void tegra_dc_host_trigger(struct tegra_dc *dc)
+{
+ /* We release the lock here to prevent deadlock between
+ * cancel_delayed_work_sync and one-shot work. */
+ mutex_unlock(&dc->lock);
+
+ cancel_delayed_work_sync(&dc->one_shot_work);
+ mutex_lock(&dc->lock);
+
+ schedule_delayed_work(&dc->one_shot_work,
+ msecs_to_jiffies(dc->one_shot_delay_ms));
+ tegra_dc_program_bandwidth(dc);
+ tegra_dc_writel(dc, NC_HOST_TRIG, DC_CMD_STATE_CONTROL);
+}
+
+static void tegra_dc_one_shot_worker(struct work_struct *work)
+{
+ struct tegra_dc *dc = container_of(
+ to_delayed_work(work), struct tegra_dc, one_shot_work);
+ mutex_lock(&dc->lock);
+ /* memory client has gone idle */
+ tegra_dc_clear_bandwidth(dc);
+ mutex_unlock(&dc->lock);
+}
+
+/* return an arbitrarily large number if count overflow occurs.
+ * make it a nice base-10 number to show up in stats output */
+static u64 tegra_dc_underflow_count(struct tegra_dc *dc, unsigned reg)
+{
+ unsigned count = tegra_dc_readl(dc, reg);
+ tegra_dc_writel(dc, 0, reg);
+ return ((count & 0x80000000) == 0) ? count : 10000000000ll;
+}
+
static void tegra_dc_underflow_handler(struct tegra_dc *dc)
{
- u32 val, i;
+ u32 val;
+ int i;
+
+ dc->stats.underflows++;
+ if (dc->underflow_mask & WIN_A_UF_INT) {
+ dc->stats.underflows_a += tegra_dc_underflow_count(dc,
+ DC_WINBUF_AD_UFLOW_STATUS);
+ trace_printk("%s:Window A Underflow\n", dc->ndev->name);
+ }
+ if (dc->underflow_mask & WIN_B_UF_INT) {
+ dc->stats.underflows_b += tegra_dc_underflow_count(dc,
+ DC_WINBUF_BD_UFLOW_STATUS);
+ trace_printk("%s:Window B Underflow\n", dc->ndev->name);
+ }
+ if (dc->underflow_mask & WIN_C_UF_INT) {
+ dc->stats.underflows_c += tegra_dc_underflow_count(dc,
+ DC_WINBUF_CD_UFLOW_STATUS);
+ trace_printk("%s:Window C Underflow\n", dc->ndev->name);
+ }
/* Check for any underflow reset conditions */
for (i = 0; i < DC_N_WINDOWS; i++) {
dc->windows[i].underflows++;
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- if (dc->windows[i].underflows > 4)
+ if (dc->windows[i].underflows > 4) {
schedule_work(&dc->reset_work);
+ /* reset counter */
+ dc->windows[i].underflows = 0;
+ trace_printk("%s:Reset work scheduled for "
+ "window %c\n",
+ dc->ndev->name, (65 + i));
+ }
#endif
} else {
dc->windows[i].underflows = 0;
}
}
- if (!dc->underflow_mask) {
- /* If we have no underflow to check, go ahead
- and disable the interrupt */
- val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
- if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
- val &= ~FRAME_END_INT;
- else
- val &= ~V_BLANK_INT;
- tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
- }
-
/* Clear the underflow mask now that we've checked it. */
+ tegra_dc_writel(dc, dc->underflow_mask, DC_CMD_INT_STATUS);
dc->underflow_mask = 0;
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+ tegra_dc_writel(dc, val | ALL_UF_INT, DC_CMD_INT_MASK);
+ print_underflow_info(dc);
+}
+
+#ifndef CONFIG_TEGRA_FPGA_PLATFORM
+static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
+{
+#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
+ u32 val;
+
+ val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+ if (val & (WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE))
+ return true;
+#endif
+ return false;
}
static void tegra_dc_trigger_windows(struct tegra_dc *dc)
}
if (!dirty) {
- val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
val &= ~V_BLANK_INT;
else
val &= ~FRAME_END_INT;
- tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
+ tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
}
if (completed) {
if (!dirty) {
/* With the last completed window, go ahead
and enable the vblank interrupt for nvsd. */
- val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
- val |= V_BLANK_INT;
- tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
-
val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
val |= V_BLANK_INT;
tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
/* Schedule any additional bottom-half vblank actvities. */
schedule_work(&dc->vblank_work);
-
- /* Mark the vblank as complete. */
- complete(&dc->vblank_complete);
-
}
- /* Check underflow at frame end */
- if (status & FRAME_END_INT)
- tegra_dc_underflow_handler(dc);
+ if (status & FRAME_END_INT) {
+ /* Mark the frame_end as complete. */
+ if (!completion_done(&dc->frame_end_complete))
+ complete(&dc->frame_end_complete);
+ }
}
static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
{
if (status & V_BLANK_INT) {
- /* Check underflow */
- tegra_dc_underflow_handler(dc);
-
/* Schedule any additional bottom-half vblank actvities. */
schedule_work(&dc->vblank_work);
- /* Mark the vblank as complete. */
- complete(&dc->vblank_complete);
+ /* All windows updated. Mask subsequent V_BLANK interrupts */
+ if (!tegra_dc_windows_are_dirty(dc)) {
+ u32 val;
+
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+ val &= ~V_BLANK_INT;
+ tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
+ }
}
- if (status & FRAME_END_INT)
+ if (status & FRAME_END_INT) {
+ /* Mark the frame_end as complete. */
+ if (!completion_done(&dc->frame_end_complete))
+ complete(&dc->frame_end_complete);
+
tegra_dc_trigger_windows(dc);
+ }
}
#endif
-/* return an arbitrarily large number if count overflow occurs.
- * make it a nice base-10 number to show up in stats output */
-static u64 tegra_dc_underflow_count(struct tegra_dc *dc, unsigned reg)
-{
- unsigned count = tegra_dc_readl(dc, reg);
- tegra_dc_writel(dc, 0, reg);
- return ((count & 0x80000000) == 0) ? count : 10000000000ll;
-}
-
static irqreturn_t tegra_dc_irq(int irq, void *ptr)
{
#ifndef CONFIG_TEGRA_FPGA_PLATFORM
struct tegra_dc *dc = ptr;
unsigned long status;
- unsigned long val;
unsigned long underflow_mask;
+ u32 val;
+
+ if (!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)) {
+ WARN(1, "IRQ when DC not powered!\n");
+ tegra_dc_io_start(dc);
+ status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
+ tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
+ tegra_dc_io_end(dc);
+ return IRQ_HANDLED;
+ }
+ /* clear all status flags except underflow, save those for the worker */
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
- tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
+ tegra_dc_writel(dc, status & ~ALL_UF_INT, DC_CMD_INT_STATUS);
+ val = tegra_dc_readl(dc, DC_CMD_INT_MASK);
+ tegra_dc_writel(dc, val & ~ALL_UF_INT, DC_CMD_INT_MASK);
/*
* Overlays can get thier internal state corrupted during and underflow
*/
underflow_mask = status & ALL_UF_INT;
+ /* Check underflow */
if (underflow_mask) {
- val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
- val |= V_BLANK_INT;
- tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
dc->underflow_mask |= underflow_mask;
- dc->stats.underflows++;
- if (status & WIN_A_UF_INT)
- dc->stats.underflows_a += tegra_dc_underflow_count(dc,
- DC_WINBUF_AD_UFLOW_STATUS);
- if (status & WIN_B_UF_INT)
- dc->stats.underflows_b += tegra_dc_underflow_count(dc,
- DC_WINBUF_BD_UFLOW_STATUS);
- if (status & WIN_C_UF_INT)
- dc->stats.underflows_c += tegra_dc_underflow_count(dc,
- DC_WINBUF_CD_UFLOW_STATUS);
+ schedule_delayed_work(&dc->underflow_work,
+ msecs_to_jiffies(1));
}
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
return syncpt_id;
}
-static void tegra_dc_init(struct tegra_dc *dc)
+static int tegra_dc_init(struct tegra_dc *dc)
{
int i;
tegra_dc_writel(dc, 0x00202020, DC_DISP_MEM_HIGH_PRIORITY);
tegra_dc_writel(dc, 0x00010101, DC_DISP_MEM_HIGH_PRIORITY_TIMER);
- tegra_dc_writel(dc, (FRAME_END_INT |
- V_BLANK_INT |
- ALL_UF_INT), DC_CMD_INT_MASK);
- tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_ENABLE);
+ /* enable interrupts for vblank, frame_end and underflows */
+ tegra_dc_writel(dc, (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT),
+ DC_CMD_INT_ENABLE);
+ tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_MASK);
tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
dc->syncpt[i].id = syncpt;
dc->syncpt[i].min = dc->syncpt[i].max =
- nvhost_syncpt_read(&dc->ndev->host->syncpt, syncpt);
+ nvhost_syncpt_read(&nvhost_get_host(dc->ndev)->syncpt,
+ syncpt);
}
print_mode(dc, &dc->mode, __func__);
if (dc->mode.pclk)
- tegra_dc_program_mode(dc, &dc->mode);
+ if (tegra_dc_program_mode(dc, &dc->mode))
+ return -EINVAL;
/* Initialize SD AFTER the modeset.
nvsd_init handles the sd_settings = NULL case. */
nvsd_init(dc, dc->out->sd_settings);
+
+ return 0;
}
static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
{
+ int failed_init = 0;
+
if (dc->out->enable)
dc->out->enable();
tegra_dc_setup_clk(dc, dc->clk);
- tegra_periph_reset_assert(dc->clk);
clk_enable(dc->clk);
- clk_enable(dc->emc_clk);
/* do not accept interrupts during initialization */
tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
enable_dc_irq(dc->irq);
- tegra_dc_init(dc);
+ failed_init = tegra_dc_init(dc);
+ if (failed_init) {
+ _tegra_dc_controller_disable(dc);
+ return false;
+ }
if (dc->out_ops && dc->out_ops->enable)
dc->out_ops->enable(dc);
- if (dc->out->out_pins)
- tegra_dc_set_out_pin_polars(dc, dc->out->out_pins,
- dc->out->n_out_pins);
-
if (dc->out->postpoweron)
dc->out->postpoweron();
tegra_dc_ext_enable(dc->ext);
+ trace_printk("%s:enable\n", dc->ndev->name);
return true;
}
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
{
+ bool ret = true;
+
if (dc->out->enable)
dc->out->enable();
tegra_dc_setup_clk(dc, dc->clk);
clk_enable(dc->clk);
- clk_enable(dc->emc_clk);
if (dc->ndev->id == 0 && tegra_dcs[1] != NULL) {
mutex_lock(&tegra_dcs[1]->lock);
enable_dc_irq(dc->irq);
- tegra_dc_init(dc);
+ if (tegra_dc_init(dc)) {
+ dev_err(&dc->ndev->dev, "cannot initialize\n");
+ ret = false;
+ }
if (dc->out_ops && dc->out_ops->enable)
dc->out_ops->enable(dc);
- if (dc->out->out_pins)
- tegra_dc_set_out_pin_polars(dc, dc->out->out_pins,
- dc->out->n_out_pins);
-
if (dc->out->postpoweron)
dc->out->postpoweron();
/* force a full blending update */
dc->blend.z[0] = -1;
- return true;
+ tegra_dc_ext_enable(dc->ext);
+
+ if (!ret) {
+ dev_err(&dc->ndev->dev, "initialization failed,disabling");
+ _tegra_dc_controller_disable(dc);
+ }
+
+ trace_printk("%s:reset enable\n", dc->ndev->name);
+ return ret;
}
#endif
+static int _tegra_dc_set_default_videomode(struct tegra_dc *dc)
+{
+ return tegra_dc_set_fb_mode(dc, &tegra_dc_hdmi_fallback_mode, 0);
+}
+
static bool _tegra_dc_enable(struct tegra_dc *dc)
{
- if (dc->mode.pclk == 0)
- return false;
+ if (dc->mode.pclk == 0) {
+ switch (dc->out->type) {
+ case TEGRA_DC_OUT_HDMI:
+ /* DC enable called but no videomode is loaded.
+ Check if HDMI is connected, then set fallback mdoe */
+ if (tegra_dc_hpd(dc)) {
+ if (_tegra_dc_set_default_videomode(dc))
+ return false;
+ } else
+ return false;
+
+ break;
+
+ /* Do nothing for other outputs for now */
+ case TEGRA_DC_OUT_RGB:
+
+ case TEGRA_DC_OUT_DSI:
+
+ default:
+ return false;
+ }
+ }
if (!dc->out)
return false;
dc->enabled = _tegra_dc_enable(dc);
mutex_unlock(&dc->lock);
+ print_mode_info(dc, dc->mode);
}
static void _tegra_dc_controller_disable(struct tegra_dc *dc)
{
unsigned i;
- disable_irq(dc->irq);
-
if (dc->out_ops && dc->out_ops->disable)
dc->out_ops->disable(dc);
- clk_disable(dc->emc_clk);
+ tegra_dc_writel(dc, 0, DC_CMD_INT_MASK);
+ tegra_dc_writel(dc, 0, DC_CMD_INT_ENABLE);
+ disable_irq(dc->irq);
+
+ tegra_dc_clear_bandwidth(dc);
clk_disable(dc->clk);
tegra_dvfs_set_rate(dc->clk, 0);
/* flush any pending syncpt waits */
while (dc->syncpt[i].min < dc->syncpt[i].max) {
+ trace_printk("%s:syncpt flush id=%d\n", dc->ndev->name,
+ dc->syncpt[i].id);
dc->syncpt[i].min++;
- nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt,
+ nvhost_syncpt_cpu_incr(
+ &nvhost_get_host(dc->ndev)->syncpt,
dc->syncpt[i].id);
}
}
+ trace_printk("%s:disabled\n", dc->ndev->name);
}
void tegra_dc_stats_enable(struct tegra_dc *dc, bool enable)
void tegra_dc_disable(struct tegra_dc *dc)
{
- if (dc->overlay)
- tegra_overlay_disable(dc->overlay);
-
tegra_dc_ext_disable(dc->ext);
+ /* it's important that new underflow work isn't scheduled before the
+ * lock is acquired. */
+ cancel_delayed_work_sync(&dc->underflow_work);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
+ mutex_lock(&dc->one_shot_lock);
+ cancel_delayed_work_sync(&dc->one_shot_work);
+ }
+
mutex_lock(&dc->lock);
if (dc->enabled) {
_tegra_dc_disable(dc);
}
+#ifdef CONFIG_SWITCH
+ switch_set_state(&dc->modeset_switch, 0);
+#endif
+
mutex_unlock(&dc->lock);
+ if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+ mutex_unlock(&dc->one_shot_lock);
+ print_mode_info(dc, dc->mode);
}
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
unsigned long val = 0;
+ mutex_lock(&shared_lock);
+
dev_warn(&dc->ndev->dev, "overlay stuck in underflow state. resetting.\n");
tegra_dc_ext_disable(dc->ext);
- mutex_lock(&shared_lock);
mutex_lock(&dc->lock);
if (dc->enabled == false)
_tegra_dc_controller_reset_enable(dc);
dc->enabled = true;
+
+ /* reopen host read bus */
+ val = tegra_dc_readl(dc, DC_CMD_CONT_SYNCPT_VSYNC);
+ val &= ~(0x00000100);
+ val |= 0x100;
+ tegra_dc_writel(dc, val, DC_CMD_CONT_SYNCPT_VSYNC);
+
unlock:
mutex_unlock(&dc->lock);
mutex_unlock(&shared_lock);
+ trace_printk("%s:reset complete\n", dc->ndev->name);
}
#endif
+static void tegra_dc_underflow_worker(struct work_struct *work)
+{
+ struct tegra_dc *dc = container_of(
+ to_delayed_work(work), struct tegra_dc, underflow_work);
-static int tegra_dc_probe(struct nvhost_device *ndev)
+ mutex_lock(&dc->lock);
+ if (dc->enabled) {
+ tegra_dc_underflow_handler(dc);
+ }
+ mutex_unlock(&dc->lock);
+}
+
+#ifdef CONFIG_SWITCH
+static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
+{
+ struct tegra_dc *dc =
+ container_of(sdev, struct tegra_dc, modeset_switch);
+
+ if (!sdev->state)
+ return sprintf(buf, "offline\n");
+
+ return sprintf(buf, "%dx%d\n", dc->mode.h_active, dc->mode.v_active);
+}
+#endif
+
+static int tegra_dc_probe(struct nvhost_device *ndev,
+ struct nvhost_device_id *id_table)
{
struct tegra_dc *dc;
struct clk *clk;
dc->clk = clk;
dc->emc_clk = emc_clk;
+ dc->shift_clk_div = 1;
+ /* Initialize one shot work delay, it will be assigned by dsi
+ * according to refresh rate later. */
+ dc->one_shot_delay_ms = 40;
dc->base_res = base_res;
dc->base = base;
* The emc is a shared clock, it will be set based on
* the requirements for each user on the bus.
*/
- dc->emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc);
- clk_set_rate(emc_clk, dc->emc_clk_rate);
-
- if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
- dc->enabled = true;
+ dc->emc_clk_rate = 0;
mutex_init(&dc->lock);
- init_completion(&dc->vblank_complete);
+ mutex_init(&dc->one_shot_lock);
+ init_completion(&dc->frame_end_complete);
init_waitqueue_head(&dc->wq);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
INIT_WORK(&dc->reset_work, tegra_dc_reset_worker);
#endif
INIT_WORK(&dc->vblank_work, tegra_dc_vblank);
+ INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker);
+ INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker);
tegra_dc_init_lut_defaults(&dc->fb_lut);
nvhost_set_drvdata(ndev, dc);
+#ifdef CONFIG_SWITCH
+ dc->modeset_switch.name = dev_name(&ndev->dev);
+ dc->modeset_switch.state = 0;
+ dc->modeset_switch.print_state = switch_modeset_print_mode;
+ switch_dev_register(&dc->modeset_switch);
+#endif
+
+ tegra_dc_feature_register(dc);
+
if (dc->pdata->default_out)
tegra_dc_set_out(dc, dc->pdata->default_out);
else
dc->ext = NULL;
}
+ mutex_lock(&dc->lock);
+ if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
+ dc->enabled = _tegra_dc_enable(dc);
+ mutex_unlock(&dc->lock);
+
/* interrupt handler must be registered before tegra_fb_register() */
- if (request_irq(irq, tegra_dc_irq, IRQF_DISABLED,
+ if (request_irq(irq, tegra_dc_irq, 0,
dev_name(&ndev->dev), dc)) {
dev_err(&ndev->dev, "request_irq %d failed\n", irq);
ret = -EBUSY;
goto err_put_emc_clk;
}
- /* hack to balance enable_irq calls in _tegra_dc_enable() */
- disable_dc_irq(dc->irq);
-
- mutex_lock(&dc->lock);
- if (dc->enabled)
- _tegra_dc_enable(dc);
- mutex_unlock(&dc->lock);
-
tegra_dc_create_debugfs(dc);
dev_info(&ndev->dev, "probed\n");
dc->fb = NULL;
}
- if (dc->fb) {
- dc->overlay = tegra_overlay_register(ndev, dc);
- if (IS_ERR_OR_NULL(dc->overlay))
- dc->overlay = NULL;
- }
-
if (dc->out && dc->out->hotplug_init)
dc->out->hotplug_init();
tegra_dc_remove_sysfs(&dc->ndev->dev);
tegra_dc_remove_debugfs(dc);
- if (dc->overlay) {
- tegra_overlay_unregister(dc->overlay);
- }
-
if (dc->fb) {
tegra_fb_unregister(dc->fb);
if (dc->fb_mem)
if (dc->enabled)
_tegra_dc_disable(dc);
+#ifdef CONFIG_SWITCH
+ switch_dev_unregister(&dc->modeset_switch);
+#endif
free_irq(dc->irq, dc);
clk_put(dc->emc_clk);
clk_put(dc->clk);
{
struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ trace_printk("%s:suspend\n", dc->ndev->name);
dev_info(&ndev->dev, "suspend\n");
- if (dc->overlay)
- tegra_overlay_disable(dc->overlay);
-
tegra_dc_ext_disable(dc->ext);
mutex_lock(&dc->lock);
if (dc->out && dc->out->postsuspend) {
dc->out->postsuspend();
- msleep(100); /* avoid resume event due to voltage falling */
+ if (dc->out->type && dc->out->type == TEGRA_DC_OUT_HDMI)
+ /*
+ * avoid resume event due to voltage falling
+ */
+ msleep(100);
}
mutex_unlock(&dc->lock);
{
struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ trace_printk("%s:resume\n", dc->ndev->name);
dev_info(&ndev->dev, "resume\n");
mutex_lock(&dc->lock);