diff options
author | Tom Cherry <tcherry@nvidia.com> | 2012-05-23 12:06:13 -0700 |
---|---|---|
committer | Tom Cherry <tcherry@nvidia.com> | 2012-05-23 12:06:13 -0700 |
commit | a168c03bd97fd9761218779623db0cec09fa8f4a (patch) | |
tree | 521d2b51904da963d771c24fd9b142cc416f8259 /drivers/video/tegra/dc | |
parent | 11fb7d0e35d56230919eb91bee1aa138a10b8416 (diff) | |
parent | c7e3189c1802c2a6552eec960f521a1891529892 (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/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 374 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_config.c | 246 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_config.h | 148 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 19 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_reg.h | 6 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dsi.c | 128 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/dev.c | 41 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h | 2 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/util.c | 2 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 182 | ||||
-rw-r--r-- | drivers/video/tegra/dc/rgb.c | 8 |
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); |