summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/dc
diff options
context:
space:
mode:
authorTom Cherry <tcherry@nvidia.com>2012-05-23 12:06:13 -0700
committerTom Cherry <tcherry@nvidia.com>2012-05-23 12:06:13 -0700
commita168c03bd97fd9761218779623db0cec09fa8f4a (patch)
tree521d2b51904da963d771c24fd9b142cc416f8259 /drivers/video/tegra/dc
parent11fb7d0e35d56230919eb91bee1aa138a10b8416 (diff)
parentc7e3189c1802c2a6552eec960f521a1891529892 (diff)
Merge commit 'main-ics-2012.05.22-B3' into HEAD
Conflicts: arch/arm/mach-tegra/pm.c drivers/media/video/tegra/nvavp/nvavp_dev.c drivers/power/smb349-charger.c include/linux/smb349-charger.h include/trace/events/power.h Change-Id: Ia8c82e2acfe3463ae6778bdd03aac8da104f7ad3
Diffstat (limited to 'drivers/video/tegra/dc')
-rw-r--r--drivers/video/tegra/dc/Makefile1
-rw-r--r--drivers/video/tegra/dc/dc.c374
-rw-r--r--drivers/video/tegra/dc/dc_config.c246
-rw-r--r--drivers/video/tegra/dc/dc_config.h148
-rw-r--r--drivers/video/tegra/dc/dc_priv.h19
-rw-r--r--drivers/video/tegra/dc/dc_reg.h6
-rw-r--r--drivers/video/tegra/dc/dsi.c128
-rw-r--r--drivers/video/tegra/dc/ext/Makefile1
-rw-r--r--drivers/video/tegra/dc/ext/dev.c41
-rw-r--r--drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h2
-rw-r--r--drivers/video/tegra/dc/ext/util.c2
-rw-r--r--drivers/video/tegra/dc/hdmi.c182
-rw-r--r--drivers/video/tegra/dc/rgb.c8
13 files changed, 936 insertions, 222 deletions
diff --git a/drivers/video/tegra/dc/Makefile b/drivers/video/tegra/dc/Makefile
index 01f13918ca63..ebe9e890fad6 100644
--- a/drivers/video/tegra/dc/Makefile
+++ b/drivers/video/tegra/dc/Makefile
@@ -7,4 +7,5 @@ obj-y += edid.o
obj-y += nvsd.o
obj-y += dsi.o
obj-y += dc_sysfs.o
+obj-y += dc_config.o
obj-$(CONFIG_TEGRA_DC_EXTENSIONS) += ext/
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index 5203fbba9efc..55d3fccf56b2 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -23,7 +23,6 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/mutex.h>
@@ -34,6 +33,7 @@
#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
@@ -49,6 +49,7 @@
#include <mach/latency_allowance.h>
#include "dc_reg.h"
+#include "dc_config.h"
#include "dc_priv.h"
#include "nvsd.h"
@@ -64,9 +65,21 @@
#define ALL_UF_INT (0)
#endif
-static int calc_refresh(const struct tegra_dc_mode *m);
-
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);
@@ -81,25 +94,14 @@ struct tegra_dc *tegra_dcs[TEGRA_MAX_DC];
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);
}
@@ -141,14 +143,32 @@ static inline int tegra_dc_fmt_bpp(int fmt)
case TEGRA_WIN_FMT_YUV422RA:
return 8;
+ /* YUYV packed into 32-bits */
case TEGRA_WIN_FMT_YCbCr422:
case TEGRA_WIN_FMT_YUV422:
- /* FIXME: need to know the bpp of these formats */
- return 0;
+ 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:
+ return true;
+ }
+ return false;
+}
+
static inline bool tegra_dc_is_yuv_planar(int fmt)
{
switch (fmt) {
@@ -171,6 +191,34 @@ static inline bool tegra_dc_is_yuv_planar(int fmt)
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))
{
@@ -571,6 +619,20 @@ bool tegra_dc_get_connected(struct tegra_dc *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)
@@ -767,6 +829,8 @@ static int tegra_dc_update_winlut(struct tegra_dc *dc, int win_idx, int fbovr)
mutex_unlock(&dc->lock);
+ tegra_dc_update_windows(&win, 1);
+
return 0;
}
@@ -828,7 +892,7 @@ static void tegra_dc_set_latency_allowance(struct tegra_dc *dc,
/* 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 kbytes/sec, but LA takes MBps.
@@ -946,14 +1010,9 @@ static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
* 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);
- ret = dc->mode.pclk / 1000UL * bpp / 8 * (win_use_v_filter(w) ? 2 : 1)
+ 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 ~35% efficiency: i.e. if we calculate we need 70MBps, we
- * will request 200MBps from EMC.
- */
- ret = ret * 29 / 10;
return ret;
}
@@ -981,6 +1040,8 @@ static unsigned long tegra_dc_get_bandwidth(
/* 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;
@@ -1007,6 +1068,8 @@ static void tegra_dc_program_bandwidth(struct tegra_dc *dc)
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);
}
}
@@ -1030,6 +1093,7 @@ static int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n)
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;
@@ -1114,14 +1178,6 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
else
tegra_dc_writel(dc, WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, DC_CMD_STATE_ACCESS);
- for (i = 0; i < DC_N_WINDOWS; i++) {
- tegra_dc_writel(dc, WINDOW_A_SELECT << i,
- DC_CMD_DISPLAY_WINDOW_HEADER);
- tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS);
- if (!no_vsync)
- update_mask |= WIN_A_ACT_REQ << i;
- }
-
for (i = 0; i < n; i++) {
struct tegra_dc_win *win = windows[i];
unsigned h_dda;
@@ -1129,12 +1185,13 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
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;
@@ -1158,8 +1215,8 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
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),
@@ -1167,19 +1224,22 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
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);
@@ -1217,19 +1277,21 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
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;
@@ -1249,6 +1311,14 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
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 "
@@ -1259,6 +1329,9 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
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) {
@@ -1296,6 +1369,7 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
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)
@@ -1353,6 +1427,7 @@ static bool tegra_dc_windows_are_clean(struct tegra_dc_win *windows[],
/* 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;
@@ -1361,13 +1436,18 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
#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);
@@ -1614,6 +1694,7 @@ static bool check_ref_to_sync(struct tegra_dc_mode *mode)
return true;
}
+#ifdef DEBUG
/* return in 1000ths of a Hertz */
static int calc_refresh(const struct tegra_dc_mode *m)
{
@@ -1628,7 +1709,6 @@ static int calc_refresh(const struct tegra_dc_mode *m)
return refresh;
}
-#ifdef DEBUG
static void print_mode(struct tegra_dc *dc,
const struct tegra_dc_mode *mode, const char *note)
{
@@ -1708,6 +1788,7 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
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,
@@ -1719,6 +1800,7 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
}
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);
@@ -1733,18 +1815,14 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
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;
}
int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dc->mode_lock, flags);
memcpy(&dc->mode, mode, sizeof(dc->mode));
- dc->mode_dirty = true;
- spin_unlock_irqrestore(&dc->mode_lock, flags);
print_mode(dc, mode, __func__);
@@ -1752,46 +1830,6 @@ int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
}
EXPORT_SYMBOL(tegra_dc_set_mode);
-/* convert tegra_dc_mode to fb_videomode
- * return non-zero on error. */
-int tegra_dc_to_fb_videomode(struct fb_videomode *fbmode,
- const struct tegra_dc_mode *mode)
-{
- if (!fbmode || !mode || !mode->pclk)
- return -EINVAL;
-
- memset(fbmode, 0, sizeof(*fbmode));
-
- if (mode->rated_pclk >= 1000)
- fbmode->pixclock = KHZ2PICOS(mode->rated_pclk / 1000);
- else if (mode->pclk >= 1000)
- fbmode->pixclock = KHZ2PICOS(mode->pclk / 1000);
- fbmode->hsync_len = mode->h_sync_width;
- fbmode->vsync_len = mode->v_sync_width;
- fbmode->left_margin = mode->h_back_porch;
- fbmode->upper_margin = mode->v_back_porch;
- fbmode->xres = mode->h_active;
- fbmode->yres = mode->v_active;
- fbmode->right_margin = mode->h_front_porch;
- fbmode->lower_margin = mode->v_front_porch;
- fbmode->vmode = FB_VMODE_NONINTERLACED;
- fbmode->vmode |= mode->stereo_mode ?
-#ifndef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
- FB_VMODE_STEREO_FRAME_PACK
-#else
- FB_VMODE_STEREO_LEFT_RIGHT
-#endif
- : 0;
- fbmode->sync |= (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) ?
- 0 : FB_SYNC_HOR_HIGH_ACT;
- fbmode->sync |= (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) ?
- 0 : FB_SYNC_VERT_HIGH_ACT;
- fbmode->refresh = (calc_refresh(mode) + 500) / 1000;
-
- return 0;
-}
-EXPORT_SYMBOL(tegra_dc_to_fb_videomode);
-
int tegra_dc_set_fb_mode(struct tegra_dc *dc,
const struct fb_videomode *fbmode, bool stereo_mode)
{
@@ -2053,7 +2091,7 @@ u32 tegra_dc_read_checksum_latched(struct tegra_dc *dc)
{
int crc = 0;
- if(!dc) {
+ if (!dc) {
dev_err(&dc->ndev->dev, "Failed to get dc.\n");
goto crc_error;
}
@@ -2133,15 +2171,21 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
int i;
dc->stats.underflows++;
- if (dc->underflow_mask & WIN_A_UF_INT)
+ if (dc->underflow_mask & WIN_A_UF_INT) {
dc->stats.underflows_a += tegra_dc_underflow_count(dc,
DC_WINBUF_AD_UFLOW_STATUS);
- if (dc->underflow_mask & WIN_B_UF_INT)
+ 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);
- if (dc->underflow_mask & WIN_C_UF_INT)
+ 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++) {
@@ -2153,6 +2197,9 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
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
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
@@ -2185,6 +2232,7 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
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
@@ -2200,18 +2248,6 @@ static bool tegra_dc_windows_are_dirty(struct tegra_dc *dc)
return false;
}
-static void tegra_dc_update_mode(struct tegra_dc *dc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dc->mode_lock, flags);
- if (dc->mode_dirty) {
- tegra_dc_program_mode(dc, &dc->mode);
- dc->mode_dirty = false;
- }
- spin_unlock_irqrestore(&dc->mode_lock, flags);
-}
-
static void tegra_dc_trigger_windows(struct tegra_dc *dc)
{
u32 val, i;
@@ -2271,9 +2307,6 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
/* Mark the frame_end as complete. */
if (!completion_done(&dc->frame_end_complete))
complete(&dc->frame_end_complete);
-
- /* handle a mode change, if it was scheduled */
- tegra_dc_update_mode(dc);
}
}
@@ -2298,8 +2331,6 @@ static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
if (!completion_done(&dc->frame_end_complete))
complete(&dc->frame_end_complete);
- /* handle a mode change, if it was scheduled */
- tegra_dc_update_mode(dc);
tegra_dc_trigger_windows(dc);
}
}
@@ -2573,6 +2604,7 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
tegra_dc_ext_enable(dc->ext);
+ trace_printk("%s:enable\n", dc->ndev->name);
return true;
}
@@ -2634,14 +2666,40 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc)
_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;
@@ -2659,6 +2717,7 @@ void tegra_dc_enable(struct tegra_dc *dc)
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)
@@ -2691,12 +2750,15 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
/* 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(
&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)
@@ -2781,6 +2843,7 @@ void tegra_dc_disable(struct tegra_dc *dc)
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
@@ -2834,6 +2897,7 @@ static void tegra_dc_reset_worker(struct work_struct *work)
unlock:
mutex_unlock(&dc->lock);
mutex_unlock(&shared_lock);
+ trace_printk("%s:reset complete\n", dc->ndev->name);
}
#endif
@@ -2862,7 +2926,8 @@ static ssize_t switch_modeset_print_mode(struct switch_dev *sdev, char *buf)
}
#endif
-static int tegra_dc_probe(struct nvhost_device *ndev)
+static int tegra_dc_probe(struct nvhost_device *ndev,
+ struct nvhost_device_id *id_table)
{
struct tegra_dc *dc;
struct clk *clk;
@@ -2949,12 +3014,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
*/
dc->emc_clk_rate = 0;
- if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
- dc->enabled = true;
-
mutex_init(&dc->lock);
mutex_init(&dc->one_shot_lock);
- spin_lock_init(&dc->mode_lock);
init_completion(&dc->frame_end_complete);
init_waitqueue_head(&dc->wq);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -2990,6 +3051,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
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
@@ -3004,22 +3067,19 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
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");
@@ -3041,22 +3101,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
dc->fb = NULL;
}
- if (dc->out && dc->out->n_modes) {
- struct fb_monspecs specs;
- int i;
-
- memset(&specs, 0, sizeof(specs));
- specs.max_x = dc->mode.h_active * 1000;
- specs.max_y = dc->mode.v_active * 1000;
- specs.modedb_len = dc->out->n_modes;
- specs.modedb = kzalloc(specs.modedb_len *
- sizeof(struct fb_videomode), GFP_KERNEL);
- for (i = 0; i < dc->out->n_modes; i++)
- tegra_dc_to_fb_videomode(&specs.modedb[i],
- &dc->out->modes[i]);
- tegra_fb_update_monspecs(dc->fb, &specs, NULL);
- }
-
if (dc->out && dc->out->hotplug_init)
dc->out->hotplug_init();
@@ -3127,6 +3171,7 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
{
struct tegra_dc *dc = nvhost_get_drvdata(ndev);
+ trace_printk("%s:suspend\n", dc->ndev->name);
dev_info(&ndev->dev, "suspend\n");
tegra_dc_ext_disable(dc->ext);
@@ -3160,6 +3205,7 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
{
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);
diff --git a/drivers/video/tegra/dc/dc_config.c b/drivers/video/tegra/dc/dc_config.c
new file mode 100644
index 000000000000..a40712f5dca1
--- /dev/null
+++ b/drivers/video/tegra/dc/dc_config.c
@@ -0,0 +1,246 @@
+/*
+ * drivers/video/tegra/dc/dc_config.c
+ * * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "dc_config.h"
+
+static struct tegra_dc_feature_entry t20_feature_entries_a[] = {
+ { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} },
+ { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} },
+ { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} },
+ { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} },
+ { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} },
+ { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+};
+
+static struct tegra_dc_feature_entry t20_feature_entries_b[] = {
+ { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} },
+ { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} },
+ { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} },
+ { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} },
+ { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} },
+ { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+};
+
+struct tegra_dc_feature t20_feature_table_a = {
+ ARRAY_SIZE(t20_feature_entries_a), t20_feature_entries_a,
+};
+
+struct tegra_dc_feature t20_feature_table_b = {
+ ARRAY_SIZE(t20_feature_entries_b), t20_feature_entries_b,
+};
+
+static struct tegra_dc_feature_entry t30_feature_entries_a[] = {
+ { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} },
+ { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} },
+ { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} },
+ { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} },
+ { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} },
+ { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} },
+ { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} },
+ { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1} },
+ { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+};
+
+static struct tegra_dc_feature_entry t30_feature_entries_b[] = {
+ { 0, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_A,} },
+ { 0, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 0, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 0, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 0,} },
+ { 0, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 0, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 1, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_PREFERRED_FORMATS, {TEGRA_WIN_PREF_FMT_WIN_B,} },
+ { 1, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 1, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 1, TEGRA_DC_FEATURE_FILTER_TYPE, {1, 1,} },
+ { 1, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 1, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+
+ { 2, TEGRA_DC_FEATURE_FORMATS, {TEGRA_WIN_FMT_WIN_C,} },
+ { 2, TEGRA_DC_FEATURE_BLEND_TYPE, {1,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SIZE, {4095, 16, 4095, 16,} },
+ { 2, TEGRA_DC_FEATURE_MAXIMUM_SCALE, {2, 2, 2, 2,} },
+ { 2, TEGRA_DC_FEATURE_FILTER_TYPE, {0, 1,} },
+ { 2, TEGRA_DC_FEATURE_LAYOUT_TYPE, {1, 1,} },
+ { 2, TEGRA_DC_FEATURE_INVERT_TYPE, {1, 1, 0,} },
+};
+
+struct tegra_dc_feature t30_feature_table_a = {
+ ARRAY_SIZE(t30_feature_entries_a), t30_feature_entries_a,
+};
+
+struct tegra_dc_feature t30_feature_table_b = {
+ ARRAY_SIZE(t30_feature_entries_b), t30_feature_entries_b,
+};
+
+int tegra_dc_get_feature(struct tegra_dc_feature *feature, int win_idx,
+ enum tegra_dc_feature_option option)
+{
+ int i;
+ struct tegra_dc_feature_entry *entry;
+
+ if (!feature)
+ return -EINVAL;
+
+ for (i = 0; i < feature->num_entries; i++) {
+ entry = &feature->entries[i];
+ if (entry->window_index == win_idx && entry->option == option)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+long *tegra_dc_parse_feature(struct tegra_dc *dc, int win_idx, int operation)
+{
+ int idx;
+ struct tegra_dc_feature_entry *entry;
+ enum tegra_dc_feature_option option;
+ struct tegra_dc_feature *feature = dc->feature;
+
+ switch (operation) {
+ case GET_WIN_FORMATS:
+ option = TEGRA_DC_FEATURE_FORMATS;
+ break;
+ case GET_WIN_SIZE:
+ option = TEGRA_DC_FEATURE_MAXIMUM_SIZE;
+ break;
+ case HAS_SCALE:
+ option = TEGRA_DC_FEATURE_MAXIMUM_SCALE;
+ break;
+ case HAS_TILED:
+ option = TEGRA_DC_FEATURE_LAYOUT_TYPE;
+ break;
+ case HAS_V_FILTER:
+ option = TEGRA_DC_FEATURE_FILTER_TYPE;
+ break;
+ case HAS_H_FILTER:
+ option = TEGRA_DC_FEATURE_FILTER_TYPE;
+ break;
+ case HAS_GEN2_BLEND:
+ option = TEGRA_DC_FEATURE_BLEND_TYPE;
+ break;
+ default:
+ return NULL;
+ }
+
+ idx = tegra_dc_get_feature(feature, win_idx, option);
+ if (IS_ERR_VALUE(idx))
+ return NULL;
+ entry = &feature->entries[idx];
+
+ return entry->arg;
+}
+
+int tegra_dc_feature_has_scaling(struct tegra_dc *dc, int win_idx)
+{
+ int i;
+ long *addr = tegra_dc_parse_feature(dc, win_idx, HAS_SCALE);
+
+ for (i = 0; i < ENTRY_SIZE; i++)
+ if (addr[i] != 1)
+ return 1;
+ return 0;
+}
+
+int tegra_dc_feature_has_tiling(struct tegra_dc *dc, int win_idx)
+{
+ long *addr = tegra_dc_parse_feature(dc, win_idx, HAS_TILED);
+
+ return addr[TILED_LAYOUT];
+}
+
+int tegra_dc_feature_has_filter(struct tegra_dc *dc, int win_idx, int operation)
+{
+ long *addr = tegra_dc_parse_feature(dc, win_idx, operation);
+
+ if (operation == HAS_V_FILTER)
+ return addr[V_FILTER];
+ else
+ return addr[H_FILTER];
+}
+
+void tegra_dc_feature_register(struct tegra_dc *dc)
+{
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ if (!dc->ndev->id)
+ dc->feature = &t20_feature_table_a;
+ else
+ dc->feature = &t20_feature_table_b;
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ if (!dc->ndev->id)
+ dc->feature = &t30_feature_table_a;
+ else
+ dc->feature = &t30_feature_table_b;
+#endif
+}
diff --git a/drivers/video/tegra/dc/dc_config.h b/drivers/video/tegra/dc/dc_config.h
new file mode 100644
index 000000000000..f513cd06dc45
--- /dev/null
+++ b/drivers/video/tegra/dc/dc_config.h
@@ -0,0 +1,148 @@
+/*
+ * drivers/video/tegra/dc/dc_config.c
+ * * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_CONFIG_H
+#define __DRIVERS_VIDEO_TEGRA_DC_DC_CONFIG_H
+
+#include <linux/errno.h>
+#include <mach/dc.h>
+
+#include "dc_priv.h"
+
+#define ENTRY_SIZE 4 /* Size of feature entry args */
+
+/* Define the supported formats. TEGRA_WIN_FMT_WIN_x macros are defined
+ * based on T20/T30 formats. */
+#define TEGRA_WIN_FMT_BASE_CNT (TEGRA_WIN_FMT_YUV422RA + 1)
+#define TEGRA_WIN_FMT_BASE ((1 << TEGRA_WIN_FMT_P8) | \
+ (1 << TEGRA_WIN_FMT_B4G4R4A4) | \
+ (1 << TEGRA_WIN_FMT_B5G5R5A) | \
+ (1 << TEGRA_WIN_FMT_B5G6R5) | \
+ (1 << TEGRA_WIN_FMT_AB5G5R5) | \
+ (1 << TEGRA_WIN_FMT_B8G8R8A8) | \
+ (1 << TEGRA_WIN_FMT_R8G8B8A8) | \
+ (1 << TEGRA_WIN_FMT_YCbCr422) | \
+ (1 << TEGRA_WIN_FMT_YUV422) | \
+ (1 << TEGRA_WIN_FMT_YCbCr420P) | \
+ (1 << TEGRA_WIN_FMT_YUV420P) | \
+ (1 << TEGRA_WIN_FMT_YCbCr422P) | \
+ (1 << TEGRA_WIN_FMT_YUV422P) | \
+ (1 << TEGRA_WIN_FMT_YCbCr422R) | \
+ (1 << TEGRA_WIN_FMT_YUV422R))
+
+#define TEGRA_WIN_FMT_WIN_A ((1 << TEGRA_WIN_FMT_P1) | \
+ (1 << TEGRA_WIN_FMT_P2) | \
+ (1 << TEGRA_WIN_FMT_P4) | \
+ (1 << TEGRA_WIN_FMT_P8) | \
+ (1 << TEGRA_WIN_FMT_B4G4R4A4) | \
+ (1 << TEGRA_WIN_FMT_B5G5R5A) | \
+ (1 << TEGRA_WIN_FMT_B5G6R5) | \
+ (1 << TEGRA_WIN_FMT_AB5G5R5) | \
+ (1 << TEGRA_WIN_FMT_B8G8R8A8) | \
+ (1 << TEGRA_WIN_FMT_R8G8B8A8) | \
+ (1 << TEGRA_WIN_FMT_B6x2G6x2R6x2A8) | \
+ (1 << TEGRA_WIN_FMT_R6x2G6x2B6x2A8))
+
+#define TEGRA_WIN_FMT_WIN_B (TEGRA_WIN_FMT_BASE | \
+ (1 << TEGRA_WIN_FMT_B6x2G6x2R6x2A8) | \
+ (1 << TEGRA_WIN_FMT_R6x2G6x2B6x2A8) | \
+ (1 << TEGRA_WIN_FMT_YCbCr422RA) | \
+ (1 << TEGRA_WIN_FMT_YUV422RA))
+
+#define TEGRA_WIN_FMT_WIN_C (TEGRA_WIN_FMT_BASE | \
+ (1 << TEGRA_WIN_FMT_B6x2G6x2R6x2A8) | \
+ (1 << TEGRA_WIN_FMT_R6x2G6x2B6x2A8) | \
+ (1 << TEGRA_WIN_FMT_YCbCr422RA) | \
+ (1 << TEGRA_WIN_FMT_YUV422RA))
+
+/* preferred formats do not include 32-bpp formats */
+#define TEGRA_WIN_PREF_FMT_WIN_B (TEGRA_WIN_FMT_WIN_B & \
+ ~(1 << TEGRA_WIN_FMT_B8G8R8A8) & \
+ ~(1 << TEGRA_WIN_FMT_R8G8B8A8))
+
+
+
+/* For each entry, we define the offset to read specific feature. Define the
+ * offset for TEGRA_DC_FEATURE_MAXIMUM_SCALE */
+#define H_SCALE_UP 0
+#define V_SCALE_UP 1
+#define H_FILTER_DOWN 2
+#define V_FILTER_DOWN 3
+
+/* Define the offset for TEGRA_DC_FEATURE_MAXIMUM_SIZE */
+#define MAX_WIDTH 0
+#define MIN_WIDTH 1
+#define MAX_HEIGHT 2
+#define MIN_HEIGHT 3
+#define CHECK_SIZE(val, min, max) ( \
+ ((val) < (min) || (val) > (max)) ? -EINVAL : 0)
+
+/* Define the offset for TEGRA_DC_FEATURE_FILTER_TYPE */
+#define V_FILTER 0
+#define H_FILTER 1
+
+/* Define the offset for TEGRA_DC_FEATURE_INVERT_TYPE */
+#define H_INVERT 0
+#define V_INVERT 1
+#define SCAN_COLUMN 2
+
+/* Define the offset for TEGRA_DC_FEATURE_LAYOUT_TYPE. */
+#define PITCHED_LAYOUT 0
+#define TILED_LAYOUT 1
+
+/* Available operations on feature table. */
+enum {
+ HAS_SCALE,
+ HAS_TILED,
+ HAS_V_FILTER,
+ HAS_H_FILTER,
+ HAS_GEN2_BLEND,
+ GET_WIN_FORMATS,
+ GET_WIN_SIZE,
+};
+
+enum tegra_dc_feature_option {
+ TEGRA_DC_FEATURE_FORMATS,
+ TEGRA_DC_FEATURE_BLEND_TYPE,
+ TEGRA_DC_FEATURE_MAXIMUM_SIZE,
+ TEGRA_DC_FEATURE_MAXIMUM_SCALE,
+ TEGRA_DC_FEATURE_FILTER_TYPE,
+ TEGRA_DC_FEATURE_LAYOUT_TYPE,
+ TEGRA_DC_FEATURE_INVERT_TYPE,
+ TEGRA_DC_FEATURE_PREFERRED_FORMATS,
+};
+
+struct tegra_dc_feature_entry {
+ int window_index;
+ enum tegra_dc_feature_option option;
+ long arg[ENTRY_SIZE];
+};
+
+struct tegra_dc_feature {
+ unsigned num_entries;
+ struct tegra_dc_feature_entry *entries;
+};
+
+int tegra_dc_feature_has_scaling(struct tegra_dc *dc, int win_idx);
+int tegra_dc_feature_has_tiling(struct tegra_dc *dc, int win_idx);
+int tegra_dc_feature_has_filter(struct tegra_dc *dc, int win_idx, int operation);
+
+long *tegra_dc_parse_feature(struct tegra_dc *dc, int win_idx, int operation);
+void tegra_dc_feature_register(struct tegra_dc *dc);
+#endif
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index 5cba88f9e806..ce7ac3c7b0d0 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -4,8 +4,6 @@
* Copyright (C) 2010 Google, Inc.
* Author: Erik Gilling <konkers@android.com>
*
- * 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
* may be copied, distributed, and modified under those terms.
@@ -23,12 +21,14 @@
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/wait.h>
+#include <linux/fb.h>
#include <linux/completion.h>
#include <linux/switch.h>
#include <mach/dc.h>
#include "../host/dev.h"
+#include "../host/nvhost_acm.h"
#include "../host/host1x/host1x_syncpt.h"
#include <mach/tegra_dc_ext.h>
@@ -65,11 +65,13 @@ struct tegra_dc_out_ops {
void (*enable)(struct tegra_dc *dc);
/* disable output. dc clocks are on at this point */
void (*disable)(struct tegra_dc *dc);
-
/* suspend output. dc clocks are on at this point */
void (*suspend)(struct tegra_dc *dc);
/* resume output. dc clocks are on at this point */
void (*resume)(struct tegra_dc *dc);
+ /* mode filter. to provide a list of supported modes*/
+ bool (*mode_filter)(const struct tegra_dc *dc,
+ struct fb_videomode *mode);
};
struct tegra_dc {
@@ -95,8 +97,6 @@ struct tegra_dc {
void *out_data;
struct tegra_dc_mode mode;
- bool mode_dirty;
- spinlock_t mode_lock;
struct tegra_dc_win windows[DC_N_WINDOWS];
struct tegra_dc_blend blend;
@@ -137,6 +137,8 @@ struct tegra_dc {
struct tegra_dc_ext *ext;
+ struct tegra_dc_feature *feature;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugdir;
#endif
@@ -159,14 +161,19 @@ static inline void tegra_dc_io_end(struct tegra_dc *dc)
static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
unsigned long reg)
{
+ unsigned long ret;
+
BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev));
- return readl(dc->base + reg * 4);
+ ret = readl(dc->base + reg * 4);
+ trace_printk("readl %p=%#08lx\n", dc->base + reg * 4, ret);
+ return ret;
}
static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val,
unsigned long reg)
{
BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev));
+ trace_printk("writel %p=%#08lx\n", dc->base + reg * 4, val);
writel(val, dc->base + reg * 4);
}
diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h
index 2b8f8becb15b..ded64de2decc 100644
--- a/drivers/video/tegra/dc/dc_reg.h
+++ b/drivers/video/tegra/dc/dc_reg.h
@@ -460,6 +460,12 @@
#define DC_WIN_HP_FETCH_CONTROL 0x714
+
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+#define DC_WIN_GLOBAL_ALPHA 0x715
+#define GLOBAL_ALPHA_ENABLE 0x10000
+#endif
+
#define DC_WINBUF_START_ADDR 0x800
#define DC_WINBUF_START_ADDR_NS 0x801
#define DC_WINBUF_START_ADDR_U 0x802
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c
index c33d6e0a58b3..69cc60f70f1c 100644
--- a/drivers/video/tegra/dc/dsi.c
+++ b/drivers/video/tegra/dc/dsi.c
@@ -24,6 +24,8 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <mach/clk.h>
#include <mach/dc.h>
@@ -107,6 +109,7 @@ struct tegra_dc_dsi_data {
struct clk *dc_clk;
struct clk *dsi_clk;
+ struct clk *dsi_fixed_clk;
bool clk_ref;
struct mutex lock;
@@ -287,18 +290,128 @@ const u32 init_reg[] = {
inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg)
{
+ unsigned long ret;
+
BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev));
- return readl(dsi->base + reg * 4);
+ ret = readl(dsi->base + reg * 4);
+ trace_printk("readl %p=%#08lx\n", dsi->base + reg * 4, ret);
+ return ret;
}
EXPORT_SYMBOL(tegra_dsi_readl);
inline void tegra_dsi_writel(struct tegra_dc_dsi_data *dsi, u32 val, u32 reg)
{
BUG_ON(!nvhost_module_powered(nvhost_get_host(dsi->dc->ndev)->dev));
+ trace_printk("writel %p=%#08x\n", dsi->base + reg * 4, val);
writel(val, dsi->base + reg * 4);
}
EXPORT_SYMBOL(tegra_dsi_writel);
+#ifdef CONFIG_DEBUG_FS
+static int dbg_dsi_show(struct seq_file *s, void *unused)
+{
+ struct tegra_dc_dsi_data *dsi = s->private;
+
+#define DUMP_REG(a) do { \
+ seq_printf(s, "%-32s\t%03x\t%08lx\n", \
+ #a, a, tegra_dsi_readl(dsi, a)); \
+ } while (0)
+
+ tegra_dc_io_start(dsi->dc);
+ clk_enable(dsi->dsi_clk);
+
+ DUMP_REG(DSI_INCR_SYNCPT_CNTRL);
+ DUMP_REG(DSI_INCR_SYNCPT_ERROR);
+ DUMP_REG(DSI_CTXSW);
+ DUMP_REG(DSI_POWER_CONTROL);
+ DUMP_REG(DSI_INT_ENABLE);
+ DUMP_REG(DSI_CONTROL);
+ DUMP_REG(DSI_SOL_DELAY);
+ DUMP_REG(DSI_MAX_THRESHOLD);
+ DUMP_REG(DSI_TRIGGER);
+ DUMP_REG(DSI_TX_CRC);
+ DUMP_REG(DSI_STATUS);
+ DUMP_REG(DSI_INIT_SEQ_CONTROL);
+ DUMP_REG(DSI_INIT_SEQ_DATA_0);
+ DUMP_REG(DSI_INIT_SEQ_DATA_1);
+ DUMP_REG(DSI_INIT_SEQ_DATA_2);
+ DUMP_REG(DSI_INIT_SEQ_DATA_3);
+ DUMP_REG(DSI_INIT_SEQ_DATA_4);
+ DUMP_REG(DSI_INIT_SEQ_DATA_5);
+ DUMP_REG(DSI_INIT_SEQ_DATA_6);
+ DUMP_REG(DSI_INIT_SEQ_DATA_7);
+ DUMP_REG(DSI_PKT_SEQ_0_LO);
+ DUMP_REG(DSI_PKT_SEQ_0_HI);
+ DUMP_REG(DSI_PKT_SEQ_1_LO);
+ DUMP_REG(DSI_PKT_SEQ_1_HI);
+ DUMP_REG(DSI_PKT_SEQ_2_LO);
+ DUMP_REG(DSI_PKT_SEQ_2_HI);
+ DUMP_REG(DSI_PKT_SEQ_3_LO);
+ DUMP_REG(DSI_PKT_SEQ_3_HI);
+ DUMP_REG(DSI_PKT_SEQ_4_LO);
+ DUMP_REG(DSI_PKT_SEQ_4_HI);
+ DUMP_REG(DSI_PKT_SEQ_5_LO);
+ DUMP_REG(DSI_PKT_SEQ_5_HI);
+ DUMP_REG(DSI_DCS_CMDS);
+ DUMP_REG(DSI_PKT_LEN_0_1);
+ DUMP_REG(DSI_PKT_LEN_2_3);
+ DUMP_REG(DSI_PKT_LEN_4_5);
+ DUMP_REG(DSI_PKT_LEN_6_7);
+ DUMP_REG(DSI_PHY_TIMING_0);
+ DUMP_REG(DSI_PHY_TIMING_1);
+ DUMP_REG(DSI_PHY_TIMING_2);
+ DUMP_REG(DSI_BTA_TIMING);
+ DUMP_REG(DSI_TIMEOUT_0);
+ DUMP_REG(DSI_TIMEOUT_1);
+ DUMP_REG(DSI_TO_TALLY);
+ DUMP_REG(DSI_PAD_CONTROL);
+ DUMP_REG(DSI_PAD_CONTROL_CD);
+ DUMP_REG(DSI_PAD_CD_STATUS);
+ DUMP_REG(DSI_VID_MODE_CONTROL);
+#undef DUMP_REG
+
+ clk_disable(dsi->dsi_clk);
+ tegra_dc_io_end(dsi->dc);
+
+ return 0;
+}
+
+static int dbg_dsi_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_dsi_show, inode->i_private);
+}
+
+static const struct file_operations dbg_fops = {
+ .open = dbg_dsi_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *dsidir;
+
+static void tegra_dc_dsi_debug_create(struct tegra_dc_dsi_data *dsi)
+{
+ struct dentry *retval;
+
+ dsidir = debugfs_create_dir("tegra_dsi", NULL);
+ if (!dsidir)
+ return;
+ retval = debugfs_create_file("regs", S_IRUGO, dsidir, dsi,
+ &dbg_fops);
+ if (!retval)
+ goto free_out;
+ return;
+free_out:
+ debugfs_remove_recursive(dsidir);
+ dsidir = NULL;
+ return;
+}
+#else
+static inline void tegra_dc_dsi_debug_create(struct tegra_dc_dsi_data *dsi)
+{ }
+#endif
+
static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi)
{
u32 val;
@@ -1341,6 +1454,7 @@ static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc,
if (!dsi->clk_ref) {
dsi->clk_ref = true;
clk_enable(dsi->dsi_clk);
+ clk_enable(dsi->dsi_fixed_clk);
tegra_periph_reset_deassert(dsi->dsi_clk);
}
dsi->current_dsi_clk_khz = clk_get_rate(dsi->dsi_clk) / 1000;
@@ -1817,6 +1931,10 @@ static struct dsi_status *tegra_dsi_prepare_host_transmission(
if (tegra_dsi_host_busy(dsi)) {
tegra_dsi_soft_reset(dsi);
+
+ /* WAR to stop host write in middle */
+ tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_TRIGGER);
+
if (tegra_dsi_host_busy(dsi)) {
err = -EBUSY;
dev_err(&dc->ndev->dev, "DSI host busy\n");
@@ -2584,6 +2702,7 @@ static void _tegra_dc_dsi_init(struct tegra_dc *dc)
{
struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+ tegra_dc_dsi_debug_create(dsi);
tegra_dsi_init_sw(dc, dsi);
/* TODO: Configure the CSI pad configuration */
}
@@ -2748,6 +2867,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc)
void __iomem *base;
struct clk *dc_clk = NULL;
struct clk *dsi_clk = NULL;
+ struct clk *dsi_fixed_clk = NULL;
struct tegra_dsi_out *dsi_pdata;
int err;
@@ -2790,8 +2910,9 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc)
dsi_clk = clk_get(&dc->ndev->dev, "dsib");
else
dsi_clk = clk_get(&dc->ndev->dev, "dsia");
+ dsi_fixed_clk = clk_get(&dc->ndev->dev, "dsi-fixed");
- if (IS_ERR_OR_NULL(dsi_clk)) {
+ if (IS_ERR_OR_NULL(dsi_clk) || IS_ERR_OR_NULL(dsi_fixed_clk)) {
dev_err(&dc->ndev->dev, "dsi: can't get clock\n");
err = -EBUSY;
goto err_release_regs;
@@ -2811,6 +2932,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc)
dsi->base_res = base_res;
dsi->dc_clk = dc_clk;
dsi->dsi_clk = dsi_clk;
+ dsi->dsi_fixed_clk = dsi_fixed_clk;
err = tegra_dc_dsi_cp_info(dsi, dsi_pdata);
if (err < 0)
@@ -2824,6 +2946,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc)
err_dsi_data:
err_clk_put:
clk_put(dsi_clk);
+ clk_put(dsi_fixed_clk);
err_release_regs:
release_resource(base_res);
err_free_dsi:
@@ -2937,6 +3060,7 @@ static int tegra_dsi_deep_sleep(struct tegra_dc *dc,
/* Disable dsi source clock */
clk_disable(dsi->dsi_clk);
+ clk_disable(dsi->dsi_fixed_clk);
dsi->clk_ref = false;
dsi->enabled = false;
diff --git a/drivers/video/tegra/dc/ext/Makefile b/drivers/video/tegra/dc/ext/Makefile
index 19860ab5db11..343217ccc4a8 100644
--- a/drivers/video/tegra/dc/ext/Makefile
+++ b/drivers/video/tegra/dc/ext/Makefile
@@ -1,3 +1,4 @@
+GCOV_PROFILE := y
obj-y += dev.o
obj-y += util.o
obj-y += cursor.o
diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c
index 04553e778390..c349a4720d2e 100644
--- a/drivers/video/tegra/dc/ext/dev.c
+++ b/drivers/video/tegra/dc/ext/dev.c
@@ -27,11 +27,12 @@
#include <video/tegra_dc_ext.h>
#include <mach/dc.h>
-#include <mach/nvmap.h>
+#include <linux/nvmap.h>
#include <mach/tegra_dc_ext.h>
/* XXX ew */
#include "../dc_priv.h"
+#include "../dc_config.h"
/* XXX ew 2 */
#include "../../host/dev.h"
/* XXX ew 3 */
@@ -172,10 +173,39 @@ void tegra_dc_ext_disable(struct tegra_dc_ext *ext)
}
}
+int tegra_dc_ext_check_windowattr(struct tegra_dc_ext *ext,
+ struct tegra_dc_win *win)
+{
+ long *addr;
+ struct tegra_dc *dc = ext->dc;
+
+ /* Check the window format */
+ addr = tegra_dc_parse_feature(dc, win->idx, GET_WIN_FORMATS);
+ if (!test_bit(win->fmt, addr)) {
+ dev_err(&dc->ndev->dev, "Color format of window %d is"
+ " invalid.\n", win->idx);
+ goto fail;
+ }
+
+ /* Check window size */
+ addr = tegra_dc_parse_feature(dc, win->idx, GET_WIN_SIZE);
+ if (CHECK_SIZE(win->out_w, addr[MIN_WIDTH], addr[MAX_WIDTH]) ||
+ CHECK_SIZE(win->out_h, addr[MIN_HEIGHT], addr[MAX_HEIGHT])) {
+ dev_err(&dc->ndev->dev, "Size of window %d is"
+ " invalid.\n", win->idx);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
struct tegra_dc_win *win,
const struct tegra_dc_ext_flip_win *flip_win)
{
+ int err = 0;
struct tegra_dc_ext_win *ext_win = &ext->win[win->idx];
if (flip_win->handle[TEGRA_DC_Y] == NULL) {
@@ -195,6 +225,10 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
win->flags |= TEGRA_WIN_FLAG_INVERT_H;
if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_INVERT_V)
win->flags |= TEGRA_WIN_FLAG_INVERT_V;
+ if (flip_win->attr.flags & TEGRA_DC_EXT_FLIP_FLAG_GLOBAL_ALPHA)
+ win->global_alpha = flip_win->attr.global_alpha;
+ else
+ win->global_alpha = 255;
win->fmt = flip_win->attr.pixformat;
win->x.full = flip_win->attr.x;
win->y.full = flip_win->attr.y;
@@ -223,6 +257,11 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
win->stride = flip_win->attr.stride;
win->stride_uv = flip_win->attr.stride_uv;
+ err = tegra_dc_ext_check_windowattr(ext, win);
+ if (err < 0)
+ dev_err(&ext->dc->ndev->dev,
+ "Window atrributes are invalid.\n");
+
if ((s32)flip_win->attr.pre_syncpt_id >= 0) {
nvhost_syncpt_wait_timeout(
&nvhost_get_host(ext->dc->ndev)->syncpt,
diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
index 95a637d5a52a..2a0c5497b7cb 100644
--- a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
+++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
@@ -25,7 +25,7 @@
#include <linux/poll.h>
#include <mach/dc.h>
-#include <mach/nvmap.h>
+#include <linux/nvmap.h>
#include <video/tegra_dc_ext.h>
diff --git a/drivers/video/tegra/dc/ext/util.c b/drivers/video/tegra/dc/ext/util.c
index 747085579f15..cede642372f3 100644
--- a/drivers/video/tegra/dc/ext/util.c
+++ b/drivers/video/tegra/dc/ext/util.c
@@ -20,7 +20,7 @@
#include <linux/types.h>
#include <mach/dc.h>
-#include <mach/nvmap.h>
+#include <linux/nvmap.h>
/* ugh */
#include "../../nvmap/nvmap.h"
diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c
index 2e1df8d82641..59b9afcec0e4 100644
--- a/drivers/video/tegra/dc/hdmi.c
+++ b/drivers/video/tegra/dc/hdmi.c
@@ -67,6 +67,28 @@
#define HDMI_ELD_PRODUCT_CODE_INDEX 18
#define HDMI_ELD_MONITOR_NAME_INDEX 20
+/* These two values need to be cross checked in case of
+ addition/removal from tegra_dc_hdmi_aspect_ratios[] */
+#define TEGRA_DC_HDMI_MIN_ASPECT_RATIO_PERCENT 80
+#define TEGRA_DC_HDMI_MAX_ASPECT_RATIO_PERCENT 320
+
+/* Percentage equivalent of standard aspect ratios
+ accurate upto two decimal digits */
+static int tegra_dc_hdmi_aspect_ratios[] = {
+ /* 3:2 */ 150,
+ /* 4:3 */ 133,
+ /* 4:5 */ 80,
+ /* 5:4 */ 125,
+ /* 9:5 */ 180,
+ /* 16:5 */ 320,
+ /* 16:9 */ 178,
+ /* 16:10 */ 160,
+ /* 19:10 */ 190,
+ /* 25:16 */ 156,
+ /* 64:35 */ 183,
+ /* 72:35 */ 206
+};
+
struct tegra_dc_hdmi_data {
struct tegra_dc *dc;
struct tegra_edid *edid;
@@ -94,6 +116,7 @@ struct tegra_dc_hdmi_data {
bool clk_enabled;
unsigned audio_freq;
unsigned audio_source;
+ bool audio_inject_null;
bool dvi;
};
@@ -947,12 +970,16 @@ static const struct tegra_hdmi_audio_config
unsigned long tegra_hdmi_readl(struct tegra_dc_hdmi_data *hdmi,
unsigned long reg)
{
- return readl(hdmi->base + reg * 4);
+ unsigned long ret;
+ ret = readl(hdmi->base + reg * 4);
+ trace_printk("readl %p=%#08lx\n", hdmi->base + reg * 4, ret);
+ return ret;
}
void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi,
unsigned long val, unsigned long reg)
{
+ trace_printk("writel %p=%#08lx\n", hdmi->base + reg * 4, val);
writel(val, hdmi->base + reg * 4);
}
@@ -1240,62 +1267,84 @@ static bool tegra_dc_reload_mode(struct fb_videomode *mode)
return false;
}
+static bool tegra_dc_hdmi_valid_asp_ratio(const struct tegra_dc *dc,
+ struct fb_videomode *mode)
+{
+ int count = 0;
+ int m_aspratio = 0;
+ int s_aspratio = 0;
+
+ /* To check the aspect upto two decimal digits, calculate in % */
+ m_aspratio = (mode->xres*100 / mode->yres);
+
+ if ((m_aspratio < TEGRA_DC_HDMI_MIN_ASPECT_RATIO_PERCENT) ||
+ (m_aspratio > TEGRA_DC_HDMI_MAX_ASPECT_RATIO_PERCENT))
+ return false;
+
+ /* Check from the table of supported aspect ratios, allow
+ difference of 1% for second decimal digit calibration */
+ for (count = 0; count < ARRAY_SIZE(tegra_dc_hdmi_aspect_ratios);
+ count++) {
+ s_aspratio = tegra_dc_hdmi_aspect_ratios[count];
+ if ((m_aspratio == s_aspratio) ||
+ (abs(m_aspratio - s_aspratio) == 1))
+ return true;
+ }
+
+ return false;
+}
+
static bool tegra_dc_hdmi_mode_filter(const struct tegra_dc *dc,
struct fb_videomode *mode)
{
- int i;
- int clock_per_frame;
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ return false;
+ /* Ignore modes with a 0 pixel clock */
if (!mode->pixclock)
return false;
#ifdef CONFIG_TEGRA_HDMI_74MHZ_LIMIT
- if (PICOS2KHZ(mode->pixclock) > 74250)
- return false;
+ if (PICOS2KHZ(mode->pixclock) > 74250)
+ return false;
#endif
- for (i = 0; i < ARRAY_SIZE(tegra_dc_hdmi_supported_modes); i++) {
- const struct fb_videomode *supported_mode
- = &tegra_dc_hdmi_supported_modes[i];
- if (tegra_dc_hdmi_mode_equal(supported_mode, mode) &&
- tegra_dc_hdmi_valid_pixclock(dc, supported_mode)) {
- if (mode->lower_margin == 1) {
- /* This might be the case for HDMI<->DVI
- * where std VESA representation will not
- * pass constraint V_FRONT_PORCH >=
- * V_REF_TO_SYNC + 1.So reload mode in
- * CVT timing standards.
- */
- if (!tegra_dc_reload_mode(mode))
- return false;
- }
- else
- memcpy(mode, supported_mode, sizeof(*mode));
+ /* Check if the mode's pixel clock is more than the max rate*/
+ if (!tegra_dc_hdmi_valid_pixclock(dc, mode))
+ return false;
- mode->flag = FB_MODE_IS_DETAILED;
- clock_per_frame = tegra_dc_calc_clock_per_frame(mode);
- mode->refresh = (PICOS2KHZ(mode->pixclock) * 1000)
- / clock_per_frame;
- return true;
+ /* Check if the mode's aspect ratio is supported */
+ if (!tegra_dc_hdmi_valid_asp_ratio(dc, mode))
+ return false;
+
+ /* Check some of DC's constraints */
+ if (mode->hsync_len > 1 && mode->vsync_len > 1 &&
+ mode->lower_margin + mode->vsync_len + mode->upper_margin > 1 &&
+ mode->xres >= 16 && mode->yres >= 16) {
+
+ if (mode->lower_margin == 1) {
+ /* This might be the case for HDMI<->DVI
+ * where std VESA representation will not
+ * pass constraint V_FRONT_PORCH >=
+ * V_REF_TO_SYNC + 1.So reload mode in
+ * CVT timing standards.
+ */
+ if (!tegra_dc_reload_mode(mode))
+ return false;
}
+ mode->flag = FB_MODE_IS_DETAILED;
+ mode->refresh = (PICOS2KHZ(mode->pixclock) * 1000) /
+ tegra_dc_calc_clock_per_frame(mode);
+ return true;
}
return false;
}
-
static bool tegra_dc_hdmi_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);
+ return tegra_dc_hpd(dc);
}
@@ -1816,7 +1865,10 @@ static int tegra_dc_hdmi_setup_audio(struct tegra_dc *dc, unsigned audio_freq,
a_source = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF;
#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
- tegra_hdmi_writel(hdmi,a_source | AUDIO_CNTRL0_INJECT_NULLSMPL,
+ if (hdmi->audio_inject_null)
+ a_source |= AUDIO_CNTRL0_INJECT_NULLSMPL;
+
+ tegra_hdmi_writel(hdmi,a_source,
HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0);
tegra_hdmi_writel(hdmi,
AUDIO_CNTRL0_ERROR_TOLERANCE(6) |
@@ -1919,6 +1971,31 @@ int tegra_hdmi_setup_audio_freq_source(unsigned audio_freq, unsigned audio_sourc
EXPORT_SYMBOL(tegra_hdmi_setup_audio_freq_source);
#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+int tegra_hdmi_audio_null_sample_inject(bool on)
+{
+ struct tegra_dc_hdmi_data *hdmi = dc_hdmi;
+ unsigned int val = 0;
+
+ if (!hdmi)
+ return -EAGAIN;
+
+ if (hdmi->audio_inject_null != on) {
+ hdmi->audio_inject_null = on;
+ if (hdmi->clk_enabled) {
+ val = tegra_hdmi_readl(hdmi,
+ HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0);
+ val &= ~AUDIO_CNTRL0_INJECT_NULLSMPL;
+ if (on)
+ val |= AUDIO_CNTRL0_INJECT_NULLSMPL;
+ tegra_hdmi_writel(hdmi,val,
+ HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_hdmi_audio_null_sample_inject);
+
int tegra_hdmi_setup_hda_presence()
{
struct tegra_dc_hdmi_data *hdmi = dc_hdmi;
@@ -2005,6 +2082,11 @@ static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi)
avi.r = HDMI_AVI_R_SAME;
+ if ((dc->mode.h_active == 720) && ((dc->mode.v_active == 480) || (dc->mode.v_active == 576)))
+ tegra_dc_writel(dc, 0x00101010, DC_DISP_BORDER_COLOR);
+ else
+ tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
+
if (dc->mode.v_active == 480) {
if (dc->mode.h_active == 640) {
avi.m = HDMI_AVI_M_4_3;
@@ -2037,9 +2119,12 @@ static void tegra_dc_hdmi_setup_avi_infoframe(struct tegra_dc *dc, bool dvi)
(dc->mode.v_active == 2205 && dc->mode.stereo_mode)) {
/* VIC for both 1080p and 1080p 3D mode */
avi.m = HDMI_AVI_M_16_9;
- if (dc->mode.h_front_porch == 88)
- avi.vic = 16; /* 60 Hz */
- else if (dc->mode.h_front_porch == 528)
+ if (dc->mode.h_front_porch == 88) {
+ if (dc->mode.pclk > 74250000)
+ avi.vic = 16; /* 60 Hz */
+ else
+ avi.vic = 34; /* 30 Hz */
+ } else if (dc->mode.h_front_porch == 528)
avi.vic = 31; /* 50 Hz */
else
avi.vic = 32; /* 24 Hz */
@@ -2200,10 +2285,16 @@ static void tegra_dc_hdmi_enable(struct tegra_dc *dc)
VSYNC_WINDOW_ENABLE,
HDMI_NV_PDISP_HDMI_VSYNC_WINDOW);
- tegra_hdmi_writel(hdmi,
- (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) |
- ARM_VIDEO_RANGE_LIMITED,
- HDMI_NV_PDISP_INPUT_CONTROL);
+ if ((dc->mode.h_active == 720) && ((dc->mode.v_active == 480) || (dc->mode.v_active == 576)))
+ tegra_hdmi_writel(hdmi,
+ (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) |
+ ARM_VIDEO_RANGE_FULL,
+ HDMI_NV_PDISP_INPUT_CONTROL);
+ else
+ tegra_hdmi_writel(hdmi,
+ (dc->ndev->id ? HDMI_SRC_DISPLAYB : HDMI_SRC_DISPLAYA) |
+ ARM_VIDEO_RANGE_LIMITED,
+ HDMI_NV_PDISP_INPUT_CONTROL);
clk_disable(hdmi->disp1_clk);
clk_disable(hdmi->disp2_clk);
@@ -2374,6 +2465,7 @@ struct tegra_dc_out_ops tegra_dc_hdmi_ops = {
.detect = tegra_dc_hdmi_detect,
.suspend = tegra_dc_hdmi_suspend,
.resume = tegra_dc_hdmi_resume,
+ .mode_filter = tegra_dc_hdmi_mode_filter,
};
struct tegra_dc_edid *tegra_dc_get_edid(struct tegra_dc *dc)
diff --git a/drivers/video/tegra/dc/rgb.c b/drivers/video/tegra/dc/rgb.c
index 2112643058f4..47d4e69cab2e 100644
--- a/drivers/video/tegra/dc/rgb.c
+++ b/drivers/video/tegra/dc/rgb.c
@@ -90,7 +90,7 @@ static const u32 tegra_dc_rgb_disable_pintable[] = {
DC_COM_PIN_OUTPUT_SELECT6, 0x00000000,
};
-void tegra_dc_rgb_enable(struct tegra_dc *dc)
+static void tegra_dc_rgb_enable(struct tegra_dc *dc)
{
int i;
u32 out_sel_pintable[ARRAY_SIZE(tegra_dc_rgb_enable_out_sel_pintable)];
@@ -144,9 +144,13 @@ void tegra_dc_rgb_enable(struct tegra_dc *dc)
}
tegra_dc_write_table(dc, out_sel_pintable);
+
+ /* Inform DC register updated */
+ tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
}
-void tegra_dc_rgb_disable(struct tegra_dc *dc)
+static void tegra_dc_rgb_disable(struct tegra_dc *dc)
{
tegra_dc_writel(dc, 0x00000000, DC_CMD_DISPLAY_POWER_CONTROL);