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 | |
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')
63 files changed, 1922 insertions, 907 deletions
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index 473875a3ae78..5e15a92609b0 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -1103,13 +1103,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var, if (mx3_fbi->txd) async_tx_ack(mx3_fbi->txd); -<<<<<<< HEAD - txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg + - mx3_fbi->cur_ipu_buf, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT); -======= txd = dmaengine_prep_slave_sg(dma_chan, sg + mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); ->>>>>>> 1605282... dmaengine/dma_slave: introduce inline wrappers if (!txd) { dev_err(fbi->device, "Error preparing a DMA transaction descriptor.\n"); diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index 7de26267155e..59e27788680d 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -53,7 +53,7 @@ config NVMAP_RECLAIM_UNPINNED_VM config NVMAP_ALLOW_SYSMEM bool "Allow physical system memory to be used by nvmap" depends on TEGRA_NVMAP - default y + default n help Say Y here to allow nvmap to use physical system memory (i.e., shared with the operating system but not translated through @@ -85,6 +85,22 @@ config NVMAP_CARVEOUT_COMPACTOR heap and retries the failed allocation. Say Y here to let nvmap to keep carveout fragmentation under control. +config NVMAP_PAGE_POOLS + bool "Use page pools to reduce allocation overhead" + depends on TEGRA_NVMAP + default y + help + say Y here to reduce the alloction overhead, which is significant + for uncached, writecombine and inner cacheable memories as it + involves changing page attributes during every allocation per page + and flushing cache. Alloc time is reduced by allcoating the pages + ahead and keeping them aside. The reserved pages would be released + when system is low on memory and acquired back during release of + memory. + +config NVMAP_PAGE_POOL_SIZE + hex + default 0x0 config NVMAP_VPR bool "Enable VPR Heap." 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); diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index e24588bdd0b7..50aa9b383059 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -6,7 +6,7 @@ * Colin Cross <ccross@android.com> * Travis Geiselbrecht <travis@palm.com> * - * Copyright (C) 2010-2012 NVIDIA Corporation + * Copyright (C) 2010-2011 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 @@ -37,7 +37,7 @@ #include <mach/dc.h> #include <mach/fb.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "host/dev.h" #include "nvmap/nvmap.h" @@ -64,11 +64,27 @@ static u32 pseudo_palette[16]; static int tegra_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { + struct tegra_fb_info *tegra_fb = info->par; + struct tegra_dc *dc = tegra_fb->win->dc; + struct tegra_dc_out_ops *ops = dc->out_ops; + struct fb_videomode mode; + if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) > info->screen_size) return -EINVAL; - /* double yres_virtual to allow double buffering through pan_display */ + /* Apply mode filter for HDMI only -LVDS supports only fix mode */ + if (ops && ops->mode_filter) { + + fb_var_to_videomode(&mode, var); + if (!ops->mode_filter(dc, &mode)) + return -EINVAL; + + /* Mode filter may have modified the mode */ + fb_videomode_to_var(var, &mode); + } + + /* Double yres_virtual to allow double buffering through pan_display */ var->yres_virtual = var->yres * 2; return 0; @@ -79,10 +95,6 @@ static int tegra_fb_set_par(struct fb_info *info) struct tegra_fb_info *tegra_fb = info->par; struct fb_var_screeninfo *var = &info->var; - BUG_ON(info == NULL); - if (!info) - return -EINVAL; - if (var->bits_per_pixel) { /* we only support RGB ordering for now */ switch (var->bits_per_pixel) { @@ -287,16 +299,6 @@ static int tegra_fb_blank(int blank, struct fb_info *info) dev_dbg(&tegra_fb->ndev->dev, "unblank\n"); tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED; tegra_dc_enable(tegra_fb->win->dc); -#if defined(CONFIG_FRAMEBUFFER_CONSOLE) - /* - * TODO: - * This is a work around to provide an unblanking flip - * to dc driver, required to display fb-console after - * a blank event,and needs to be replaced by a proper - * unblanking mechanism - */ - tegra_fb_flip_win(tegra_fb); -#endif return 0; case FB_BLANK_NORMAL: @@ -335,7 +337,8 @@ static int tegra_fb_pan_display(struct fb_var_screeninfo *var, (var->xoffset * (var->bits_per_pixel/8)); tegra_fb->win->phys_addr = addr; - /* TODO: update virt_addr */ + tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED; + tegra_fb->win->virt_addr = info->screen_base; tegra_dc_update_windows(&tegra_fb->win, 1); tegra_dc_sync_windows(&tegra_fb->win, 1); @@ -524,7 +527,6 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, unsigned long fb_size = 0; unsigned long fb_phys = 0; int ret = 0; - struct fb_videomode m; win = tegra_dc_get_window(dc, fb_data->win); if (!win) { @@ -576,15 +578,22 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev, info->fix.line_length = round_up(info->fix.line_length, TEGRA_LINEAR_PITCH_ALIGNMENT); - INIT_LIST_HEAD(&info->modelist); - tegra_dc_to_fb_videomode(&m, &dc->mode); - fb_videomode_to_var(&info->var, &m); + info->var.xres = fb_data->xres; + info->var.yres = fb_data->yres; info->var.xres_virtual = fb_data->xres; info->var.yres_virtual = fb_data->yres * 2; info->var.bits_per_pixel = fb_data->bits_per_pixel; info->var.activate = FB_ACTIVATE_VBL; info->var.height = tegra_dc_get_out_height(dc); info->var.width = tegra_dc_get_out_width(dc); + info->var.pixclock = 0; + info->var.left_margin = 0; + info->var.right_margin = 0; + info->var.upper_margin = 0; + info->var.lower_margin = 0; + info->var.hsync_len = 0; + info->var.vsync_len = 0; + info->var.vmode = FB_VMODE_NONINTERLACED; win->x.full = dfixed_const(0); win->y.full = dfixed_const(0); diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile index 0180885af4d7..d47c97571286 100644 --- a/drivers/video/tegra/host/Makefile +++ b/drivers/video/tegra/host/Makefile @@ -9,7 +9,8 @@ nvhost-objs = \ bus.o \ dev.o \ debug.o \ - bus_client.o + bus_client.o \ + chip_support.o obj-$(CONFIG_TEGRA_GRHOST) += mpe/ obj-$(CONFIG_TEGRA_GRHOST) += gr3d/ diff --git a/drivers/video/tegra/host/bus.c b/drivers/video/tegra/host/bus.c index 774aac7bd431..e59dc4153b14 100644 --- a/drivers/video/tegra/host/bus.c +++ b/drivers/video/tegra/host/bus.c @@ -17,11 +17,14 @@ * */ +#include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/nvhost.h> +#include "bus.h" #include "dev.h" +struct nvhost_bus *nvhost_bus_inst; struct nvhost_master *nvhost; struct resource *nvhost_get_resource(struct nvhost_device *dev, @@ -72,12 +75,42 @@ int nvhost_get_irq_byname(struct nvhost_device *dev, const char *name) } EXPORT_SYMBOL_GPL(nvhost_get_irq_byname); +static struct nvhost_device_id *nvhost_bus_match_id(struct nvhost_device *dev, + struct nvhost_device_id *id_table) +{ + while (id_table->name[0]) { + if (strcmp(dev->name, id_table->name) == 0) + return id_table; + id_table++; + } + return NULL; +} + +static int nvhost_bus_match(struct device *_dev, struct device_driver *drv) +{ + struct nvhost_device *dev = to_nvhost_device(_dev); + struct nvhost_driver *ndrv = to_nvhost_driver(drv); + + /* check if driver support multiple devices through id_table */ + if (ndrv->id_table) + return nvhost_bus_match_id(dev, ndrv->id_table) != NULL; + else /* driver does not support id_table */ + return !strncmp(dev->name, drv->name, strlen(drv->name)); +} + static int nvhost_drv_probe(struct device *_dev) { struct nvhost_driver *drv = to_nvhost_driver(_dev->driver); struct nvhost_device *dev = to_nvhost_device(_dev); - return drv->probe(dev); + if (drv && drv->probe) { + if (drv->id_table) + return drv->probe(dev, nvhost_bus_match_id(dev, drv->id_table)); + else + return drv->probe(dev, NULL); + } + else + return -ENODEV; } static int nvhost_drv_remove(struct device *_dev) @@ -98,7 +131,7 @@ static void nvhost_drv_shutdown(struct device *_dev) int nvhost_driver_register(struct nvhost_driver *drv) { - drv->driver.bus = &nvhost_bus_type; + drv->driver.bus = &nvhost_bus_inst->nvhost_bus_type; if (drv->probe) drv->driver.probe = nvhost_drv_probe; if (drv->remove) @@ -129,7 +162,7 @@ int nvhost_device_register(struct nvhost_device *dev) if (!dev->dev.parent && nvhost && nvhost->dev != dev) dev->dev.parent = &nvhost->dev->dev; - dev->dev.bus = &nvhost_bus_type; + dev->dev.bus = &nvhost_bus_inst->nvhost_bus_type; if (dev->id != -1) dev_set_name(&dev->dev, "%s.%d", dev->name, dev->id); @@ -194,13 +227,6 @@ void nvhost_device_unregister(struct nvhost_device *dev) } EXPORT_SYMBOL_GPL(nvhost_device_unregister); -static int nvhost_bus_match(struct device *_dev, struct device_driver *drv) -{ - struct nvhost_device *dev = to_nvhost_device(_dev); - - return !strncmp(dev->name, drv->name, strlen(drv->name)); -} - #ifdef CONFIG_PM_SLEEP static int nvhost_legacy_suspend(struct device *dev, pm_message_t mesg) @@ -528,13 +554,6 @@ static const struct dev_pm_ops nvhost_dev_pm_ops = { .runtime_idle = nvhost_pm_runtime_idle, }; -struct bus_type nvhost_bus_type = { - .name = "nvhost", - .match = nvhost_bus_match, - .pm = &nvhost_dev_pm_ops, -}; -EXPORT_SYMBOL(nvhost_bus_type); - static int set_parent(struct device *dev, void *data) { struct nvhost_device *ndev = to_nvhost_device(dev); @@ -549,21 +568,44 @@ int nvhost_bus_add_host(struct nvhost_master *host) nvhost = host; /* Assign host1x as parent to all devices in nvhost bus */ - bus_for_each_dev(&nvhost_bus_type, NULL, host, set_parent); + bus_for_each_dev(&nvhost_bus_inst->nvhost_bus_type, NULL, host, set_parent); return 0; } +struct nvhost_bus *nvhost_bus_get(void) +{ + return nvhost_bus_inst; +} int nvhost_bus_init(void) { int err; + struct nvhost_chip_support *chip_ops; pr_info("host1x bus init\n"); - err = bus_register(&nvhost_bus_type); + nvhost_bus_inst = kzalloc(sizeof(*nvhost_bus_inst), GFP_KERNEL); + if (nvhost_bus_inst == NULL) { + pr_err("%s: Cannot allocate nvhost_bus\n", __func__); + return -ENOMEM; + } + + chip_ops = kzalloc(sizeof(*chip_ops), GFP_KERNEL); + if (chip_ops == NULL) { + pr_err("%s: Cannot allocate nvhost_chip_support\n", __func__); + kfree(nvhost_bus_inst); + nvhost_bus_inst = NULL; + return -ENOMEM; + } + + nvhost_bus_inst->nvhost_bus_type.name = "nvhost"; + nvhost_bus_inst->nvhost_bus_type.match = nvhost_bus_match; + nvhost_bus_inst->nvhost_bus_type.pm = &nvhost_dev_pm_ops; + nvhost_bus_inst->nvhost_chip_ops = chip_ops; + + err = bus_register(&nvhost_bus_inst->nvhost_bus_type); return err; } postcore_initcall(nvhost_bus_init); - diff --git a/drivers/video/tegra/host/bus.h b/drivers/video/tegra/host/bus.h new file mode 100644 index 000000000000..99f820335d60 --- /dev/null +++ b/drivers/video/tegra/host/bus.h @@ -0,0 +1,38 @@ +/* + * drivers/video/tegra/host/bus.h + * + * Tegra Graphics Host bus API header + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NVHOST_BUS_H +#define __NVHOST_BUS_H + +#include <linux/types.h> +#include <linux/device.h> + +#include "chip_support.h" + +struct nvhost_bus { + struct nvhost_chip_support *nvhost_chip_ops; + struct bus_type nvhost_bus_type; +}; + +struct nvhost_bus *nvhost_bus_get(void); + +extern struct nvhost_bus *nvhost_bus_inst; + +#endif diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index b49c26e04f29..fd632a6ea9c5 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -36,7 +36,7 @@ #include <linux/nvhost.h> #include <linux/nvhost_ioctl.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <mach/gpufuse.h> #include <mach/hardware.h> #include <mach/iomap.h> @@ -44,6 +44,11 @@ #include "debug.h" #include "bus_client.h" #include "dev.h" +#include "nvhost_acm.h" + +#include "nvhost_channel.h" +#include "nvhost_job.h" +#include "nvhost_hwctx.h" void nvhost_read_module_regs(struct nvhost_device *ndev, u32 offset, int count, u32 *values) @@ -85,45 +90,6 @@ struct nvhost_channel_userctx { int clientid; }; -/* - * Write cmdbuf to ftrace output. Checks if cmdbuf contents should be output - * and mmaps the cmdbuf contents if required. - */ -static void trace_write_cmdbufs(struct nvhost_job *job) -{ - struct nvmap_handle_ref handle; - void *mem = NULL; - int i = 0; - - for (i = 0; i < job->num_gathers; i++) { - struct nvhost_channel_gather *gather = &job->gathers[i]; - if (nvhost_debug_trace_cmdbuf) { - handle.handle = nvmap_id_to_handle(gather->mem_id); - mem = nvmap_mmap(&handle); - if (IS_ERR_OR_NULL(mem)) - mem = NULL; - }; - - if (mem) { - u32 i; - /* - * Write in batches of 128 as there seems to be a limit - * of how much you can output to ftrace at once. - */ - for (i = 0; i < gather->words; i += TRACE_MAX_LENGTH) { - trace_nvhost_channel_write_cmdbuf_data( - job->ch->dev->name, - gather->mem_id, - min(gather->words - i, - TRACE_MAX_LENGTH), - gather->offset + i * sizeof(u32), - mem); - } - nvmap_munmap(&handle, mem); - } - } -} - static int nvhost_channelrelease(struct inode *inode, struct file *filp) { struct nvhost_channel_userctx *priv = filp->private_data; @@ -174,6 +140,7 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp) priv->priority = NVHOST_PRIORITY_MEDIUM; priv->clientid = atomic_add_return(1, &nvhost_get_host(ch->dev)->clientid); + priv->timeout = MAX_STUCK_CHECK_COUNT * SYNCPT_CHECK_PERIOD; priv->job = nvhost_job_alloc(ch, priv->hwctx, &priv->hdr, NULL, priv->priority, priv->clientid); @@ -364,8 +331,6 @@ static int nvhost_ioctl_channel_flush( ctx->timeout = nvhost_debug_force_timeout_val; } - trace_write_cmdbufs(ctx->job); - /* context switch if needed, and submit user's gathers to the channel */ err = nvhost_channel_submit(ctx->job); args->value = ctx->job->syncpt_end; @@ -375,12 +340,11 @@ static int nvhost_ioctl_channel_flush( return err; } -static int nvhost_ioctl_channel_read_3d_reg( - struct nvhost_channel_userctx *ctx, +static int nvhost_ioctl_channel_read_3d_reg(struct nvhost_channel_userctx *ctx, struct nvhost_read_3d_reg_args *args) { - BUG_ON(!channel_op(ctx->ch).read3dreg); - return channel_op(ctx->ch).read3dreg(ctx->ch, ctx->hwctx, + BUG_ON(!channel_op().read3dreg); + return channel_op().read3dreg(ctx->ch, ctx->hwctx, args->offset, &args->value); } @@ -535,18 +499,23 @@ int nvhost_client_user_init(struct nvhost_device *dev) int err, devno; struct nvhost_channel *ch = dev->channel; + err = alloc_chrdev_region(&devno, 0, 1, IFACE_NAME); + if (err < 0) { + dev_err(&dev->dev, "failed to allocate devno\n"); + goto fail; + } cdev_init(&ch->cdev, &nvhost_channelops); ch->cdev.owner = THIS_MODULE; - devno = MKDEV(nvhost_major, nvhost_minor + dev->index); err = cdev_add(&ch->cdev, devno, 1); if (err < 0) { dev_err(&dev->dev, "failed to add chan %i cdev\n", dev->index); goto fail; } - ch->node = device_create(nvhost_get_host(dev)->nvhost_class, NULL, devno, NULL, + ch->node = device_create(nvhost_get_host(dev)->nvhost_class, + NULL, devno, NULL, IFACE_NAME "-%s", dev->name); if (IS_ERR(ch->node)) { err = PTR_ERR(ch->node); @@ -564,7 +533,11 @@ int nvhost_client_device_init(struct nvhost_device *dev) { int err; struct nvhost_master *nvhost_master = nvhost_get_host(dev); - struct nvhost_channel *ch = &nvhost_master->channels[dev->index]; + struct nvhost_channel *ch; + + ch = nvhost_alloc_channel(dev->index); + if (ch == NULL) + return -ENODEV; /* store the pointer to this device for channel */ ch->dev = dev; @@ -587,6 +560,7 @@ int nvhost_client_device_init(struct nvhost_device *dev) fail: /* Add clean-up */ + nvhost_free_channel(ch); return err; } diff --git a/drivers/video/tegra/host/chip_support.c b/drivers/video/tegra/host/chip_support.c new file mode 100644 index 000000000000..9abb1fa026a4 --- /dev/null +++ b/drivers/video/tegra/host/chip_support.c @@ -0,0 +1,56 @@ +/* + * drivers/video/tegra/host/chip_support.c + * + * Tegra Graphics Host Chip support module + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/errno.h> + +#include <mach/hardware.h> + +#include "bus.h" +#include "chip_support.h" +#include "t20/t20.h" +#include "t30/t30.h" + +struct nvhost_chip_support *nvhost_get_chip_ops(void) +{ + return (nvhost_bus_get())->nvhost_chip_ops; +} + +int nvhost_init_chip_support(struct nvhost_master *host) +{ + int err = 0; + struct nvhost_chip_support *chip_ops; + + chip_ops = nvhost_get_chip_ops(); + + switch (tegra_get_chipid()) { + case TEGRA_CHIPID_TEGRA2: + err = nvhost_init_t20_support(host, chip_ops); + break; + + case TEGRA_CHIPID_TEGRA3: + err = nvhost_init_t30_support(host, chip_ops); + break; + + default: + err = -ENODEV; + } + + return err; +} diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h index 6727e7a69fb4..edc5f6a51574 100644 --- a/drivers/video/tegra/host/chip_support.h +++ b/drivers/video/tegra/host/chip_support.h @@ -21,21 +21,26 @@ #define _NVHOST_CHIP_SUPPORT_H_ #include <linux/types.h> +#include "bus.h" + struct output; + +struct nvhost_master; +struct nvhost_intr; +struct nvhost_syncpt; struct nvhost_waitchk; struct nvhost_userctx_timeout; -struct nvhost_master; struct nvhost_channel; struct nvmap_handle; struct nvmap_client; struct nvhost_hwctx; struct nvhost_cdma; -struct nvhost_intr; +struct nvhost_job; struct push_buffer; struct nvhost_syncpt; -struct nvhost_master; struct dentry; struct nvhost_job; +struct nvhost_intr_syncpt; struct nvhost_chip_support { struct { @@ -64,11 +69,6 @@ struct nvhost_chip_support { u32 syncpt_incrs, u32 syncval, u32 nr_slots); - void (*timeout_pb_incr)(struct nvhost_cdma *, - u32 getptr, - u32 syncpt_incrs, - u32 nr_slots, - bool exec_ctxsave); } cdma; struct { @@ -133,9 +133,22 @@ struct nvhost_chip_support { } intr; struct { - struct nvhost_device *(*get_nvhost_device)(struct nvhost_master *host, - char *name); + struct nvhost_device *(*get_nvhost_device)(char *name); + struct nvhost_channel *(*alloc_nvhost_channel)(int chid); + void (*free_nvhost_channel)(struct nvhost_channel *ch); } nvhost_dev; }; +struct nvhost_chip_support *nvhost_get_chip_ops(void); + +#define host_device_op() nvhost_get_chip_ops()->nvhost_dev +#define channel_cdma_op() nvhost_get_chip_ops()->cdma +#define channel_op() nvhost_get_chip_ops()->channel +#define syncpt_op() nvhost_get_chip_ops()->syncpt +#define intr_op() nvhost_get_chip_ops()->intr +#define cdma_op() nvhost_get_chip_ops()->cdma +#define cdma_pb_op() nvhost_get_chip_ops()->push_buffer + +int nvhost_init_chip_support(struct nvhost_master *); + #endif /* _NVHOST_CHIP_SUPPORT_H_ */ diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c index 91436c903fc6..8a26f92c79f6 100644 --- a/drivers/video/tegra/host/debug.c +++ b/drivers/video/tegra/host/debug.c @@ -22,8 +22,12 @@ #include <linux/io.h> +#include "bus.h" #include "dev.h" #include "debug.h" +#include "nvhost_acm.h" +#include "nvhost_channel.h" +#include "chip_support.h" pid_t nvhost_debug_null_kickoff_pid; unsigned int nvhost_debug_trace_cmdbuf; @@ -59,8 +63,8 @@ static int show_channels(struct device *dev, void *data) mutex_lock(&ch->reflock); if (ch->refcount) { mutex_lock(&ch->cdma.lock); - m->op.debug.show_channel_fifo(m, ch, o, nvdev->index); - m->op.debug.show_channel_cdma(m, ch, o, nvdev->index); + nvhost_get_chip_ops()->debug.show_channel_fifo(m, ch, o, nvdev->index); + nvhost_get_chip_ops()->debug.show_channel_cdma(m, ch, o, nvdev->index); mutex_unlock(&ch->cdma.lock); } mutex_unlock(&ch->reflock); @@ -72,7 +76,7 @@ static int show_channels(struct device *dev, void *data) static void show_syncpts(struct nvhost_master *m, struct output *o) { int i; - BUG_ON(!m->op.syncpt.name); + BUG_ON(!nvhost_get_chip_ops()->syncpt.name); nvhost_debug_output(o, "---- syncpts ----\n"); for (i = 0; i < m->syncpt.nb_pts; i++) { u32 max = nvhost_syncpt_read_max(&m->syncpt, i); @@ -80,7 +84,7 @@ static void show_syncpts(struct nvhost_master *m, struct output *o) if (!min && !max) continue; nvhost_debug_output(o, "id %d (%s) min %d max %d\n", - i, m->op.syncpt.name(&m->syncpt, i), + i, nvhost_get_chip_ops()->syncpt.name(&m->syncpt, i), min, max); } @@ -99,10 +103,10 @@ static void show_all(struct nvhost_master *m, struct output *o) { nvhost_module_busy(m->dev); - m->op.debug.show_mlocks(m, o); + nvhost_get_chip_ops()->debug.show_mlocks(m, o); show_syncpts(m, o); nvhost_debug_output(o, "---- channels ----\n"); - bus_for_each_dev(&nvhost_bus_type, NULL, o, show_channels); + bus_for_each_dev(&(nvhost_bus_get())->nvhost_bus_type, NULL, o, show_channels); nvhost_module_idle(m->dev); } @@ -142,8 +146,8 @@ void nvhost_debug_init(struct nvhost_master *master) debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de, &nvhost_debug_trace_cmdbuf); - if (master->op.debug.debug_init) - master->op.debug.debug_init(de); + if (nvhost_get_chip_ops()->debug.debug_init) + nvhost_get_chip_ops()->debug.debug_init(de); debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de, &nvhost_debug_force_timeout_pid); diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index 2e1ffeea7729..ca73528fbeeb 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -36,22 +36,21 @@ #include <linux/nvhost.h> #include <linux/nvhost_ioctl.h> -#include <mach/nvmap.h> #include <mach/gpufuse.h> #include <mach/hardware.h> #include <mach/iomap.h> #include "debug.h" -#include "nvhost_job.h" #include "t20/t20.h" #include "t30/t30.h" #include "bus_client.h" +#include "nvhost_acm.h" +#include <linux/nvmap.h> +#include "nvhost_channel.h" +#include "nvhost_job.h" #define DRIVER_NAME "host1x" -int nvhost_major; -int nvhost_minor; - static unsigned int register_sets; struct nvhost_ctrl_userctx { @@ -79,7 +78,8 @@ static int nvhost_ctrlrelease(struct inode *inode, struct file *filp) static int nvhost_ctrlopen(struct inode *inode, struct file *filp) { - struct nvhost_master *host = container_of(inode->i_cdev, struct nvhost_master, cdev); + struct nvhost_master *host = + container_of(inode->i_cdev, struct nvhost_master, cdev); struct nvhost_ctrl_userctx *priv; u32 *mod_locks; @@ -167,24 +167,21 @@ static int nvhost_ioctl_ctrl_module_mutex(struct nvhost_ctrl_userctx *ctx, return err; } -static struct nvhost_device *get_ndev_by_moduleid(struct nvhost_master *host, - u32 id) +static int match_by_moduleid(struct device *dev, void *data) { - int i; + struct nvhost_device *ndev = to_nvhost_device(dev); + u32 id = (u32)data; - for (i = 0; i < host->nb_channels; i++) { - struct nvhost_device *ndev = host->channels[i].dev; + return id == ndev->moduleid; +} - /* display and dsi do not use channel for register programming. - * so their channels do not have device instance. - * hence skip such channels from here. */ - if (ndev == NULL) - continue; +static struct nvhost_device *get_ndev_by_moduleid(struct nvhost_master *host, + u32 id) +{ + struct device *dev = bus_find_device(&nvhost_bus_inst->nvhost_bus_type, NULL, (void *)id, + match_by_moduleid); - if (id == ndev->moduleid) - return ndev; - } - return NULL; + return dev ? to_nvhost_device(dev) : NULL; } static int nvhost_ioctl_ctrl_module_regrdwr(struct nvhost_ctrl_userctx *ctx, @@ -329,9 +326,7 @@ static int __devinit nvhost_user_init(struct nvhost_master *host) goto fail; } - err = alloc_chrdev_region(&devno, nvhost_minor, - host->nb_channels + 1, IFACE_NAME); - nvhost_major = MAJOR(devno); + err = alloc_chrdev_region(&devno, 0, 1, IFACE_NAME); if (err < 0) { dev_err(&host->dev->dev, "failed to reserve chrdev region\n"); goto fail; @@ -339,7 +334,6 @@ static int __devinit nvhost_user_init(struct nvhost_master *host) cdev_init(&host->cdev, &nvhost_ctrlops); host->cdev.owner = THIS_MODULE; - devno = MKDEV(nvhost_major, nvhost_minor + host->nb_channels); err = cdev_add(&host->cdev, devno, 1); if (err < 0) goto fail; @@ -358,45 +352,40 @@ fail: struct nvhost_device *nvhost_get_device(char *name) { - BUG_ON(!host_device_op(nvhost).get_nvhost_device); - return host_device_op(nvhost).get_nvhost_device(nvhost, name); + BUG_ON(!host_device_op().get_nvhost_device); + return host_device_op().get_nvhost_device(name); } -static void nvhost_remove_chip_support(struct nvhost_master *host) +struct nvhost_channel *nvhost_alloc_channel(int index) { - kfree(host->channels); - host->channels = 0; + BUG_ON(!host_device_op().alloc_nvhost_channel); + return host_device_op().alloc_nvhost_channel(index); +} + +void nvhost_free_channel(struct nvhost_channel *ch) +{ + BUG_ON(!host_device_op().free_nvhost_channel); + host_device_op().free_nvhost_channel(ch); +} +static void nvhost_free_resources(struct nvhost_master *host) +{ kfree(host->intr.syncpt); host->intr.syncpt = 0; } -static int __devinit nvhost_init_chip_support(struct nvhost_master *host) +static int __devinit nvhost_alloc_resources(struct nvhost_master *host) { int err; - switch (tegra_get_chipid()) { - case TEGRA_CHIPID_TEGRA2: - err = nvhost_init_t20_support(host); - break; - - case TEGRA_CHIPID_TEGRA3: - err = nvhost_init_t30_support(host); - break; - default: - return -ENODEV; - } + err = nvhost_init_chip_support(host); if (err) return err; - /* allocate items sized in chip specific support init */ - host->channels = kzalloc(sizeof(struct nvhost_channel) * - host->nb_channels, GFP_KERNEL); - host->intr.syncpt = kzalloc(sizeof(struct nvhost_intr_syncpt) * host->syncpt.nb_pts, GFP_KERNEL); - if (!(host->channels && host->intr.syncpt)) { + if (!host->intr.syncpt) { /* frees happen in the support removal phase */ return -ENOMEM; } @@ -427,13 +416,12 @@ struct nvhost_device tegra_grhost_device = { .id = -1, .resource = nvhost_resources, .num_resources = ARRAY_SIZE(nvhost_resources), - .finalize_poweron = power_on_host, - .prepare_poweroff = power_off_host, .clocks = {{"host1x", UINT_MAX}, {} }, NVHOST_MODULE_NO_POWERGATE_IDS, }; -static int __devinit nvhost_probe(struct nvhost_device *dev) +static int __devinit nvhost_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { struct nvhost_master *host; struct resource *regs, *intr0, *intr1; @@ -474,7 +462,7 @@ static int __devinit nvhost_probe(struct nvhost_device *dev) goto fail; } - err = nvhost_init_chip_support(host); + err = nvhost_alloc_resources(host); if (err) { dev_err(&dev->dev, "failed to init chip support\n"); goto fail; @@ -516,7 +504,7 @@ static int __devinit nvhost_probe(struct nvhost_device *dev) return 0; fail: - nvhost_remove_chip_support(host); + nvhost_free_resources(host); if (host->nvmap) nvmap_client_put(host->nvmap); kfree(host); @@ -528,7 +516,7 @@ static int __exit nvhost_remove(struct nvhost_device *dev) struct nvhost_master *host = nvhost_get_drvdata(dev); nvhost_intr_deinit(&host->intr); nvhost_syncpt_deinit(&host->syncpt); - nvhost_remove_chip_support(host); + nvhost_free_resources(host); return 0; } @@ -557,7 +545,9 @@ static struct nvhost_driver nvhost_driver = { .driver = { .owner = THIS_MODULE, .name = DRIVER_NAME - } + }, + .finalize_poweron = power_on_host, + .prepare_poweroff = power_off_host, }; static int __init nvhost_mod_init(void) diff --git a/drivers/video/tegra/host/dev.h b/drivers/video/tegra/host/dev.h index 74d7e16fc272..41c4fb59b264 100644 --- a/drivers/video/tegra/host/dev.h +++ b/drivers/video/tegra/host/dev.h @@ -21,19 +21,16 @@ #ifndef __NVHOST_DEV_H #define __NVHOST_DEV_H -#include "nvhost_acm.h" +#include <linux/cdev.h> #include "nvhost_syncpt.h" #include "nvhost_intr.h" -#include "nvhost_channel.h" #include "chip_support.h" #define TRACE_MAX_LENGTH 128U #define IFACE_NAME "nvhost" -extern int nvhost_major; -extern int nvhost_minor; - struct nvhost_hwctx; +struct nvhost_channel; struct nvhost_master { void __iomem *aperture; @@ -46,11 +43,6 @@ struct nvhost_master { struct nvmap_client *nvmap; struct nvhost_intr intr; struct nvhost_device *dev; - struct nvhost_channel *channels; - u32 nb_channels; - - struct nvhost_chip_support op; - atomic_t clientid; }; @@ -59,9 +51,9 @@ extern struct nvhost_master *nvhost; void nvhost_debug_init(struct nvhost_master *master); void nvhost_debug_dump(struct nvhost_master *master); -#define host_device_op(host) (host->op.nvhost_dev) - struct nvhost_device *nvhost_get_device(char *name); +struct nvhost_channel *nvhost_alloc_channel(int index); +void nvhost_free_channel(struct nvhost_channel *ch); extern pid_t nvhost_debug_null_kickoff_pid; diff --git a/drivers/video/tegra/host/dsi/dsi.c b/drivers/video/tegra/host/dsi/dsi.c index 0e49f591574d..87da8a6f1b8a 100644 --- a/drivers/video/tegra/host/dsi/dsi.c +++ b/drivers/video/tegra/host/dsi/dsi.c @@ -21,7 +21,8 @@ #include "dev.h" #include "bus_client.h" -static int dsi_probe(struct nvhost_device *dev) +static int dsi_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { return nvhost_client_device_init(dev); } diff --git a/drivers/video/tegra/host/gr2d/gr2d.c b/drivers/video/tegra/host/gr2d/gr2d.c index f88eb72e0a40..c91a3aa23714 100644 --- a/drivers/video/tegra/host/gr2d/gr2d.c +++ b/drivers/video/tegra/host/gr2d/gr2d.c @@ -21,7 +21,8 @@ #include "dev.h" #include "bus_client.h" -static int __devinit gr2d_probe(struct nvhost_device *dev) +static int __devinit gr2d_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { return nvhost_client_device_init(dev); } diff --git a/drivers/video/tegra/host/gr3d/gr3d.c b/drivers/video/tegra/host/gr3d/gr3d.c index f387d54e585e..b621a47bf650 100644 --- a/drivers/video/tegra/host/gr3d/gr3d.c +++ b/drivers/video/tegra/host/gr3d/gr3d.c @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <linux/slab.h> #include "t20/t20.h" @@ -28,7 +28,13 @@ #include "nvhost_hwctx.h" #include "dev.h" #include "gr3d.h" +#include "gr3d_t20.h" +#include "gr3d_t30.h" +#include "scale3d.h" #include "bus_client.h" +#include "nvhost_channel.h" + +#include <mach/hardware.h> #ifndef TEGRA_POWERGATE_3D1 #define TEGRA_POWERGATE_3D1 -1 @@ -68,7 +74,6 @@ void nvhost_3dctx_restore_end(struct host1x_hwctx_handler *p, u32 *ptr) } /*** ctx3d ***/ - struct host1x_hwctx *nvhost_3dctx_alloc_common(struct host1x_hwctx_handler *p, struct nvhost_channel *ch, bool map_restore) { @@ -150,8 +155,75 @@ int nvhost_gr3d_prepare_power_off(struct nvhost_device *dev) return host1x_save_context(dev, NVSYNCPT_3D); } -static int __devinit gr3d_probe(struct nvhost_device *dev) +enum gr3d_ip_ver { + gr3d_01, + gr3d_02, +}; + +struct gr3d_desc { + void (*finalize_poweron)(struct nvhost_device *dev); + void (*busy)(struct nvhost_device *); + void (*idle)(struct nvhost_device *); + void (*suspend_ndev)(struct nvhost_device *); + void (*init)(struct nvhost_device *dev); + void (*deinit)(struct nvhost_device *dev); + int (*prepare_poweroff)(struct nvhost_device *dev); + struct nvhost_hwctx_handler *(*alloc_hwctx_handler)(u32 syncpt, + u32 waitbase, struct nvhost_channel *ch); +}; + +static const struct gr3d_desc gr3d[] = { + [gr3d_01] = { + .finalize_poweron = NULL, + .busy = NULL, + .idle = NULL, + .suspend_ndev = NULL, + .init = NULL, + .deinit = NULL, + .prepare_poweroff = nvhost_gr3d_prepare_power_off, + .alloc_hwctx_handler = nvhost_gr3d_t20_ctxhandler_init, + }, + [gr3d_02] = { + .finalize_poweron = NULL, + .busy = nvhost_scale3d_notify_busy, + .idle = nvhost_scale3d_notify_idle, + .suspend_ndev = nvhost_scale3d_suspend, + .init = nvhost_scale3d_init, + .deinit = nvhost_scale3d_deinit, + .prepare_poweroff = nvhost_gr3d_prepare_power_off, + .alloc_hwctx_handler = nvhost_gr3d_t30_ctxhandler_init, + }, +}; + +static struct nvhost_device_id gr3d_id[] = { + { "gr3d01", gr3d_01 }, + { "gr3d02", gr3d_02 }, + { }, +}; + +MODULE_DEVICE_TABLE(nvhost, gr3d_id); + +static int __devinit gr3d_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { + int index = 0; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); + + index = id_table->driver_data; + + drv->finalize_poweron = gr3d[index].finalize_poweron; + drv->busy = gr3d[index].busy; + drv->idle = gr3d[index].idle; + drv->suspend_ndev = gr3d[index].suspend_ndev; + drv->init = gr3d[index].init; + drv->deinit = gr3d[index].deinit; + drv->prepare_poweroff = gr3d[index].prepare_poweroff; + drv->alloc_hwctx_handler = gr3d[index].alloc_hwctx_handler; + + /* reset device name so that consistent device name can be + * found in clock tree */ + dev->name = "gr3d"; + return nvhost_client_device_init(dev); } @@ -184,7 +256,8 @@ static struct nvhost_driver gr3d_driver = { .driver = { .owner = THIS_MODULE, .name = "gr3d", - } + }, + .id_table = gr3d_id, }; static int __init gr3d_init(void) @@ -196,8 +269,10 @@ static int __init gr3d_init(void) return -ENXIO; err = nvhost_device_register(gr3d_device); - if (err) + if (err) { + pr_err("Could not register 3D device\n"); return err; + } return nvhost_driver_register(&gr3d_driver); } diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.c b/drivers/video/tegra/host/gr3d/gr3d_t20.c index 9ca990f89077..c0efac03b882 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t20.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t20.c @@ -19,6 +19,7 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" #include "dev.h" #include "host1x/host1x_channel.h" #include "host1x/host1x_hardware.h" @@ -136,8 +137,9 @@ static void save_push_v0(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) struct host1x_hwctx_handler *p = host1x_hwctx_handler(ctx); nvhost_cdma_push_gather(cdma, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, + nvhost_get_host(nctx->channel->dev)->nvmap, + p->save_buf->handle, + 0, nvhost_opcode_gather(p->save_size), p->save_phys); } diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.c b/drivers/video/tegra/host/gr3d/gr3d_t30.c index 8ca6b7b44b9e..93d98dfa645c 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t30.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t30.c @@ -19,6 +19,8 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" +#include "nvhost_cdma.h" #include "dev.h" #include "host1x/host1x_hardware.h" #include "host1x/host1x_syncpt.h" @@ -71,7 +73,6 @@ static const struct hwctx_reginfo ctxsave_regs_3d_global[] = { HWCTX_REGINFO(0xa02, 10, DIRECT), HWCTX_REGINFO(0xb04, 1, DIRECT), HWCTX_REGINFO(0xb06, 13, DIRECT), - HWCTX_REGINFO(0xe42, 2, DIRECT), /* HW bug workaround */ }; static const struct hwctx_reginfo ctxsave_regs_3d_perset[] = { @@ -143,8 +144,9 @@ static void save_push_v1(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma) ctx->restore_phys); /* gather the save buffer */ nvhost_cdma_push_gather(cdma, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, - (void *)NVHOST_CDMA_PUSH_GATHER_CTXSAVE, + nvhost_get_host(nctx->channel->dev)->nvmap, + p->save_buf->handle, + 0, nvhost_opcode_gather(p->save_size), p->save_phys); } diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.c b/drivers/video/tegra/host/host1x/host1x_cdma.c index cdd6026718b1..fcb1f05f0025 100644 --- a/drivers/video/tegra/host/host1x/host1x_cdma.c +++ b/drivers/video/tegra/host/host1x/host1x_cdma.c @@ -19,7 +19,9 @@ */ #include <linux/slab.h> +#include "nvhost_acm.h" #include "nvhost_cdma.h" +#include "nvhost_channel.h" #include "dev.h" #include "host1x_hardware.h" @@ -66,8 +68,8 @@ static int push_buffer_init(struct push_buffer *pb) pb->phys = 0; pb->nvmap = NULL; - BUG_ON(!cdma_pb_op(cdma).reset); - cdma_pb_op(cdma).reset(pb); + BUG_ON(!cdma_pb_op().reset); + cdma_pb_op().reset(pb); /* allocate and map pushbuffer memory */ pb->mem = nvmap_alloc(nvmap, PUSH_BUFFER_SIZE + 4, 32, @@ -101,7 +103,7 @@ static int push_buffer_init(struct push_buffer *pb) return 0; fail: - cdma_pb_op(cdma).destroy(pb); + cdma_pb_op().destroy(pb); return -ENOMEM; } @@ -191,96 +193,25 @@ static u32 push_buffer_putptr(struct push_buffer *pb) */ /** - * Init timeout and syncpt incr buffer resources + * Init timeout resources */ static int cdma_timeout_init(struct nvhost_cdma *cdma, u32 syncpt_id) { - struct nvhost_master *dev = cdma_to_dev(cdma); - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); - struct syncpt_buffer *sb = &cdma->syncpt_buffer; - struct nvhost_channel *ch = cdma_to_channel(cdma); - u32 i = 0; - if (syncpt_id == NVSYNCPT_INVALID) return -EINVAL; - /* allocate and map syncpt incr memory */ - sb->mem = nvmap_alloc(nvmap, - (SYNCPT_INCR_BUFFER_SIZE_WORDS * sizeof(u32)), 32, - NVMAP_HANDLE_WRITE_COMBINE, 0); - if (IS_ERR_OR_NULL(sb->mem)) { - sb->mem = NULL; - goto fail; - } - sb->mapped = nvmap_mmap(sb->mem); - if (sb->mapped == NULL) - goto fail; - - /* pin syncpt buffer and get physical address */ - sb->phys = nvmap_pin(nvmap, sb->mem); - if (sb->phys >= 0xfffff000) { - sb->phys = 0; - goto fail; - } - - dev_dbg(&dev->dev->dev, "%s: SYNCPT_INCR buffer at 0x%x\n", - __func__, sb->phys); - - sb->words_per_incr = (syncpt_id == NVSYNCPT_3D) ? 5 : 3; - sb->incr_per_buffer = (SYNCPT_INCR_BUFFER_SIZE_WORDS / - sb->words_per_incr); - - /* init buffer with SETCL and INCR_SYNCPT methods */ - while (i < sb->incr_per_buffer) { - sb->mapped[i++] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, - 0, 0); - sb->mapped[i++] = nvhost_opcode_imm_incr_syncpt( - NV_SYNCPT_IMMEDIATE, - syncpt_id); - if (syncpt_id == NVSYNCPT_3D) { - /* also contains base increments */ - sb->mapped[i++] = nvhost_opcode_nonincr( - NV_CLASS_HOST_INCR_SYNCPT_BASE, - 1); - sb->mapped[i++] = nvhost_class_host_incr_syncpt_base( - NVWAITBASE_3D, 1); - } - sb->mapped[i++] = nvhost_opcode_setclass(ch->dev->class, - 0, 0); - } - wmb(); - INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler); cdma->timeout.initialized = true; return 0; -fail: - cdma_op(cdma).timeout_destroy(cdma); - return -ENOMEM; } /** - * Clean up timeout syncpt buffer resources + * Clean up timeout resources */ static void cdma_timeout_destroy(struct nvhost_cdma *cdma) { - struct nvmap_client *nvmap = cdma_to_nvmap(cdma); - struct syncpt_buffer *sb = &cdma->syncpt_buffer; - - if (sb->mapped) - nvmap_munmap(sb->mem, sb->mapped); - - if (sb->phys != 0) - nvmap_unpin(nvmap, sb->mem); - - if (sb->mem) - nvmap_free(nvmap, sb->mem); - - sb->mem = NULL; - sb->mapped = NULL; - sb->phys = 0; - if (cdma->timeout.initialized) cancel_delayed_work(&cdma->timeout.wq); cdma->timeout.initialized = false; @@ -324,73 +255,6 @@ static void cdma_timeout_cpu_incr(struct nvhost_cdma *cdma, u32 getptr, } /** - * This routine is called at the point we transition back into a timed - * ctx. The syncpts are incremented via pushbuffer with a flag indicating - * whether there's a CTXSAVE that should be still executed (for the - * preceding HW ctx). - */ -static void cdma_timeout_pb_incr(struct nvhost_cdma *cdma, u32 getptr, - u32 syncpt_incrs, u32 nr_slots, - bool exec_ctxsave) -{ - struct nvhost_master *dev = cdma_to_dev(cdma); - struct syncpt_buffer *sb = &cdma->syncpt_buffer; - struct push_buffer *pb = &cdma->push_buffer; - struct host1x_hwctx *hwctx = to_host1x_hwctx(cdma->timeout.ctx); - u32 getidx, *p; - - /* should have enough slots to incr to desired count */ - BUG_ON(syncpt_incrs > (nr_slots * sb->incr_per_buffer)); - - getidx = getptr - pb->phys; - if (exec_ctxsave) { - /* don't disrupt the CTXSAVE of a good/non-timed out ctx */ - nr_slots -= hwctx->save_slots; - syncpt_incrs -= hwctx->save_incrs; - - getidx += (hwctx->save_slots * 8); - getidx &= (PUSH_BUFFER_SIZE - 1); - - dev_dbg(&dev->dev->dev, - "%s: exec CTXSAVE of prev ctx (slots %d, incrs %d)\n", - __func__, nr_slots, syncpt_incrs); - } - - while (syncpt_incrs) { - u32 incrs, count; - - /* GATHER count are incrs * number of DWORDs per incr */ - incrs = min(syncpt_incrs, sb->incr_per_buffer); - count = incrs * sb->words_per_incr; - - p = (u32 *)((u32)pb->mapped + getidx); - *(p++) = nvhost_opcode_gather(count); - *(p++) = sb->phys; - - dev_dbg(&dev->dev->dev, - "%s: GATHER at 0x%x, from 0x%x, dcount = %d\n", - __func__, - pb->phys + getidx, sb->phys, - (incrs * sb->words_per_incr)); - - syncpt_incrs -= incrs; - getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); - nr_slots--; - } - - /* NOP remaining slots */ - while (nr_slots--) { - p = (u32 *)((u32)pb->mapped + getidx); - *(p++) = NVHOST_OPCODE_NOOP; - *(p++) = NVHOST_OPCODE_NOOP; - dev_dbg(&dev->dev->dev, "%s: NOP at 0x%x\n", - __func__, pb->phys + getidx); - getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); - } - wmb(); -} - -/** * Start channel DMA */ static void cdma_start(struct nvhost_cdma *cdma) @@ -400,8 +264,8 @@ static void cdma_start(struct nvhost_cdma *cdma) if (cdma->running) return; - BUG_ON(!cdma_pb_op(cdma).putptr); - cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + BUG_ON(!cdma_pb_op().putptr); + cdma->last_put = cdma_pb_op().putptr(&cdma->push_buffer); writel(host1x_channel_dmactrl(true, false, false), chan_regs + HOST1X_CHANNEL_DMACTRL); @@ -435,8 +299,8 @@ static void cdma_timeout_restart(struct nvhost_cdma *cdma, u32 getptr) if (cdma->running) return; - BUG_ON(!cdma_pb_op(cdma).putptr); - cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + BUG_ON(!cdma_pb_op().putptr); + cdma->last_put = cdma_pb_op().putptr(&cdma->push_buffer); writel(host1x_channel_dmactrl(true, false, false), chan_regs + HOST1X_CHANNEL_DMACTRL); @@ -475,9 +339,9 @@ static void cdma_timeout_restart(struct nvhost_cdma *cdma, u32 getptr) static void cdma_kick(struct nvhost_cdma *cdma) { u32 put; - BUG_ON(!cdma_pb_op(cdma).putptr); + BUG_ON(!cdma_pb_op().putptr); - put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + put = cdma_pb_op().putptr(&cdma->push_buffer); if (put != cdma->last_put) { void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; @@ -629,37 +493,36 @@ static void cdma_timeout_handler(struct work_struct *work) "%s: timeout: %d (%s) ctx 0x%p, HW thresh %d, done %d\n", __func__, cdma->timeout.syncpt_id, - syncpt_op(sp).name(sp, cdma->timeout.syncpt_id), + syncpt_op().name(sp, cdma->timeout.syncpt_id), cdma->timeout.ctx, syncpt_val, cdma->timeout.syncpt_val); /* stop HW, resetting channel/module */ - cdma_op(cdma).timeout_teardown_begin(cdma); + cdma_op().timeout_teardown_begin(cdma); nvhost_cdma_update_sync_queue(cdma, sp, &dev->dev->dev); mutex_unlock(&cdma->lock); } -int host1x_init_cdma_support(struct nvhost_master *host) +int host1x_init_cdma_support(struct nvhost_chip_support *op) { - host->op.cdma.start = cdma_start; - host->op.cdma.stop = cdma_stop; - host->op.cdma.kick = cdma_kick; - - host->op.cdma.timeout_init = cdma_timeout_init; - host->op.cdma.timeout_destroy = cdma_timeout_destroy; - host->op.cdma.timeout_teardown_begin = cdma_timeout_teardown_begin; - host->op.cdma.timeout_teardown_end = cdma_timeout_teardown_end; - host->op.cdma.timeout_cpu_incr = cdma_timeout_cpu_incr; - host->op.cdma.timeout_pb_incr = cdma_timeout_pb_incr; - - host->op.push_buffer.reset = push_buffer_reset; - host->op.push_buffer.init = push_buffer_init; - host->op.push_buffer.destroy = push_buffer_destroy; - host->op.push_buffer.push_to = push_buffer_push_to; - host->op.push_buffer.pop_from = push_buffer_pop_from; - host->op.push_buffer.space = push_buffer_space; - host->op.push_buffer.putptr = push_buffer_putptr; + op->cdma.start = cdma_start; + op->cdma.stop = cdma_stop; + op->cdma.kick = cdma_kick; + + op->cdma.timeout_init = cdma_timeout_init; + op->cdma.timeout_destroy = cdma_timeout_destroy; + op->cdma.timeout_teardown_begin = cdma_timeout_teardown_begin; + op->cdma.timeout_teardown_end = cdma_timeout_teardown_end; + op->cdma.timeout_cpu_incr = cdma_timeout_cpu_incr; + + op->push_buffer.reset = push_buffer_reset; + op->push_buffer.init = push_buffer_init; + op->push_buffer.destroy = push_buffer_destroy; + op->push_buffer.push_to = push_buffer_push_to; + op->push_buffer.pop_from = push_buffer_pop_from; + op->push_buffer.space = push_buffer_space; + op->push_buffer.putptr = push_buffer_putptr; return 0; } diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.h b/drivers/video/tegra/host/host1x/host1x_cdma.h index 60909236a7ca..374097272d3f 100644 --- a/drivers/video/tegra/host/host1x/host1x_cdma.h +++ b/drivers/video/tegra/host/host1x/host1x_cdma.h @@ -36,6 +36,6 @@ * and replaces the original timed out contexts GATHER slots */ #define SYNCPT_INCR_BUFFER_SIZE_WORDS (4096 / sizeof(u32)) -int host1x_init_cdma_support(struct nvhost_master *); +int host1x_init_cdma_support(struct nvhost_chip_support *); #endif diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c index b16a34f416ab..8c4a7a5c74ad 100644 --- a/drivers/video/tegra/host/host1x/host1x_channel.c +++ b/drivers/video/tegra/host/host1x/host1x_channel.c @@ -20,6 +20,8 @@ #include "nvhost_channel.h" #include "dev.h" +#include "nvhost_acm.h" +#include "nvhost_job.h" #include "nvhost_hwctx.h" #include <trace/events/nvhost.h> #include <linux/slab.h> @@ -142,6 +144,7 @@ static void submit_ctxrestore(struct nvhost_job *job) nvhost_cdma_push_gather(&ch->cdma, host->nvmap, nvmap_ref_to_handle(ctx->restore), + 0, nvhost_opcode_gather(ctx->restore_size), ctx->restore_phys); @@ -179,12 +182,14 @@ void submit_nullkickoff(struct nvhost_job *job, int user_syncpt_incrs) void submit_gathers(struct nvhost_job *job) { /* push user gathers */ - int i = 0; - for ( ; i < job->num_gathers; i++) { + int i; + for (i = 0 ; i < job->num_gathers; i++) { u32 op1 = nvhost_opcode_gather(job->gathers[i].words); u32 op2 = job->gathers[i].mem; nvhost_cdma_push_gather(&job->ch->cdma, - job->nvmap, job->unpins[i/2], + job->nvmap, + nvmap_id_to_handle(job->gathers[i].mem_id), + job->gathers[i].offset, op1, op2); } } @@ -198,6 +203,7 @@ int host1x_channel_submit(struct nvhost_job *job) u32 syncval; int err; void *completed_waiter = NULL, *ctxsave_waiter = NULL; + struct nvhost_driver *drv = to_nvhost_driver(ch->dev->dev.driver); /* Bail out on timed out contexts */ if (job->hwctx && job->hwctx->has_timedout) @@ -205,8 +211,8 @@ int host1x_channel_submit(struct nvhost_job *job) /* Turn on the client module and host1x */ nvhost_module_busy(ch->dev); - if (ch->dev->busy) - ch->dev->busy(ch->dev); + if (drv->busy) + drv->busy(ch->dev); /* before error checks, return current max */ prev_max = job->syncpt_end = @@ -544,6 +550,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) void *ref; void *ctx_waiter = NULL, *wakeup_waiter = NULL; struct nvhost_job *job; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); ctx_waiter = nvhost_intr_alloc_waiter(); wakeup_waiter = nvhost_intr_alloc_waiter(); @@ -552,8 +559,8 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) goto done; } - if (dev->busy) - dev->busy(dev); + if (drv->busy) + drv->busy(dev); mutex_lock(&ch->submitlock); hwctx_to_save = ch->cur_ctx; diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c index 1a1d764bbd63..76483d82528b 100644 --- a/drivers/video/tegra/host/host1x/host1x_debug.c +++ b/drivers/video/tegra/host/host1x/host1x_debug.c @@ -25,10 +25,10 @@ #include "dev.h" #include "debug.h" +#include "host1x_hardware.h" #include "nvhost_cdma.h" +#include "nvhost_channel.h" #include "../../nvmap/nvmap.h" - -#include "host1x_hardware.h" #include "host1x_cdma.h" #define NVHOST_DEBUG_MAX_PAGE_OFFSET 102400 @@ -174,11 +174,6 @@ static void show_channel_gather(struct output *o, u32 addr, phys_addr_t pin_addr; int state, count, i; - if ((u32)nvmap->handle == NVHOST_CDMA_PUSH_GATHER_CTXSAVE) { - nvhost_debug_output(o, "[context save]\n"); - return; - } - if (!nvmap->handle || !nvmap->client || atomic_read(&nvmap->handle->ref) < 1) { nvhost_debug_output(o, "[already deallocated]\n"); @@ -394,11 +389,11 @@ static void t20_debug_show_mlocks(struct nvhost_master *m, struct output *o) nvhost_debug_output(o, "\n"); } -int nvhost_init_t20_debug_support(struct nvhost_master *host) +int nvhost_init_t20_debug_support(struct nvhost_chip_support *op) { - host->op.debug.show_channel_cdma = t20_debug_show_channel_cdma; - host->op.debug.show_channel_fifo = t20_debug_show_channel_fifo; - host->op.debug.show_mlocks = t20_debug_show_mlocks; + op->debug.show_channel_cdma = t20_debug_show_channel_cdma; + op->debug.show_channel_fifo = t20_debug_show_channel_fifo; + op->debug.show_mlocks = t20_debug_show_mlocks; return 0; } diff --git a/drivers/video/tegra/host/host1x/host1x_hwctx.h b/drivers/video/tegra/host/host1x/host1x_hwctx.h index 7587642d0e14..b5046c461d9d 100644 --- a/drivers/video/tegra/host/host1x/host1x_hwctx.h +++ b/drivers/video/tegra/host/host1x/host1x_hwctx.h @@ -24,6 +24,7 @@ #define __NVHOST_HOST1X_HWCTX_H #include <linux/kref.h> +#include "nvhost_hwctx.h" struct nvhost_hwctx_handler; struct nvhost_channel; diff --git a/drivers/video/tegra/host/host1x/host1x_intr.c b/drivers/video/tegra/host/host1x/host1x_intr.c index 47e984e2943e..2b611faaa708 100644 --- a/drivers/video/tegra/host/host1x/host1x_intr.c +++ b/drivers/video/tegra/host/host1x/host1x_intr.c @@ -3,6 +3,7 @@ * * Tegra Graphics Host Interrupt Management * + * Copyright (C) 2010 Google, Inc. * Copyright (c) 2010-2012, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify it @@ -20,18 +21,69 @@ #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/io.h> +#include <asm/mach/irq.h> #include "nvhost_intr.h" #include "dev.h" #include "host1x_hardware.h" - /*** HW host sync management ***/ +static void syncpt_thresh_mask(struct irq_data *data) +{ + (void)data; +} + +static void syncpt_thresh_unmask(struct irq_data *data) +{ + (void)data; +} + +static void syncpt_thresh_cascade(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *sync_regs = irq_desc_get_handler_data(desc); + unsigned long reg; + int id; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + reg = readl(sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); + + for_each_set_bit(id, ®, 32) + generic_handle_irq(id + INT_SYNCPT_THRESH_BASE); + + chained_irq_exit(chip, desc); +} + +static struct irq_chip syncpt_thresh_irq = { + .name = "syncpt", + .irq_mask = syncpt_thresh_mask, + .irq_unmask = syncpt_thresh_unmask +}; + static void t20_intr_init_host_sync(struct nvhost_intr *intr) { struct nvhost_master *dev = intr_to_dev(intr); void __iomem *sync_regs = dev->sync_aperture; + int i, irq; + + writel(0xffffffffUL, + sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); + writel(0xffffffffUL, + sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); + + for (i = 0; i < INT_SYNCPT_THRESH_NR; i++) { + irq = INT_SYNCPT_THRESH_BASE + i; + irq_set_chip_and_handler(irq, &syncpt_thresh_irq, + handle_simple_irq); + irq_set_chip_data(irq, sync_regs); + set_irq_flags(irq, IRQF_VALID); + } + irq_set_chained_handler(INT_HOST1X_MPCORE_SYNCPT, + syncpt_thresh_cascade); + irq_set_handler_data(INT_HOST1X_MPCORE_SYNCPT, sync_regs); /* disable the ip_busy_timeout. this prevents write drops, etc. * there's no real way to recover from a hung client anyway. */ @@ -198,21 +250,16 @@ static int t20_request_syncpt_irq(struct nvhost_intr_syncpt *syncpt) return 0; } -int nvhost_init_t20_intr_support(struct nvhost_master *host) +int nvhost_init_t20_intr_support(struct nvhost_chip_support *op) { - host->op.intr.init_host_sync = t20_intr_init_host_sync; - host->op.intr.set_host_clocks_per_usec = - t20_intr_set_host_clocks_per_usec; - host->op.intr.set_syncpt_threshold = t20_intr_set_syncpt_threshold; - host->op.intr.enable_syncpt_intr = t20_intr_enable_syncpt_intr; - host->op.intr.disable_all_syncpt_intrs = - t20_intr_disable_all_syncpt_intrs; - host->op.intr.request_host_general_irq = - t20_intr_request_host_general_irq; - host->op.intr.free_host_general_irq = - t20_intr_free_host_general_irq; - host->op.intr.request_syncpt_irq = - t20_request_syncpt_irq; + op->intr.init_host_sync = t20_intr_init_host_sync; + op->intr.set_host_clocks_per_usec = t20_intr_set_host_clocks_per_usec; + op->intr.set_syncpt_threshold = t20_intr_set_syncpt_threshold; + op->intr.enable_syncpt_intr = t20_intr_enable_syncpt_intr; + op->intr.disable_all_syncpt_intrs = t20_intr_disable_all_syncpt_intrs; + op->intr.request_host_general_irq = t20_intr_request_host_general_irq; + op->intr.free_host_general_irq = t20_intr_free_host_general_irq; + op->intr.request_syncpt_irq = t20_request_syncpt_irq; return 0; } diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c index b431fa350638..b7d6587acc61 100644 --- a/drivers/video/tegra/host/host1x/host1x_syncpt.c +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c @@ -19,10 +19,14 @@ */ #include <linux/nvhost_ioctl.h> +#include <linux/io.h> +#include <trace/events/nvhost.h> #include "nvhost_syncpt.h" +#include "nvhost_acm.h" #include "dev.h" #include "host1x_syncpt.h" #include "host1x_hardware.h" +#include "chip_support.h" /** * Write the current syncpoint value back to hw. @@ -122,6 +126,8 @@ static int t20_syncpt_wait_check(struct nvhost_syncpt *sp, u32 override; BUG_ON(wait->syncpt_id >= NV_HOST1X_SYNCPT_NB_PTS); + trace_nvhost_syncpt_wait_check(wait->mem, wait->offset, + wait->syncpt_id, wait->thresh); if (nvhost_syncpt_is_expired(sp, wait->syncpt_id, wait->thresh)) { /* @@ -135,7 +141,7 @@ static int t20_syncpt_wait_check(struct nvhost_syncpt *sp, dev_dbg(&syncpt_to_dev(sp)->dev->dev, "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n", wait->syncpt_id, - syncpt_op(sp).name(sp, wait->syncpt_id), + syncpt_op().name(sp, wait->syncpt_id), wait->thresh, nvhost_syncpt_read_min(sp, wait->syncpt_id)); @@ -189,7 +195,7 @@ static void t20_syncpt_debug(struct nvhost_syncpt *sp) continue; dev_info(&syncpt_to_dev(sp)->dev->dev, "id %d (%s) min %d max %d\n", - i, syncpt_op(sp).name(sp, i), + i, syncpt_op().name(sp, i), min, max); } @@ -223,23 +229,23 @@ static void syncpt_mutex_unlock(struct nvhost_syncpt *sp, writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); } -int host1x_init_syncpt_support(struct nvhost_master *host) +int host1x_init_syncpt_support(struct nvhost_master *host, + struct nvhost_chip_support *op) { - host->sync_aperture = host->aperture + (NV_HOST1X_CHANNEL0_BASE + HOST1X_CHANNEL_SYNC_REG_BASE); - host->op.syncpt.reset = t20_syncpt_reset; - host->op.syncpt.reset_wait_base = t20_syncpt_reset_wait_base; - host->op.syncpt.read_wait_base = t20_syncpt_read_wait_base; - host->op.syncpt.update_min = t20_syncpt_update_min; - host->op.syncpt.cpu_incr = t20_syncpt_cpu_incr; - host->op.syncpt.wait_check = t20_syncpt_wait_check; - host->op.syncpt.debug = t20_syncpt_debug; - host->op.syncpt.name = t20_syncpt_name; - host->op.syncpt.mutex_try_lock = syncpt_mutex_try_lock; - host->op.syncpt.mutex_unlock = syncpt_mutex_unlock; + op->syncpt.reset = t20_syncpt_reset; + op->syncpt.reset_wait_base = t20_syncpt_reset_wait_base; + op->syncpt.read_wait_base = t20_syncpt_read_wait_base; + op->syncpt.update_min = t20_syncpt_update_min; + op->syncpt.cpu_incr = t20_syncpt_cpu_incr; + op->syncpt.wait_check = t20_syncpt_wait_check; + op->syncpt.debug = t20_syncpt_debug; + op->syncpt.name = t20_syncpt_name; + op->syncpt.mutex_try_lock = syncpt_mutex_try_lock; + op->syncpt.mutex_unlock = syncpt_mutex_unlock; host->syncpt.nb_pts = NV_HOST1X_SYNCPT_NB_PTS; host->syncpt.nb_bases = NV_HOST1X_SYNCPT_NB_BASES; diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.h b/drivers/video/tegra/host/host1x/host1x_syncpt.h index 0d263dc92ed5..1e94a2b846eb 100644 --- a/drivers/video/tegra/host/host1x/host1x_syncpt.h +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.h @@ -71,7 +71,10 @@ #define NVWAITBASE_MPE (4) struct nvhost_master; +struct nvhost_chip_support; + int host1x_init_syncpt(struct nvhost_master *host); -int host1x_init_syncpt_support(struct nvhost_master *host); +int host1x_init_syncpt_support(struct nvhost_master *host, + struct nvhost_chip_support *op); #endif diff --git a/drivers/video/tegra/host/isp/isp.c b/drivers/video/tegra/host/isp/isp.c index 9044d40b8574..ae9d7eb09365 100644 --- a/drivers/video/tegra/host/isp/isp.c +++ b/drivers/video/tegra/host/isp/isp.c @@ -25,7 +25,8 @@ #include "dev.h" #include "bus_client.h" -static int __devinit isp_probe(struct nvhost_device *dev) +static int __devinit isp_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { int err = 0; diff --git a/drivers/video/tegra/host/mpe/mpe.c b/drivers/video/tegra/host/mpe/mpe.c index 36d1d6f26682..d8c9da7e9a76 100644 --- a/drivers/video/tegra/host/mpe/mpe.c +++ b/drivers/video/tegra/host/mpe/mpe.c @@ -19,6 +19,7 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" #include "dev.h" #include "host1x/host1x_hardware.h" #include "host1x/host1x_channel.h" @@ -30,6 +31,7 @@ #include <linux/resource.h> #include <mach/iomap.h> +#include <mach/hardware.h> #include "bus_client.h" @@ -459,6 +461,7 @@ static struct nvhost_hwctx *ctxmpe_alloc(struct nvhost_hwctx_handler *h, ctx->hwctx.valid = false; ctx->save_incrs = 3; ctx->save_thresh = 2; + ctx->save_slots = p->save_slots; ctx->restore_phys = nvmap_pin(nvmap, ctx->restore); ctx->restore_size = restore_size; ctx->restore_incrs = 1; @@ -497,7 +500,10 @@ static void ctxmpe_save_push(struct nvhost_hwctx *nctx, { struct host1x_hwctx *ctx = to_host1x_hwctx(nctx); struct host1x_hwctx_handler *h = host1x_hwctx_handler(ctx); - nvhost_cdma_push(cdma, + nvhost_cdma_push_gather(cdma, + nvhost_get_host(nctx->channel->dev)->nvmap, + h->save_buf->handle, + 0, nvhost_opcode_gather(h->save_size), h->save_phys); } @@ -528,9 +534,8 @@ static void ctxmpe_save_service(struct nvhost_hwctx *nctx) h->syncpt); } -struct nvhost_hwctx_handler *nvhost_mpe_ctxhandler_init( - u32 syncpt, u32 waitbase, - struct nvhost_channel *ch) +struct nvhost_hwctx_handler *nvhost_mpe_ctxhandler_init(u32 syncpt, + u32 waitbase, struct nvhost_channel *ch) { struct nvmap_client *nvmap; u32 *save_ptr; @@ -562,6 +567,7 @@ struct nvhost_hwctx_handler *nvhost_mpe_ctxhandler_init( } p->save_phys = nvmap_pin(nvmap, p->save_buf); + p->save_slots = 1; setup_save(p, save_ptr); @@ -579,9 +585,51 @@ int nvhost_mpe_prepare_power_off(struct nvhost_device *dev) return host1x_save_context(dev, NVSYNCPT_MPE); } -static int __devinit mpe_probe(struct nvhost_device *dev) +enum mpe_ip_ver { + mpe_01, + mpe_02, +}; + +struct mpe_desc { + int (*prepare_poweroff)(struct nvhost_device *dev); + struct nvhost_hwctx_handler *(*alloc_hwctx_handler)(u32 syncpt, + u32 waitbase, struct nvhost_channel *ch); +}; + +static const struct mpe_desc mpe[] = { + [mpe_01] = { + .prepare_poweroff = nvhost_mpe_prepare_power_off, + .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, + }, + [mpe_02] = { + .prepare_poweroff = nvhost_mpe_prepare_power_off, + .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, + }, +}; + +static struct nvhost_device_id mpe_id[] = { + { "mpe01", mpe_01 }, + { "mpe02", mpe_02 }, + { }, +}; + +MODULE_DEVICE_TABLE(nvhost, mpe_id); + +static int __devinit mpe_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { int err = 0; + int index = 0; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); + + index = id_table->driver_data; + + drv->prepare_poweroff = mpe[index].prepare_poweroff; + drv->alloc_hwctx_handler = mpe[index].alloc_hwctx_handler; + + /* reset device name so that consistent device name can be + * found in clock tree */ + dev->name = "mpe"; err = nvhost_client_device_get_resources(dev); if (err) @@ -626,7 +674,8 @@ static struct nvhost_driver mpe_driver = { .driver = { .owner = THIS_MODULE, .name = "mpe", - } + }, + .id_table = mpe_id, }; static int __init mpe_init(void) diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c index f2a61a9547a0..6d96bd023d81 100644 --- a/drivers/video/tegra/host/nvhost_acm.c +++ b/drivers/video/tegra/host/nvhost_acm.c @@ -55,15 +55,8 @@ static void do_unpowergate_locked(int id) tegra_unpowergate_partition(id); } -void nvhost_module_reset(struct nvhost_device *dev) +static void do_module_reset_locked(struct nvhost_device *dev) { - dev_dbg(&dev->dev, - "%s: asserting %s module reset (id %d, id2 %d)\n", - __func__, dev->name, - dev->powergate_ids[0], dev->powergate_ids[1]); - - mutex_lock(&dev->lock); - /* assert module and mc client reset */ if (dev->powergate_ids[0] != -1) { tegra_powergate_mc_disable(dev->powergate_ids[0]); @@ -89,7 +82,17 @@ void nvhost_module_reset(struct nvhost_device *dev) tegra_periph_reset_deassert(dev->clk[1]); tegra_powergate_mc_enable(dev->powergate_ids[1]); } +} +void nvhost_module_reset(struct nvhost_device *dev) +{ + dev_dbg(&dev->dev, + "%s: asserting %s module reset (id %d, id2 %d)\n", + __func__, dev->name, + dev->powergate_ids[0], dev->powergate_ids[1]); + + mutex_lock(&dev->lock); + do_module_reset_locked(dev); mutex_unlock(&dev->lock); dev_dbg(&dev->dev, "%s: module %s out of reset\n", @@ -108,15 +111,21 @@ static void to_state_clockgated_locked(struct nvhost_device *dev) && dev->can_powergate) { do_unpowergate_locked(dev->powergate_ids[0]); do_unpowergate_locked(dev->powergate_ids[1]); + + if (dev->powerup_reset) + do_module_reset_locked(dev); } dev->powerstate = NVHOST_POWER_STATE_CLOCKGATED; } static void to_state_running_locked(struct nvhost_device *dev) { + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); int prev_state = dev->powerstate; + if (dev->powerstate == NVHOST_POWER_STATE_POWERGATED) to_state_clockgated_locked(dev); + if (dev->powerstate == NVHOST_POWER_STATE_CLOCKGATED) { int i; @@ -129,8 +138,8 @@ static void to_state_running_locked(struct nvhost_device *dev) } if (prev_state == NVHOST_POWER_STATE_POWERGATED - && dev->finalize_poweron) - dev->finalize_poweron(dev); + && drv->finalize_poweron) + drv->finalize_poweron(dev); } dev->powerstate = NVHOST_POWER_STATE_RUNNING; } @@ -142,12 +151,13 @@ static void to_state_running_locked(struct nvhost_device *dev) static int to_state_powergated_locked(struct nvhost_device *dev) { int err = 0; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); - if (dev->prepare_poweroff + if (drv->prepare_poweroff && dev->powerstate != NVHOST_POWER_STATE_POWERGATED) { /* Clock needs to be on in prepare_poweroff */ to_state_running_locked(dev); - err = dev->prepare_poweroff(dev); + err = drv->prepare_poweroff(dev); if (err) return err; } @@ -179,8 +189,10 @@ static void schedule_clockgating_locked(struct nvhost_device *dev) void nvhost_module_busy(struct nvhost_device *dev) { - if (dev->busy) - dev->busy(dev); + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); + + if (drv->busy) + drv->busy(dev); mutex_lock(&dev->lock); cancel_delayed_work(&dev->powerstate_down); @@ -220,6 +232,7 @@ static void powerstate_down_handler(struct work_struct *work) void nvhost_module_idle_mult(struct nvhost_device *dev, int refs) { + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); bool kick = false; mutex_lock(&dev->lock); @@ -234,8 +247,8 @@ void nvhost_module_idle_mult(struct nvhost_device *dev, int refs) if (kick) { wake_up(&dev->idle_wq); - if (dev->idle) - dev->idle(dev); + if (drv->idle) + drv->idle(dev); } } @@ -397,6 +410,7 @@ static int is_module_idle(struct nvhost_device *dev) int nvhost_module_suspend(struct nvhost_device *dev) { int ret; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); ret = wait_event_timeout(dev->idle_wq, is_module_idle(dev), ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT); @@ -411,8 +425,8 @@ int nvhost_module_suspend(struct nvhost_device *dev) to_state_powergated_locked(dev); mutex_unlock(&dev->lock); - if (dev->suspend) - dev->suspend(dev); + if (drv->suspend_ndev) + drv->suspend_ndev(dev); return 0; } @@ -420,9 +434,10 @@ int nvhost_module_suspend(struct nvhost_device *dev) void nvhost_module_deinit(struct nvhost_device *dev) { int i; + struct nvhost_driver *drv = to_nvhost_driver(dev->dev.driver); - if (dev->deinit) - dev->deinit(dev); + if (drv->deinit) + drv->deinit(dev); nvhost_module_suspend(dev); for (i = 0; i < dev->num_clks; i++) diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index 775d761e65c9..a72e18f16ac7 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -19,7 +19,11 @@ */ #include "nvhost_cdma.h" +#include "nvhost_channel.h" +#include "nvhost_job.h" +#include "nvhost_hwctx.h" #include "dev.h" +#include "debug.h" #include <asm/cacheflush.h> #include <linux/slab.h> @@ -65,8 +69,8 @@ static unsigned int cdma_status_locked(struct nvhost_cdma *cdma, return list_empty(&cdma->sync_queue) ? 1 : 0; case CDMA_EVENT_PUSH_BUFFER_SPACE: { struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).space); - return cdma_pb_op(cdma).space(pb); + BUG_ON(!cdma_pb_op().space); + return cdma_pb_op().space(pb); } default: return 0; @@ -92,7 +96,13 @@ unsigned int nvhost_cdma_wait_locked(struct nvhost_cdma *cdma, trace_nvhost_wait_cdma(cdma_to_channel(cdma)->dev->name, event); - BUG_ON(cdma->event != CDMA_EVENT_NONE); + /* If somebody has managed to already start waiting, yield */ + if (cdma->event != CDMA_EVENT_NONE) { + mutex_unlock(&cdma->lock); + schedule(); + mutex_lock(&cdma->lock); + continue; + } cdma->event = event; mutex_unlock(&cdma->lock); @@ -153,7 +163,9 @@ static void update_cdma_locked(struct nvhost_cdma *cdma) struct nvhost_syncpt *sp = &dev->syncpt; struct nvhost_job *job, *n; - BUG_ON(!cdma->running); + /* If CDMA is stopped, queue is cleared and we can return */ + if (!cdma->running) + return; /* * Walk the sync queue, reading the sync point registers as necessary, @@ -181,8 +193,8 @@ static void update_cdma_locked(struct nvhost_cdma *cdma) /* Pop push buffer slots */ if (job->num_slots) { struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).pop_from); - cdma_pb_op(cdma).pop_from(pb, job->num_slots); + BUG_ON(!cdma_pb_op().pop_from); + cdma_pb_op().pop_from(pb, job->num_slots); if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE) signal = true; } @@ -207,7 +219,6 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, { u32 get_restart; u32 syncpt_incrs; - bool exec_ctxsave; struct nvhost_job *job = NULL; u32 syncpt_val; @@ -274,7 +285,7 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, nvhost_job_dump(dev, job); /* safe to use CPU to incr syncpts */ - cdma_op(cdma).timeout_cpu_incr(cdma, + cdma_op().timeout_cpu_incr(cdma, job->first_get, syncpt_incrs, job->syncpt_end, @@ -284,45 +295,10 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, } dev_dbg(dev, - "%s: GPU incr blocked interleaved ctx buffers\n", - __func__); - - exec_ctxsave = false; - - /* setup GPU increments */ - list_for_each_entry_from(job, &cdma->sync_queue, list) { - /* same context, increment in the pushbuffer */ - if (job->clientid == cdma->timeout.clientid) { - /* won't need a timeout when replayed */ - job->timeout = 0; - - /* update buffer's syncpts in the pushbuffer */ - cdma_op(cdma).timeout_pb_incr(cdma, - job->first_get, - job->syncpt_incrs, - job->num_slots, - exec_ctxsave); - - exec_ctxsave = false; - } else { - dev_dbg(dev, - "%s: switch to a different userctx\n", - __func__); - /* - * If previous context was the timed out context - * then clear its CTXSAVE in this slot. - */ - exec_ctxsave = true; - } - - nvhost_job_dump(dev, job); - } - - dev_dbg(dev, "%s: finished sync_queue modification\n", __func__); /* roll back DMAGET and start up channel again */ - cdma_op(cdma).timeout_teardown_end(cdma, get_restart); + cdma_op().timeout_teardown_end(cdma, get_restart); if (cdma->timeout.ctx) cdma->timeout.ctx->has_timedout = true; @@ -335,7 +311,7 @@ int nvhost_cdma_init(struct nvhost_cdma *cdma) { int err; struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).init); + BUG_ON(!cdma_pb_op().init); mutex_init(&cdma->lock); sema_init(&cdma->sem, 0); @@ -345,7 +321,7 @@ int nvhost_cdma_init(struct nvhost_cdma *cdma) cdma->running = false; cdma->torndown = false; - err = cdma_pb_op(cdma).init(pb); + err = cdma_pb_op().init(pb); if (err) return err; return 0; @@ -358,10 +334,10 @@ void nvhost_cdma_deinit(struct nvhost_cdma *cdma) { struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).destroy); + BUG_ON(!cdma_pb_op().destroy); BUG_ON(cdma->running); - cdma_pb_op(cdma).destroy(pb); - cdma_op(cdma).timeout_destroy(cdma); + cdma_pb_op().destroy(pb); + cdma_op().timeout_destroy(cdma); } /** @@ -375,8 +351,8 @@ int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job) /* init state on first submit with timeout value */ if (!cdma->timeout.initialized) { int err; - BUG_ON(!cdma_op(cdma).timeout_init); - err = cdma_op(cdma).timeout_init(cdma, + BUG_ON(!cdma_op().timeout_init); + err = cdma_op().timeout_init(cdma, job->syncpt_id); if (err) { mutex_unlock(&cdma->lock); @@ -385,22 +361,58 @@ int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job) } } if (!cdma->running) { - BUG_ON(!cdma_op(cdma).start); - cdma_op(cdma).start(cdma); + BUG_ON(!cdma_op().start); + cdma_op().start(cdma); } cdma->slots_free = 0; cdma->slots_used = 0; - cdma->first_get = cdma_pb_op(cdma).putptr(&cdma->push_buffer); + cdma->first_get = cdma_pb_op().putptr(&cdma->push_buffer); return 0; } +static void trace_write_gather(struct nvhost_cdma *cdma, + struct nvmap_handle *handle, + u32 offset, u32 words) +{ + struct nvmap_handle_ref ref; + void *mem = NULL; + + if (nvhost_debug_trace_cmdbuf) { + ref.handle = handle; + mem = nvmap_mmap(&ref); + if (IS_ERR_OR_NULL(mem)) + mem = NULL; + }; + + if (mem) { + u32 i; + /* + * Write in batches of 128 as there seems to be a limit + * of how much you can output to ftrace at once. + */ + for (i = 0; i < words; i += TRACE_MAX_LENGTH) { + trace_nvhost_cdma_push_gather( + cdma_to_channel(cdma)->dev->name, + (u32)handle, + min(words - i, TRACE_MAX_LENGTH), + offset + i * sizeof(u32), + mem); + } + nvmap_munmap(&ref, mem); + } +} + /** * Push two words into a push buffer slot * Blocks as necessary if the push buffer is full. */ void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2) { - nvhost_cdma_push_gather(cdma, NULL, NULL, op1, op2); + if (nvhost_debug_trace_cmdbuf) + trace_nvhost_cdma_push(cdma_to_channel(cdma)->dev->name, + op1, op2); + + nvhost_cdma_push_gather(cdma, NULL, NULL, 0, op1, op2); } /** @@ -409,20 +421,26 @@ void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2) */ void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, struct nvmap_client *client, - struct nvmap_handle *handle, u32 op1, u32 op2) + struct nvmap_handle *handle, + u32 offset, u32 op1, u32 op2) { u32 slots_free = cdma->slots_free; struct push_buffer *pb = &cdma->push_buffer; - BUG_ON(!cdma_pb_op(cdma).push_to); - BUG_ON(!cdma_op(cdma).kick); + + BUG_ON(!cdma_pb_op().push_to); + BUG_ON(!cdma_op().kick); + + if (handle) + trace_write_gather(cdma, handle, offset, op1 & 0xffff); + if (slots_free == 0) { - cdma_op(cdma).kick(cdma); + cdma_op().kick(cdma); slots_free = nvhost_cdma_wait_locked(cdma, CDMA_EVENT_PUSH_BUFFER_SPACE); } cdma->slots_free = slots_free - 1; cdma->slots_used++; - cdma_pb_op(cdma).push_to(pb, client, handle, op1, op2); + cdma_pb_op().push_to(pb, client, handle, op1, op2); } /** @@ -436,8 +454,8 @@ void nvhost_cdma_end(struct nvhost_cdma *cdma, { bool was_idle = list_empty(&cdma->sync_queue); - BUG_ON(!cdma_op(cdma).kick); - cdma_op(cdma).kick(cdma); + BUG_ON(!cdma_op().kick); + cdma_op().kick(cdma); BUG_ON(job->syncpt_id == NVSYNCPT_INVALID); diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h index 9cb9b8277254..e6f51179150f 100644 --- a/drivers/video/tegra/host/nvhost_cdma.h +++ b/drivers/video/tegra/host/nvhost_cdma.h @@ -25,11 +25,9 @@ #include <linux/semaphore.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <linux/list.h> -#include "nvhost_acm.h" - struct nvhost_syncpt; struct nvhost_userctx_timeout; struct nvhost_job; @@ -97,7 +95,6 @@ struct nvhost_cdma { unsigned int first_get; /* DMAGET value, where submit begins */ unsigned int last_put; /* last value written to DMAPUT */ struct push_buffer push_buffer; /* channel's push buffer */ - struct syncpt_buffer syncpt_buffer; /* syncpt incr buffer */ struct list_head sync_queue; /* job queue */ struct buffer_timeout timeout; /* channel's timeout state/wq */ bool running; @@ -106,20 +103,17 @@ struct nvhost_cdma { #define cdma_to_channel(cdma) container_of(cdma, struct nvhost_channel, cdma) #define cdma_to_dev(cdma) nvhost_get_host(cdma_to_channel(cdma)->dev) -#define cdma_op(cdma) (cdma_to_dev(cdma)->op.cdma) #define cdma_to_nvmap(cdma) ((cdma_to_dev(cdma))->nvmap) #define pb_to_cdma(pb) container_of(pb, struct nvhost_cdma, push_buffer) -#define cdma_pb_op(cdma) (cdma_to_dev(cdma)->op.push_buffer) int nvhost_cdma_init(struct nvhost_cdma *cdma); void nvhost_cdma_deinit(struct nvhost_cdma *cdma); void nvhost_cdma_stop(struct nvhost_cdma *cdma); int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job); void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2); -#define NVHOST_CDMA_PUSH_GATHER_CTXSAVE 0xffffffff void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, struct nvmap_client *client, - struct nvmap_handle *handle, u32 op1, u32 op2); + struct nvmap_handle *handle, u32 offset, u32 op1, u32 op2); void nvhost_cdma_end(struct nvhost_cdma *cdma, struct nvhost_job *job); void nvhost_cdma_update(struct nvhost_cdma *cdma); diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c index afbac6fe4c4e..ef8886fe4652 100644 --- a/drivers/video/tegra/host/nvhost_channel.c +++ b/drivers/video/tegra/host/nvhost_channel.c @@ -20,13 +20,14 @@ #include "nvhost_channel.h" #include "dev.h" +#include "nvhost_acm.h" #include "nvhost_job.h" +#include "chip_support.h" + #include <trace/events/nvhost.h> #include <linux/nvhost_ioctl.h> #include <linux/slab.h> -#include <linux/platform_device.h> - #define NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT 50 int nvhost_channel_init(struct nvhost_channel *ch, @@ -36,7 +37,7 @@ int nvhost_channel_init(struct nvhost_channel *ch, struct nvhost_device *ndev; /* Link nvhost_device to nvhost_channel */ - err = host_channel_op(dev).init(ch, dev, index); + err = channel_op().init(ch, dev, index); if (err < 0) { dev_err(&dev->dev->dev, "failed to init channel %d\n", index); @@ -57,16 +58,18 @@ int nvhost_channel_submit(struct nvhost_job *job) (void)nvhost_cdma_flush(&job->ch->cdma, NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT); - return channel_op(job->ch).submit(job); + return channel_op().submit(job); } struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch) { int err = 0; + struct nvhost_driver *drv = to_nvhost_driver(ch->dev->dev.driver); + mutex_lock(&ch->reflock); if (ch->refcount == 0) { - if (ch->dev->init) - ch->dev->init(ch->dev); + if (drv->init) + drv->init(ch->dev); err = nvhost_cdma_init(&ch->cdma); } else if (ch->dev->exclusive) { err = -EBUSY; @@ -85,7 +88,7 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch) void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx) { - BUG_ON(!channel_cdma_op(ch).stop); + BUG_ON(!channel_cdma_op().stop); if (ctx) { mutex_lock(&ch->submitlock); @@ -100,7 +103,7 @@ void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx) mutex_lock(&ch->reflock); if (ch->refcount == 1) { - channel_cdma_op(ch).stop(&ch->cdma); + channel_cdma_op().stop(&ch->cdma); nvhost_cdma_deinit(&ch->cdma); nvhost_module_suspend(ch->dev); } @@ -113,14 +116,40 @@ int nvhost_channel_suspend(struct nvhost_channel *ch) int ret = 0; mutex_lock(&ch->reflock); - BUG_ON(!channel_cdma_op(ch).stop); + BUG_ON(!channel_cdma_op().stop); if (ch->refcount) { ret = nvhost_module_suspend(ch->dev); if (!ret) - channel_cdma_op(ch).stop(&ch->cdma); + channel_cdma_op().stop(&ch->cdma); } mutex_unlock(&ch->reflock); return ret; } + +struct nvhost_channel *nvhost_alloc_channel_internal(int chindex, + int max_channels, int *current_channel_count) +{ + struct nvhost_channel *ch = NULL; + + if ( (chindex > max_channels) || + ( (*current_channel_count + 1) > max_channels) ) + return NULL; + else { + ch = kzalloc(sizeof(*ch), GFP_KERNEL); + if (ch == NULL) + return NULL; + else { + (*current_channel_count)++; + return ch; + } + } +} + +void nvhost_free_channel_internal(struct nvhost_channel *ch, + int *current_channel_count) +{ + kfree(ch); + (*current_channel_count)--; +} diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h index 7b946c8ee853..eac51731547b 100644 --- a/drivers/video/tegra/host/nvhost_channel.h +++ b/drivers/video/tegra/host/nvhost_channel.h @@ -21,22 +21,20 @@ #ifndef __NVHOST_CHANNEL_H #define __NVHOST_CHANNEL_H -#include "nvhost_cdma.h" -#include "nvhost_acm.h" -#include "nvhost_hwctx.h" -#include "nvhost_job.h" - #include <linux/cdev.h> #include <linux/io.h> +#include "nvhost_cdma.h" -#define NVHOST_MAX_WAIT_CHECKS 256 -#define NVHOST_MAX_GATHERS 512 -#define NVHOST_MAX_HANDLES 1280 -#define NVHOST_MAX_POWERGATE_IDS 2 +#define NVHOST_MAX_WAIT_CHECKS 256 +#define NVHOST_MAX_GATHERS 512 +#define NVHOST_MAX_HANDLES 1280 +#define NVHOST_MAX_POWERGATE_IDS 2 struct nvhost_master; struct nvhost_waitchk; struct nvhost_device; +struct nvhost_channel; +struct nvhost_hwctx; struct nvhost_channel_gather { u32 words; @@ -60,8 +58,7 @@ struct nvhost_channel { struct nvhost_cdma cdma; }; -int nvhost_channel_init( - struct nvhost_channel *ch, +int nvhost_channel_init(struct nvhost_channel *ch, struct nvhost_master *dev, int index); int nvhost_channel_submit(struct nvhost_job *job); @@ -70,17 +67,17 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch); void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx); int nvhost_channel_suspend(struct nvhost_channel *ch); -#define channel_cdma_op(ch) (nvhost_get_host(ch->dev)->op.cdma) -#define channel_op(ch) (nvhost_get_host(ch->dev)->op.channel) -#define host_channel_op(host) (host->op.channel) - int nvhost_channel_drain_read_fifo(void __iomem *chan_regs, u32 *ptr, unsigned int count, unsigned int *pending); -int nvhost_channel_read_3d_reg( - struct nvhost_channel *channel, +int nvhost_channel_read_3d_reg(struct nvhost_channel *channel, struct nvhost_hwctx *hwctx, - u32 offset, - u32 *value); + u32 offset, u32 *value); + +struct nvhost_channel *nvhost_alloc_channel_internal(int chindex, + int max_channels, int *current_channel_count); + +void nvhost_free_channel_internal(struct nvhost_channel *ch, + int *current_channel_count); #endif diff --git a/drivers/video/tegra/host/nvhost_hwctx.h b/drivers/video/tegra/host/nvhost_hwctx.h index 02a3976f01ce..75ed0be1a72c 100644 --- a/drivers/video/tegra/host/nvhost_hwctx.h +++ b/drivers/video/tegra/host/nvhost_hwctx.h @@ -25,7 +25,7 @@ #include <linux/kref.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> struct nvhost_channel; struct nvhost_cdma; diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c index 7c4bdc7bafb6..ba821f694cb4 100644 --- a/drivers/video/tegra/host/nvhost_intr.c +++ b/drivers/video/tegra/host/nvhost_intr.c @@ -20,14 +20,13 @@ #include "nvhost_intr.h" #include "dev.h" +#include "nvhost_acm.h" #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/irq.h> #include <trace/events/nvhost.h> - - - - +#include "nvhost_channel.h" +#include "nvhost_hwctx.h" /*** Wait list management ***/ @@ -116,11 +115,11 @@ void reset_threshold_interrupt(struct nvhost_intr *intr, { u32 thresh = list_first_entry(head, struct nvhost_waitlist, list)->thresh; - BUG_ON(!(intr_op(intr).set_syncpt_threshold && - intr_op(intr).enable_syncpt_intr)); + BUG_ON(!(intr_op().set_syncpt_threshold && + intr_op().enable_syncpt_intr)); - intr_op(intr).set_syncpt_threshold(intr, id, thresh); - intr_op(intr).enable_syncpt_intr(intr, id); + intr_op().set_syncpt_threshold(intr, id, thresh); + intr_op().enable_syncpt_intr(intr, id); } @@ -264,8 +263,8 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, BUG_ON(waiter == NULL); - BUG_ON(!(intr_op(intr).set_syncpt_threshold && - intr_op(intr).enable_syncpt_intr)); + BUG_ON(!(intr_op().set_syncpt_threshold && + intr_op().enable_syncpt_intr)); /* initialize a new waiter */ INIT_LIST_HEAD(&waiter->list); @@ -288,8 +287,8 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, spin_unlock(&syncpt->lock); mutex_lock(&intr->mutex); - BUG_ON(!(intr_op(intr).request_syncpt_irq)); - err = intr_op(intr).request_syncpt_irq(syncpt); + BUG_ON(!(intr_op().request_syncpt_irq)); + err = intr_op().request_syncpt_irq(syncpt); mutex_unlock(&intr->mutex); if (err) { @@ -304,11 +303,11 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, if (add_waiter_to_queue(waiter, &syncpt->wait_head)) { /* added at head of list - new threshold value */ - intr_op(intr).set_syncpt_threshold(intr, id, thresh); + intr_op().set_syncpt_threshold(intr, id, thresh); /* added as first waiter - enable interrupt */ if (queue_was_empty) - intr_op(intr).enable_syncpt_intr(intr, id); + intr_op().enable_syncpt_intr(intr, id); } spin_unlock(&syncpt->lock); @@ -347,6 +346,7 @@ int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync) u32 nb_pts = host->syncpt.nb_pts; mutex_init(&intr->mutex); + intr_op().init_host_sync(intr); intr->host_general_irq = irq_gen; intr->host_general_irq_requested = false; @@ -374,17 +374,17 @@ void nvhost_intr_deinit(struct nvhost_intr *intr) void nvhost_intr_start(struct nvhost_intr *intr, u32 hz) { - BUG_ON(!(intr_op(intr).init_host_sync && - intr_op(intr).set_host_clocks_per_usec && - intr_op(intr).request_host_general_irq)); + BUG_ON(!(intr_op().init_host_sync && + intr_op().set_host_clocks_per_usec && + intr_op().request_host_general_irq)); mutex_lock(&intr->mutex); - intr_op(intr).init_host_sync(intr); - intr_op(intr).set_host_clocks_per_usec(intr, + intr_op().init_host_sync(intr); + intr_op().set_host_clocks_per_usec(intr, (hz + 1000000 - 1)/1000000); - intr_op(intr).request_host_general_irq(intr); + intr_op().request_host_general_irq(intr); mutex_unlock(&intr->mutex); } @@ -395,12 +395,12 @@ void nvhost_intr_stop(struct nvhost_intr *intr) struct nvhost_intr_syncpt *syncpt; u32 nb_pts = intr_to_dev(intr)->syncpt.nb_pts; - BUG_ON(!(intr_op(intr).disable_all_syncpt_intrs && - intr_op(intr).free_host_general_irq)); + BUG_ON(!(intr_op().disable_all_syncpt_intrs && + intr_op().free_host_general_irq)); mutex_lock(&intr->mutex); - intr_op(intr).disable_all_syncpt_intrs(intr); + intr_op().disable_all_syncpt_intrs(intr); for (id = 0, syncpt = intr->syncpt; id < nb_pts; @@ -422,7 +422,7 @@ void nvhost_intr_stop(struct nvhost_intr *intr) free_syncpt_irq(syncpt); } - intr_op(intr).free_host_general_irq(intr); + intr_op().free_host_general_irq(intr); mutex_unlock(&intr->mutex); } diff --git a/drivers/video/tegra/host/nvhost_intr.h b/drivers/video/tegra/host/nvhost_intr.h index 26ab04ebd4ab..eea9d837998f 100644 --- a/drivers/video/tegra/host/nvhost_intr.h +++ b/drivers/video/tegra/host/nvhost_intr.h @@ -74,7 +74,6 @@ struct nvhost_intr { bool host_general_irq_requested; }; #define intr_to_dev(x) container_of(x, struct nvhost_master, intr) -#define intr_op(intr) (intr_to_dev(intr)->op.intr) #define intr_syncpt_to_intr(is) (is->intr) /** diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index df7a62d689bc..71f2ab0e751f 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -22,9 +22,10 @@ #include <linux/kref.h> #include <linux/err.h> #include <linux/vmalloc.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvhost_channel.h" #include "nvhost_job.h" +#include "nvhost_hwctx.h" #include "dev.h" /* Magic to use to fill freed handle slots */ @@ -73,7 +74,7 @@ static int alloc_gathers(struct nvhost_job *job, gather_size(num_cmdbufs), 32, NVMAP_HANDLE_CACHEABLE, 0); if (IS_ERR_OR_NULL(job->gather_mem)) { - err = PTR_ERR(job->gather_mem); + err = job->gather_mem ? PTR_ERR(job->gather_mem) : -ENOMEM; job->gather_mem = NULL; goto error; } @@ -82,7 +83,7 @@ static int alloc_gathers(struct nvhost_job *job, /* Map memory to kernel */ job->gathers = nvmap_mmap(job->gather_mem); if (IS_ERR_OR_NULL(job->gathers)) { - err = PTR_ERR(job->gathers); + err = job->gathers ? PTR_ERR(job->gathers) : -ENOMEM; job->gathers = NULL; goto error; } diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c index 13ad0fc3a3cf..4835d22881b8 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.c +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -21,10 +21,11 @@ #include <linux/nvhost_ioctl.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <trace/events/nvhost.h> #include "nvhost_syncpt.h" +#include "nvhost_acm.h" #include "dev.h" -#define MAX_STUCK_CHECK_COUNT 15 #define MAX_SYNCPT_LENGTH 5 /* Name of sysfs node for min and max value */ static const char *min_name = "min"; @@ -36,12 +37,12 @@ static const char *max_name = "max"; void nvhost_syncpt_reset(struct nvhost_syncpt *sp) { u32 i; - BUG_ON(!(syncpt_op(sp).reset && syncpt_op(sp).reset_wait_base)); + BUG_ON(!(syncpt_op().reset && syncpt_op().reset_wait_base)); for (i = 0; i < sp->nb_pts; i++) - syncpt_op(sp).reset(sp, i); + syncpt_op().reset(sp, i); for (i = 0; i < sp->nb_bases; i++) - syncpt_op(sp).reset_wait_base(sp, i); + syncpt_op().reset_wait_base(sp, i); wmb(); } @@ -51,17 +52,17 @@ void nvhost_syncpt_reset(struct nvhost_syncpt *sp) void nvhost_syncpt_save(struct nvhost_syncpt *sp) { u32 i; - BUG_ON(!(syncpt_op(sp).update_min && syncpt_op(sp).read_wait_base)); + BUG_ON(!(syncpt_op().update_min && syncpt_op().read_wait_base)); for (i = 0; i < sp->nb_pts; i++) { if (client_managed(i)) - syncpt_op(sp).update_min(sp, i); + syncpt_op().update_min(sp, i); else BUG_ON(!nvhost_syncpt_min_eq_max(sp, i)); } for (i = 0; i < sp->nb_bases; i++) - syncpt_op(sp).read_wait_base(sp, i); + syncpt_op().read_wait_base(sp, i); } /** @@ -69,9 +70,14 @@ void nvhost_syncpt_save(struct nvhost_syncpt *sp) */ u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) { - BUG_ON(!syncpt_op(sp).update_min); + u32 val; + + BUG_ON(!syncpt_op().update_min); - return syncpt_op(sp).update_min(sp, id); + return syncpt_op().update_min(sp, id); + trace_nvhost_syncpt_update_min(id, val); + + return val; } /** @@ -80,9 +86,9 @@ u32 nvhost_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id) { u32 val; - BUG_ON(!syncpt_op(sp).update_min); + BUG_ON(!syncpt_op().update_min); nvhost_module_busy(syncpt_to_dev(sp)->dev); - val = syncpt_op(sp).update_min(sp, id); + val = syncpt_op().update_min(sp, id); nvhost_module_idle(syncpt_to_dev(sp)->dev); return val; } @@ -93,9 +99,9 @@ u32 nvhost_syncpt_read(struct nvhost_syncpt *sp, u32 id) u32 nvhost_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) { u32 val; - BUG_ON(!syncpt_op(sp).read_wait_base); + BUG_ON(!syncpt_op().read_wait_base); nvhost_module_busy(syncpt_to_dev(sp)->dev); - syncpt_op(sp).read_wait_base(sp, id); + syncpt_op().read_wait_base(sp, id); val = sp->base_val[id]; nvhost_module_idle(syncpt_to_dev(sp)->dev); return val; @@ -107,8 +113,8 @@ u32 nvhost_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) */ void nvhost_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) { - BUG_ON(!syncpt_op(sp).cpu_incr); - syncpt_op(sp).cpu_incr(sp, id); + BUG_ON(!syncpt_op().cpu_incr); + syncpt_op().cpu_incr(sp, id); } /** @@ -149,7 +155,7 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, nvhost_module_busy(syncpt_to_dev(sp)->dev); /* try to read from register */ - val = syncpt_op(sp).update_min(sp, id); + val = syncpt_op().update_min(sp, id); if (nvhost_syncpt_is_expired(sp, id, thresh)) { if (value) *value = val; @@ -198,20 +204,19 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id, } if (timeout != NVHOST_NO_TIMEOUT) timeout -= check; - if (timeout) { + if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) { dev_warn(&syncpt_to_dev(sp)->dev->dev, "%s: syncpoint id %d (%s) stuck waiting %d, timeout=%d\n", - current->comm, id, syncpt_op(sp).name(sp, id), + current->comm, id, syncpt_op().name(sp, id), thresh, timeout); - syncpt_op(sp).debug(sp); - if (check_count > MAX_STUCK_CHECK_COUNT) { + syncpt_op().debug(sp); + if (check_count == MAX_STUCK_CHECK_COUNT) { if (low_timeout) { dev_warn(&syncpt_to_dev(sp)->dev->dev, "is timeout %d too low?\n", low_timeout); } nvhost_debug_dump(syncpt_to_dev(sp)); - BUG(); } check_count++; } @@ -287,7 +292,7 @@ bool nvhost_syncpt_is_expired( void nvhost_syncpt_debug(struct nvhost_syncpt *sp) { - syncpt_op(sp).debug(sp); + syncpt_op().debug(sp); } int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx) @@ -296,7 +301,7 @@ int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx) u32 reg; nvhost_module_busy(host->dev); - reg = syncpt_op(sp).mutex_try_lock(sp, idx); + reg = syncpt_op().mutex_try_lock(sp, idx); if (reg) { nvhost_module_idle(host->dev); return -EBUSY; @@ -307,7 +312,7 @@ int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx) void nvhost_mutex_unlock(struct nvhost_syncpt *sp, int idx) { - syncpt_op(sp).mutex_unlock(sp, idx); + syncpt_op().mutex_unlock(sp, idx); nvhost_module_idle(syncpt_to_dev(sp)->dev); atomic_dec(&sp->lock_counts[idx]); } @@ -319,7 +324,7 @@ int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, struct nvhost_waitchk *wait, int num_waitchk) { - return syncpt_op(sp).wait_check(sp, nvmap, + return syncpt_op().wait_check(sp, nvmap, waitchk_mask, wait, num_waitchk); } diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h index b71cb3e6287e..b770ed91c76c 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.h +++ b/drivers/video/tegra/host/nvhost_syncpt.h @@ -24,12 +24,9 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/nvhost.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <linux/atomic.h> -struct nvhost_syncpt; -struct nvhost_waitchk; - /* host managed and invalid syncpt id */ #define NVSYNCPT_GRAPHICS_HOST (0) #define NVSYNCPT_INVALID (-1) @@ -59,9 +56,8 @@ void nvhost_syncpt_deinit(struct nvhost_syncpt *); #define client_managed(id) (BIT(id) & sp->client_managed) #define syncpt_to_dev(sp) container_of(sp, struct nvhost_master, syncpt) -#define syncpt_op(sp) (syncpt_to_dev(sp)->op.syncpt) -#define SYNCPT_CHECK_PERIOD (2*HZ) - +#define SYNCPT_CHECK_PERIOD (2 * HZ) +#define MAX_STUCK_CHECK_COUNT 15 /** * Updates the value sent to hardware. @@ -151,6 +147,7 @@ static inline int nvhost_syncpt_wait(struct nvhost_syncpt *sp, u32 id, u32 thres * @param: wait - start of filled in array of waitchk structs * @param: waitend - end ptr (one beyond last valid waitchk) */ +struct nvhost_waitchk; int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, struct nvmap_client *nvmap, u32 mask, diff --git a/drivers/video/tegra/host/t20/t20.c b/drivers/video/tegra/host/t20/t20.c index 24ddedc842e4..f6713d866f19 100644 --- a/drivers/video/tegra/host/t20/t20.c +++ b/drivers/video/tegra/host/t20/t20.c @@ -19,17 +19,19 @@ */ #include <linux/slab.h> +#include <linux/nvhost_ioctl.h> #include <mach/powergate.h> #include "dev.h" #include "t20.h" -#include "host1x/host1x_channel.h" #include "host1x/host1x_syncpt.h" #include "host1x/host1x_hardware.h" -#include "host1x/host1x_cdma.h" #include "gr3d/gr3d.h" #include "gr3d/gr3d_t20.h" #include "mpe/mpe.h" #include "nvhost_hwctx.h" +#include "nvhost_channel.h" +#include "host1x/host1x_channel.h" +#include "host1x/host1x_cdma.h" #define NVMODMUTEX_2D_FULL (1) #define NVMODMUTEX_2D_SIMPLE (2) @@ -41,7 +43,9 @@ #define NVMODMUTEX_VI (8) #define NVMODMUTEX_DSI (9) -#define NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1) +#define T20_NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1) + +static int t20_num_alloc_channels = 0; struct nvhost_device t20_devices[] = { { @@ -60,15 +64,13 @@ struct nvhost_device t20_devices[] = { }, { /* channel 1 */ - .name = "gr3d", + .name = "gr3d01", .id = -1, .index = 1, .syncpts = BIT(NVSYNCPT_3D), .waitbases = BIT(NVWAITBASE_3D), .modulemutexes = BIT(NVMODMUTEX_3D), .class = NV_GRAPHICS_3D_CLASS_ID, - .prepare_poweroff = nvhost_gr3d_prepare_power_off, - .alloc_hwctx_handler = nvhost_gr3d_t20_ctxhandler_init, .clocks = {{"gr3d", UINT_MAX}, {"emc", UINT_MAX}, {} }, .powergate_ids = {TEGRA_POWERGATE_3D, -1}, NVHOST_DEFAULT_CLOCKGATE_DELAY, @@ -117,7 +119,7 @@ struct nvhost_device t20_devices[] = { }, { /* channel 5 */ - .name = "mpe", + .name = "mpe01", .id = -1, .index = 5, .syncpts = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) | @@ -126,8 +128,6 @@ struct nvhost_device t20_devices[] = { .class = NV_VIDEO_ENCODE_MPEG_CLASS_ID, .waitbasesync = true, .keepalive = true, - .prepare_poweroff = nvhost_mpe_prepare_power_off, - .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, .clocks = { {"mpe", UINT_MAX}, {"emc", UINT_MAX} }, .powergate_ids = {TEGRA_POWERGATE_MPE, -1}, @@ -161,9 +161,10 @@ static inline int t20_nvhost_hwctx_handler_init(struct nvhost_channel *ch) unsigned long waitbases = ch->dev->waitbases; u32 syncpt = find_first_bit(&syncpts, BITS_PER_LONG); u32 waitbase = find_first_bit(&waitbases, BITS_PER_LONG); + struct nvhost_driver *drv = to_nvhost_driver(ch->dev->dev.driver); - if (ch->dev->alloc_hwctx_handler) { - ch->ctxhandler = ch->dev->alloc_hwctx_handler(syncpt, + if (drv->alloc_hwctx_handler) { + ch->ctxhandler = drv->alloc_hwctx_handler(syncpt, waitbase, ch); if (!ch->ctxhandler) err = -ENOMEM; @@ -184,50 +185,64 @@ static int t20_channel_init(struct nvhost_channel *ch, return t20_nvhost_hwctx_handler_init(ch); } -int nvhost_init_t20_channel_support(struct nvhost_master *host) +int nvhost_init_t20_channel_support(struct nvhost_master *host, + struct nvhost_chip_support *op) { - host->nb_channels = NVHOST_NUMCHANNELS; - - host->op.channel.init = t20_channel_init; - host->op.channel.submit = host1x_channel_submit; - host->op.channel.read3dreg = host1x_channel_read_3d_reg; + op->channel.init = t20_channel_init; + op->channel.submit = host1x_channel_submit; + op->channel.read3dreg = host1x_channel_read_3d_reg; return 0; } -struct nvhost_device *t20_get_nvhost_device(struct nvhost_master *host, - char *name) +static void t20_free_nvhost_channel(struct nvhost_channel *ch) +{ + nvhost_free_channel_internal(ch, &t20_num_alloc_channels); +} + +static struct nvhost_channel *t20_alloc_nvhost_channel(int chindex) +{ + return nvhost_alloc_channel_internal(chindex, + T20_NVHOST_NUMCHANNELS, &t20_num_alloc_channels); +} + +struct nvhost_device *t20_get_nvhost_device(char *name) { int i; - for (i = 0; i < host->nb_channels; i++) { - if (strcmp(t20_devices[i].name, name) == 0) + for (i = 0; i < ARRAY_SIZE(t20_devices); i++) { + if (strncmp(t20_devices[i].name, name, strlen(name)) == 0) return &t20_devices[i]; } return NULL; } -int nvhost_init_t20_support(struct nvhost_master *host) +int nvhost_init_t20_support(struct nvhost_master *host, + struct nvhost_chip_support *op) { int err; /* don't worry about cleaning up on failure... "remove" does it. */ - err = nvhost_init_t20_channel_support(host); + err = nvhost_init_t20_channel_support(host, op); if (err) return err; - err = host1x_init_cdma_support(host); + err = host1x_init_cdma_support(op); if (err) return err; - err = nvhost_init_t20_debug_support(host); + err = nvhost_init_t20_debug_support(op); if (err) return err; - err = host1x_init_syncpt_support(host); + err = host1x_init_syncpt_support(host, op); if (err) return err; - err = nvhost_init_t20_intr_support(host); + err = nvhost_init_t20_intr_support(op); if (err) return err; - host->op.nvhost_dev.get_nvhost_device = t20_get_nvhost_device; + + op->nvhost_dev.get_nvhost_device = t20_get_nvhost_device; + op->nvhost_dev.alloc_nvhost_channel = t20_alloc_nvhost_channel; + op->nvhost_dev.free_nvhost_channel = t20_free_nvhost_channel; + return 0; } diff --git a/drivers/video/tegra/host/t20/t20.h b/drivers/video/tegra/host/t20/t20.h index 93555a55b589..456d3ae1bc03 100644 --- a/drivers/video/tegra/host/t20/t20.h +++ b/drivers/video/tegra/host/t20/t20.h @@ -22,12 +22,15 @@ struct nvhost_master; struct nvhost_module; +struct nvhost_chip_support; -int nvhost_init_t20_channel_support(struct nvhost_master *); -int nvhost_init_t20_debug_support(struct nvhost_master *); +int nvhost_init_t20_channel_support(struct nvhost_master *, + struct nvhost_chip_support *); +int nvhost_init_t20_debug_support(struct nvhost_chip_support *); int nvhost_init_t20_syncpt_support(struct nvhost_master *); -int nvhost_init_t20_intr_support(struct nvhost_master *); -int nvhost_init_t20_support(struct nvhost_master *host); +int nvhost_init_t20_intr_support(struct nvhost_chip_support *); +int nvhost_init_t20_support(struct nvhost_master *, + struct nvhost_chip_support *); int nvhost_t20_save_context(struct nvhost_module *mod, u32 syncpt_id); #endif /* _NVHOST_T20_H_ */ diff --git a/drivers/video/tegra/host/t30/t30.c b/drivers/video/tegra/host/t30/t30.c index 8a8b1f4d9240..257ba0849277 100644 --- a/drivers/video/tegra/host/t30/t30.c +++ b/drivers/video/tegra/host/t30/t30.c @@ -19,19 +19,21 @@ */ #include <linux/mutex.h> +#include <linux/nvhost_ioctl.h> #include <mach/powergate.h> #include <mach/iomap.h> #include "dev.h" #include "t20/t20.h" #include "t30.h" #include "gr3d/gr3d.h" -#include "mpe/mpe.h" #include "gr3d/gr3d_t30.h" #include "gr3d/scale3d.h" +#include "mpe/mpe.h" #include "host1x/host1x_hardware.h" -#include "host1x/host1x_cdma.h" #include "host1x/host1x_syncpt.h" #include "chip_support.h" +#include "nvhost_channel.h" +#include "host1x/host1x_cdma.h" #define NVMODMUTEX_2D_FULL (1) #define NVMODMUTEX_2D_SIMPLE (2) @@ -45,6 +47,10 @@ #define NVHOST_CHANNEL_BASE 0 +#define T30_NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1) + +static int t30_num_alloc_channels = 0; + struct nvhost_device t30_devices[] = { { /* channel 0 */ @@ -62,27 +68,21 @@ struct nvhost_device t30_devices[] = { }, { /* channel 1 */ - .name = "gr3d", + .name = "gr3d02", .id = -1, .index = 1, .syncpts = BIT(NVSYNCPT_3D), .waitbases = BIT(NVWAITBASE_3D), .modulemutexes = BIT(NVMODMUTEX_3D), .class = NV_GRAPHICS_3D_CLASS_ID, - .prepare_poweroff = nvhost_gr3d_prepare_power_off, - .busy = nvhost_scale3d_notify_busy, - .idle = nvhost_scale3d_notify_idle, - .init = nvhost_scale3d_init, - .deinit = nvhost_scale3d_deinit, - .suspend = nvhost_scale3d_suspend, - .alloc_hwctx_handler = nvhost_gr3d_t30_ctxhandler_init, .clocks = { {"gr3d", UINT_MAX}, {"gr3d2", UINT_MAX}, {"emc", UINT_MAX} }, .powergate_ids = { TEGRA_POWERGATE_3D, TEGRA_POWERGATE_3D1 }, NVHOST_DEFAULT_CLOCKGATE_DELAY, - .can_powergate = false, + .can_powergate = true, + .powerup_reset = true, .powergate_delay = 250, .moduleid = NVHOST_MODULE_NONE, }, @@ -95,7 +95,7 @@ struct nvhost_device t30_devices[] = { .waitbases = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1), .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) | BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B), - .clocks = { {"gr2d", 0}, + .clocks = { {"gr2d", UINT_MAX}, {"epp", 0}, {"emc", 300000000} }, NVHOST_MODULE_NO_POWERGATE_IDS, @@ -129,7 +129,7 @@ struct nvhost_device t30_devices[] = { }, { /* channel 5 */ - .name = "mpe", + .name = "mpe02", .id = -1, .index = 5, .syncpts = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) | @@ -138,8 +138,6 @@ struct nvhost_device t30_devices[] = { .class = NV_VIDEO_ENCODE_MPEG_CLASS_ID, .waitbasesync = true, .keepalive = true, - .prepare_poweroff = nvhost_mpe_prepare_power_off, - .alloc_hwctx_handler = nvhost_mpe_ctxhandler_init, .clocks = { {"mpe", UINT_MAX}, {"emc", UINT_MAX} }, .powergate_ids = {TEGRA_POWERGATE_MPE, -1}, @@ -167,9 +165,10 @@ static inline int t30_nvhost_hwctx_handler_init(struct nvhost_channel *ch) unsigned long waitbases = ch->dev->waitbases; u32 syncpt = find_first_bit(&syncpts, BITS_PER_LONG); u32 waitbase = find_first_bit(&waitbases, BITS_PER_LONG); + struct nvhost_driver *drv = to_nvhost_driver(ch->dev->dev.driver); - if (ch->dev->alloc_hwctx_handler) { - ch->ctxhandler = ch->dev->alloc_hwctx_handler(syncpt, + if (drv->alloc_hwctx_handler) { + ch->ctxhandler = drv->alloc_hwctx_handler(syncpt, waitbase, ch); if (!ch->ctxhandler) err = -ENOMEM; @@ -198,54 +197,71 @@ static int t30_channel_init(struct nvhost_channel *ch, return t30_nvhost_hwctx_handler_init(ch); } -int nvhost_init_t30_channel_support(struct nvhost_master *host) +int nvhost_init_t30_channel_support(struct nvhost_master *host, + struct nvhost_chip_support *op) { - int result = nvhost_init_t20_channel_support(host); - host->op.channel.init = t30_channel_init; + int result = nvhost_init_t20_channel_support(host, op); + op->channel.init = t30_channel_init; return result; } -int nvhost_init_t30_debug_support(struct nvhost_master *host) + +int nvhost_init_t30_debug_support(struct nvhost_chip_support *op) { - nvhost_init_t20_debug_support(host); - host->op.debug.debug_init = nvhost_scale3d_debug_init; + nvhost_init_t20_debug_support(op); + op->debug.debug_init = nvhost_scale3d_debug_init; return 0; } -struct nvhost_device *t30_get_nvhost_device(struct nvhost_master *host, - char *name) +static void t30_free_nvhost_channel(struct nvhost_channel *ch) +{ + nvhost_free_channel_internal(ch, &t30_num_alloc_channels); +} + +static struct nvhost_channel *t30_alloc_nvhost_channel(int chindex) +{ + return nvhost_alloc_channel_internal(chindex, + T30_NVHOST_NUMCHANNELS, &t30_num_alloc_channels); +} + +struct nvhost_device *t30_get_nvhost_device(char *name) { int i; - for (i = 0; i < host->nb_channels; i++) { - if (strcmp(t30_devices[i].name, name) == 0) + for (i = 0; i < ARRAY_SIZE(t30_devices); i++) { + if (strncmp(t30_devices[i].name, name, strlen(name)) == 0) return &t30_devices[i]; } return NULL; } -int nvhost_init_t30_support(struct nvhost_master *host) +int nvhost_init_t30_support(struct nvhost_master *host, + struct nvhost_chip_support *op) { int err; /* don't worry about cleaning up on failure... "remove" does it. */ - err = nvhost_init_t30_channel_support(host); + err = nvhost_init_t30_channel_support(host, op); if (err) return err; - err = host1x_init_cdma_support(host); + err = host1x_init_cdma_support(op); if (err) return err; - err = nvhost_init_t30_debug_support(host); + err = nvhost_init_t30_debug_support(op); if (err) return err; - err = host1x_init_syncpt_support(host); + err = host1x_init_syncpt_support(host, op); if (err) return err; - err = nvhost_init_t20_intr_support(host); + err = nvhost_init_t20_intr_support(op); if (err) return err; - host->op.nvhost_dev.get_nvhost_device = t30_get_nvhost_device; + + op->nvhost_dev.get_nvhost_device = t30_get_nvhost_device; + op->nvhost_dev.alloc_nvhost_channel = t30_alloc_nvhost_channel; + op->nvhost_dev.free_nvhost_channel = t30_free_nvhost_channel; + return 0; } diff --git a/drivers/video/tegra/host/t30/t30.h b/drivers/video/tegra/host/t30/t30.h index 0446dbd19b39..e4db97b5613d 100644 --- a/drivers/video/tegra/host/t30/t30.h +++ b/drivers/video/tegra/host/t30/t30.h @@ -21,9 +21,12 @@ #define _NVHOST_T30_H_ struct nvhost_master; +struct nvhost_chip_support; -int nvhost_init_t30_channel_support(struct nvhost_master *); -int nvhost_init_t30_debug_support(struct nvhost_master *); -int nvhost_init_t30_support(struct nvhost_master *host); +int nvhost_init_t30_channel_support(struct nvhost_master *, + struct nvhost_chip_support *); +int nvhost_init_t30_debug_support(struct nvhost_chip_support *); +int nvhost_init_t30_support(struct nvhost_master *host, + struct nvhost_chip_support *); #endif /* _NVHOST_T30_H_ */ diff --git a/drivers/video/tegra/host/vi/vi.c b/drivers/video/tegra/host/vi/vi.c index a6f902a1ac7b..3cfc7e32cbc1 100644 --- a/drivers/video/tegra/host/vi/vi.c +++ b/drivers/video/tegra/host/vi/vi.c @@ -25,7 +25,8 @@ #include "dev.h" #include "bus_client.h" -static int __devinit vi_probe(struct nvhost_device *dev) +static int __devinit vi_probe(struct nvhost_device *dev, + struct nvhost_device_id *id_table) { int err = 0; diff --git a/drivers/video/tegra/nvmap/nvmap.c b/drivers/video/tegra/nvmap/nvmap.c index b4b6241618db..a0c4156668e5 100644 --- a/drivers/video/tegra/nvmap/nvmap.c +++ b/drivers/video/tegra/nvmap/nvmap.c @@ -32,7 +32,7 @@ #include <asm/tlbflush.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap.h" #include "nvmap_mru.h" diff --git a/drivers/video/tegra/nvmap/nvmap.h b/drivers/video/tegra/nvmap/nvmap.h index 44a0d86b6039..b0fb70f64a5c 100644 --- a/drivers/video/tegra/nvmap/nvmap.h +++ b/drivers/video/tegra/nvmap/nvmap.h @@ -30,7 +30,7 @@ #include <linux/sched.h> #include <linux/wait.h> #include <linux/atomic.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap_heap.h" struct nvmap_device; @@ -86,7 +86,7 @@ struct nvmap_handle { struct mutex lock; }; -#define NVMAP_DEFAULT_PAGE_POOL_SIZE 8192 +#ifdef CONFIG_NVMAP_PAGE_POOLS #define NVMAP_UC_POOL NVMAP_HANDLE_UNCACHEABLE #define NVMAP_WC_POOL NVMAP_HANDLE_WRITE_COMBINE #define NVMAP_IWB_POOL NVMAP_HANDLE_INNER_CACHEABLE @@ -103,11 +103,13 @@ struct nvmap_page_pool { }; int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags); +#endif struct nvmap_share { struct tegra_iovmm_client *iovmm; wait_queue_head_t pin_wait; struct mutex pin_lock; +#ifdef CONFIG_NVMAP_PAGE_POOLS union { struct nvmap_page_pool pools[NVMAP_NUM_POOLS]; struct { @@ -117,6 +119,7 @@ struct nvmap_share { struct nvmap_page_pool wb_pool; }; }; +#endif #ifdef CONFIG_NVMAP_RECLAIM_UNPINNED_VM struct mutex mru_lock; struct list_head *mru_lists; @@ -201,9 +204,6 @@ struct nvmap_handle *nvmap_get_handle_id(struct nvmap_client *client, struct nvmap_handle_ref *nvmap_create_handle(struct nvmap_client *client, size_t size); -struct nvmap_handle_ref *nvmap_duplicate_handle_id(struct nvmap_client *client, - unsigned long id); - int nvmap_alloc_handle_id(struct nvmap_client *client, unsigned long id, unsigned int heap_mask, size_t align, unsigned int flags); diff --git a/drivers/video/tegra/nvmap/nvmap_dev.c b/drivers/video/tegra/nvmap/nvmap_dev.c index f84f38c93aad..c78818711f74 100644 --- a/drivers/video/tegra/nvmap/nvmap_dev.c +++ b/drivers/video/tegra/nvmap/nvmap_dev.c @@ -39,7 +39,7 @@ #include <asm/tlbflush.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap.h" #include "nvmap_ioctl.h" @@ -1182,8 +1182,10 @@ static int nvmap_probe(struct platform_device *pdev) init_waitqueue_head(&dev->iovmm_master.pin_wait); mutex_init(&dev->iovmm_master.pin_lock); +#ifdef CONFIG_NVMAP_PAGE_POOLS for (i = 0; i < NVMAP_NUM_POOLS; i++) nvmap_page_pool_init(&dev->iovmm_master.pools[i], i); +#endif dev->iovmm_master.iovmm = tegra_iovmm_alloc_client(dev_name(&pdev->dev), NULL, @@ -1311,6 +1313,7 @@ static int nvmap_probe(struct platform_device *pdev) dev, &debug_iovmm_clients_fops); debugfs_create_file("allocations", 0664, iovmm_root, dev, &debug_iovmm_allocations_fops); +#ifdef CONFIG_NVMAP_PAGE_POOLS for (i = 0; i < NVMAP_NUM_POOLS; i++) { char name[40]; char *memtype_string[] = {"uc", "wc", @@ -1321,6 +1324,7 @@ static int nvmap_probe(struct platform_device *pdev) iovmm_root, &dev->iovmm_master.pools[i].npages); } +#endif } } diff --git a/drivers/video/tegra/nvmap/nvmap_handle.c b/drivers/video/tegra/nvmap/nvmap_handle.c index 539b7ce9801f..2f24ba515862 100644 --- a/drivers/video/tegra/nvmap/nvmap_handle.c +++ b/drivers/video/tegra/nvmap/nvmap_handle.c @@ -36,7 +36,7 @@ #include <asm/pgtable.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include <linux/vmstat.h> #include <linux/swap.h> @@ -66,6 +66,9 @@ * preserve kmalloc space, if the array of pages exceeds PAGELIST_VMALLOC_MIN, * the array is allocated using vmalloc. */ #define PAGELIST_VMALLOC_MIN (PAGE_SIZE * 2) + +#ifdef CONFIG_NVMAP_PAGE_POOLS + #define NVMAP_TEST_PAGE_POOL_SHRINKER 1 static bool enable_pp = 1; static int pool_size[NVMAP_NUM_POOLS]; @@ -377,6 +380,7 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) int i; static int reg = 1; struct sysinfo info; + int highmem_pages = 0; typedef int (*set_pages_array) (struct page **pages, int addrinarray); set_pages_array s_cpa[] = { set_pages_array_uc, @@ -395,14 +399,16 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) return 0; si_meminfo(&info); - if (!pool_size[flags]) { + if (!pool_size[flags] && !CONFIG_NVMAP_PAGE_POOL_SIZE) /* Use 3/8th of total ram for page pools. * 1/8th for uc, 1/8th for wc and 1/8th for iwb. */ pool->max_pages = info.totalram >> 3; - } + else + pool->max_pages = CONFIG_NVMAP_PAGE_POOL_SIZE; + if (pool->max_pages <= 0 || pool->max_pages >= info.totalram) - pool->max_pages = NVMAP_DEFAULT_PAGE_POOL_SIZE; + goto fail; pool_size[flags] = pool->max_pages; pr_info("nvmap %s page pool size=%d pages", s_memtype_str[flags], pool->max_pages); @@ -425,7 +431,14 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) __free_page(page); goto do_cpa; } + if (PageHighMem(page)) + highmem_pages++; } + si_meminfo(&info); + pr_info("nvmap pool = %s, highmem=%d, pool_size=%d," + "totalram=%lu, freeram=%lu, totalhigh=%lu, freehigh=%lu", + s_memtype_str[flags], highmem_pages, pool->max_pages, + info.totalram, info.freeram, info.totalhigh, info.freehigh); do_cpa: (*s_cpa[flags])(pool->page_array, pool->npages); nvmap_page_pool_unlock(pool); @@ -436,6 +449,7 @@ fail: vfree(pool->page_array); return -ENOMEM; } +#endif static inline void *altalloc(size_t len) { @@ -460,7 +474,9 @@ void _nvmap_handle_free(struct nvmap_handle *h) { struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); unsigned int i, nr_page, page_index = 0; +#ifdef CONFIG_NVMAP_PAGE_POOLS struct nvmap_page_pool *pool = NULL; +#endif if (nvmap_handle_remove(h->dev, h) != 0) return; @@ -481,6 +497,7 @@ void _nvmap_handle_free(struct nvmap_handle *h) nvmap_mru_remove(share, h); +#ifdef CONFIG_NVMAP_PAGE_POOLS if (h->flags < NVMAP_NUM_POOLS) pool = &share->pools[h->flags]; @@ -490,6 +507,7 @@ void _nvmap_handle_free(struct nvmap_handle *h) break; page_index++; } +#endif if (page_index == nr_page) goto skip_attr_restore; @@ -538,12 +556,14 @@ static int handle_page_alloc(struct nvmap_client *client, struct nvmap_handle *h, bool contiguous) { size_t size = PAGE_ALIGN(h->size); - struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); unsigned int nr_page = size >> PAGE_SHIFT; pgprot_t prot; unsigned int i = 0, page_index = 0; struct page **pages; +#ifdef CONFIG_NVMAP_PAGE_POOLS struct nvmap_page_pool *pool = NULL; + struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); +#endif pages = altalloc(nr_page * sizeof(*pages)); if (!pages) @@ -562,6 +582,7 @@ static int handle_page_alloc(struct nvmap_client *client, pages[i] = nth_page(page, i); } else { +#ifdef CONFIG_NVMAP_PAGE_POOLS if (h->flags < NVMAP_NUM_POOLS) pool = &share->pools[h->flags]; @@ -572,7 +593,7 @@ static int handle_page_alloc(struct nvmap_client *client, break; page_index++; } - +#endif for (; i < nr_page; i++) { pages[i] = nvmap_alloc_pages_exact(GFP_NVMAP, PAGE_SIZE); diff --git a/drivers/video/tegra/nvmap/nvmap_heap.c b/drivers/video/tegra/nvmap/nvmap_heap.c index 7474f31534ff..a6fe78c42f87 100644 --- a/drivers/video/tegra/nvmap/nvmap_heap.c +++ b/drivers/video/tegra/nvmap/nvmap_heap.c @@ -28,7 +28,7 @@ #include <linux/slab.h> #include <linux/err.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap.h" #include "nvmap_heap.h" #include "nvmap_common.h" diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.c b/drivers/video/tegra/nvmap/nvmap_ioctl.c index 58bc71d50469..14787154523f 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.c +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.c @@ -31,7 +31,7 @@ #include <asm/tlbflush.h> #include <mach/iovmm.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #include "nvmap_ioctl.h" #include "nvmap.h" diff --git a/drivers/video/tegra/nvmap/nvmap_ioctl.h b/drivers/video/tegra/nvmap/nvmap_ioctl.h index 54627683ccdb..300ce9b9a6ea 100644 --- a/drivers/video/tegra/nvmap/nvmap_ioctl.h +++ b/drivers/video/tegra/nvmap/nvmap_ioctl.h @@ -27,7 +27,7 @@ #ifdef __KERNEL__ #include <linux/file.h> -#include <mach/nvmap.h> +#include <linux/nvmap.h> #endif enum { |