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/dc.c | |
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/dc.c')
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 374 |
1 files changed, 210 insertions, 164 deletions
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); |