Commit 12fc11bc authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/tegra/for-5.13-rc1' of ssh://git.freedesktop.org/git/tegra/linux into drm-next

drm/tegra: Changes for v5.13-rc1

The changes this time around contain a couple of fixes for host1x along
with some improvements for Tegra DRM. Most notably the Tegra DRM driver
now supports the hardware cursor on Tegra186 and later, more correctly
reflects the capabilities of the display pipelines on various Tegra SoC
generations and knows how to deal with the dGPU sector layout by using
framebuffer modifiers.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210401164430.3349105-1-thierry.reding@gmail.com
parents a1a1ca70 7b6f8467
...@@ -832,10 +832,14 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm, ...@@ -832,10 +832,14 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
return &plane->base; return &plane->base;
} }
static const u32 tegra_cursor_plane_formats[] = { static const u32 tegra_legacy_cursor_plane_formats[] = {
DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBA8888,
}; };
static const u32 tegra_cursor_plane_formats[] = {
DRM_FORMAT_ARGB8888,
};
static int tegra_cursor_atomic_check(struct drm_plane *plane, static int tegra_cursor_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state) struct drm_atomic_state *state)
{ {
...@@ -875,12 +879,24 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane, ...@@ -875,12 +879,24 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
plane); plane);
struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
struct tegra_dc *dc = to_tegra_dc(new_state->crtc); struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
u32 value = CURSOR_CLIP_DISPLAY; struct tegra_drm *tegra = plane->dev->dev_private;
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
u64 dma_mask = *dc->dev->dma_mask;
#endif
unsigned int x, y;
u32 value = 0;
/* rien ne va plus */ /* rien ne va plus */
if (!new_state->crtc || !new_state->fb) if (!new_state->crtc || !new_state->fb)
return; return;
/*
* Legacy display supports hardware clipping of the cursor, but
* nvdisplay relies on software to clip the cursor to the screen.
*/
if (!dc->soc->has_nvdisplay)
value |= CURSOR_CLIP_DISPLAY;
switch (new_state->crtc_w) { switch (new_state->crtc_w) {
case 32: case 32:
value |= CURSOR_SIZE_32x32; value |= CURSOR_SIZE_32x32;
...@@ -908,7 +924,7 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane, ...@@ -908,7 +924,7 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
value = (tegra_plane_state->iova[0] >> 32) & 0x3; value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
#endif #endif
...@@ -920,15 +936,39 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane, ...@@ -920,15 +936,39 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane,
value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL); value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
value &= ~CURSOR_DST_BLEND_MASK; value &= ~CURSOR_DST_BLEND_MASK;
value &= ~CURSOR_SRC_BLEND_MASK; value &= ~CURSOR_SRC_BLEND_MASK;
value |= CURSOR_MODE_NORMAL;
if (dc->soc->has_nvdisplay)
value &= ~CURSOR_COMPOSITION_MODE_XOR;
else
value |= CURSOR_MODE_NORMAL;
value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC; value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
value |= CURSOR_SRC_BLEND_K1_TIMES_SRC; value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
value |= CURSOR_ALPHA; value |= CURSOR_ALPHA;
tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
/* nvdisplay relies on software for clipping */
if (dc->soc->has_nvdisplay) {
struct drm_rect src;
x = new_state->dst.x1;
y = new_state->dst.y1;
drm_rect_fp_to_int(&src, &new_state->src);
value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
value = (drm_rect_height(&src) & tegra->vmask) << 16 |
(drm_rect_width(&src) & tegra->hmask);
tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
} else {
x = new_state->crtc_x;
y = new_state->crtc_y;
}
/* position the cursor */ /* position the cursor */
value = (new_state->crtc_y & 0x3fff) << 16 | value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
(new_state->crtc_x & 0x3fff);
tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
} }
...@@ -982,8 +1022,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, ...@@ -982,8 +1022,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
plane->index = 6; plane->index = 6;
plane->dc = dc; plane->dc = dc;
num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); if (!dc->soc->has_nvdisplay) {
formats = tegra_cursor_plane_formats; num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
formats = tegra_legacy_cursor_plane_formats;
} else {
num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
formats = tegra_cursor_plane_formats;
}
err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
&tegra_plane_funcs, formats, &tegra_plane_funcs, formats,
...@@ -2035,6 +2080,16 @@ static bool tegra_dc_has_window_groups(struct tegra_dc *dc) ...@@ -2035,6 +2080,16 @@ static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
return false; return false;
} }
static int tegra_dc_early_init(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->host);
struct tegra_drm *tegra = drm->dev_private;
tegra->num_crtcs++;
return 0;
}
static int tegra_dc_init(struct host1x_client *client) static int tegra_dc_init(struct host1x_client *client)
{ {
struct drm_device *drm = dev_get_drvdata(client->host); struct drm_device *drm = dev_get_drvdata(client->host);
...@@ -2045,6 +2100,12 @@ static int tegra_dc_init(struct host1x_client *client) ...@@ -2045,6 +2100,12 @@ static int tegra_dc_init(struct host1x_client *client)
struct drm_plane *cursor = NULL; struct drm_plane *cursor = NULL;
int err; int err;
/*
* DC has been reset by now, so VBLANK syncpoint can be released
* for general use.
*/
host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);
/* /*
* XXX do not register DCs with no window groups because we cannot * XXX do not register DCs with no window groups because we cannot
* assign a primary plane to them, which in turn will cause KMS to * assign a primary plane to them, which in turn will cause KMS to
...@@ -2111,6 +2172,12 @@ static int tegra_dc_init(struct host1x_client *client) ...@@ -2111,6 +2172,12 @@ static int tegra_dc_init(struct host1x_client *client)
if (dc->soc->pitch_align > tegra->pitch_align) if (dc->soc->pitch_align > tegra->pitch_align)
tegra->pitch_align = dc->soc->pitch_align; tegra->pitch_align = dc->soc->pitch_align;
/* track maximum resolution */
if (dc->soc->has_nvdisplay)
drm->mode_config.max_width = drm->mode_config.max_height = 16384;
else
drm->mode_config.max_width = drm->mode_config.max_height = 4096;
err = tegra_dc_rgb_init(drm, dc); err = tegra_dc_rgb_init(drm, dc);
if (err < 0 && err != -ENODEV) { if (err < 0 && err != -ENODEV) {
dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
...@@ -2141,7 +2208,7 @@ static int tegra_dc_init(struct host1x_client *client) ...@@ -2141,7 +2208,7 @@ static int tegra_dc_init(struct host1x_client *client)
drm_plane_cleanup(primary); drm_plane_cleanup(primary);
host1x_client_iommu_detach(client); host1x_client_iommu_detach(client);
host1x_syncpt_free(dc->syncpt); host1x_syncpt_put(dc->syncpt);
return err; return err;
} }
...@@ -2166,7 +2233,17 @@ static int tegra_dc_exit(struct host1x_client *client) ...@@ -2166,7 +2233,17 @@ static int tegra_dc_exit(struct host1x_client *client)
} }
host1x_client_iommu_detach(client); host1x_client_iommu_detach(client);
host1x_syncpt_free(dc->syncpt); host1x_syncpt_put(dc->syncpt);
return 0;
}
static int tegra_dc_late_exit(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->host);
struct tegra_drm *tegra = drm->dev_private;
tegra->num_crtcs--;
return 0; return 0;
} }
...@@ -2235,8 +2312,10 @@ static int tegra_dc_runtime_resume(struct host1x_client *client) ...@@ -2235,8 +2312,10 @@ static int tegra_dc_runtime_resume(struct host1x_client *client)
} }
static const struct host1x_client_ops dc_client_ops = { static const struct host1x_client_ops dc_client_ops = {
.early_init = tegra_dc_early_init,
.init = tegra_dc_init, .init = tegra_dc_init,
.exit = tegra_dc_exit, .exit = tegra_dc_exit,
.late_exit = tegra_dc_late_exit,
.suspend = tegra_dc_runtime_suspend, .suspend = tegra_dc_runtime_suspend,
.resume = tegra_dc_runtime_resume, .resume = tegra_dc_runtime_resume,
}; };
...@@ -2246,6 +2325,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { ...@@ -2246,6 +2325,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
.supports_interlacing = false, .supports_interlacing = false,
.supports_cursor = false, .supports_cursor = false,
.supports_block_linear = false, .supports_block_linear = false,
.supports_sector_layout = false,
.has_legacy_blending = true, .has_legacy_blending = true,
.pitch_align = 8, .pitch_align = 8,
.has_powergate = false, .has_powergate = false,
...@@ -2265,6 +2345,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { ...@@ -2265,6 +2345,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
.supports_interlacing = false, .supports_interlacing = false,
.supports_cursor = false, .supports_cursor = false,
.supports_block_linear = false, .supports_block_linear = false,
.supports_sector_layout = false,
.has_legacy_blending = true, .has_legacy_blending = true,
.pitch_align = 8, .pitch_align = 8,
.has_powergate = false, .has_powergate = false,
...@@ -2284,6 +2365,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { ...@@ -2284,6 +2365,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
.supports_interlacing = false, .supports_interlacing = false,
.supports_cursor = false, .supports_cursor = false,
.supports_block_linear = false, .supports_block_linear = false,
.supports_sector_layout = false,
.has_legacy_blending = true, .has_legacy_blending = true,
.pitch_align = 64, .pitch_align = 64,
.has_powergate = true, .has_powergate = true,
...@@ -2303,6 +2385,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = { ...@@ -2303,6 +2385,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
.supports_interlacing = true, .supports_interlacing = true,
.supports_cursor = true, .supports_cursor = true,
.supports_block_linear = true, .supports_block_linear = true,
.supports_sector_layout = false,
.has_legacy_blending = false, .has_legacy_blending = false,
.pitch_align = 64, .pitch_align = 64,
.has_powergate = true, .has_powergate = true,
...@@ -2322,6 +2405,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = { ...@@ -2322,6 +2405,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
.supports_interlacing = true, .supports_interlacing = true,
.supports_cursor = true, .supports_cursor = true,
.supports_block_linear = true, .supports_block_linear = true,
.supports_sector_layout = false,
.has_legacy_blending = false, .has_legacy_blending = false,
.pitch_align = 64, .pitch_align = 64,
.has_powergate = true, .has_powergate = true,
...@@ -2375,6 +2459,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = { ...@@ -2375,6 +2459,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
.supports_interlacing = true, .supports_interlacing = true,
.supports_cursor = true, .supports_cursor = true,
.supports_block_linear = true, .supports_block_linear = true,
.supports_sector_layout = false,
.has_legacy_blending = false, .has_legacy_blending = false,
.pitch_align = 64, .pitch_align = 64,
.has_powergate = false, .has_powergate = false,
...@@ -2423,6 +2508,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = { ...@@ -2423,6 +2508,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
.supports_interlacing = true, .supports_interlacing = true,
.supports_cursor = true, .supports_cursor = true,
.supports_block_linear = true, .supports_block_linear = true,
.supports_sector_layout = true,
.has_legacy_blending = false, .has_legacy_blending = false,
.pitch_align = 64, .pitch_align = 64,
.has_powergate = false, .has_powergate = false,
...@@ -2532,9 +2618,16 @@ static int tegra_dc_couple(struct tegra_dc *dc) ...@@ -2532,9 +2618,16 @@ static int tegra_dc_couple(struct tegra_dc *dc)
static int tegra_dc_probe(struct platform_device *pdev) static int tegra_dc_probe(struct platform_device *pdev)
{ {
u64 dma_mask = dma_get_mask(pdev->dev.parent);
struct tegra_dc *dc; struct tegra_dc *dc;
int err; int err;
err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
return err;
}
dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL); dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
if (!dc) if (!dc)
return -ENOMEM; return -ENOMEM;
......
...@@ -52,6 +52,7 @@ struct tegra_dc_soc_info { ...@@ -52,6 +52,7 @@ struct tegra_dc_soc_info {
bool supports_interlacing; bool supports_interlacing;
bool supports_cursor; bool supports_cursor;
bool supports_block_linear; bool supports_block_linear;
bool supports_sector_layout;
bool has_legacy_blending; bool has_legacy_blending;
unsigned int pitch_align; unsigned int pitch_align;
bool has_powergate; bool has_powergate;
...@@ -511,6 +512,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); ...@@ -511,6 +512,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define DC_DISP_CURSOR_START_ADDR_HI 0x4ec #define DC_DISP_CURSOR_START_ADDR_HI 0x4ec
#define DC_DISP_BLEND_CURSOR_CONTROL 0x4f1 #define DC_DISP_BLEND_CURSOR_CONTROL 0x4f1
#define CURSOR_COMPOSITION_MODE_BLEND (0 << 25)
#define CURSOR_COMPOSITION_MODE_XOR (1 << 25)
#define CURSOR_MODE_LEGACY (0 << 24) #define CURSOR_MODE_LEGACY (0 << 24)
#define CURSOR_MODE_NORMAL (1 << 24) #define CURSOR_MODE_NORMAL (1 << 24)
#define CURSOR_DST_BLEND_ZERO (0 << 16) #define CURSOR_DST_BLEND_ZERO (0 << 16)
...@@ -705,6 +708,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); ...@@ -705,6 +708,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define PROTOCOL_MASK (0xf << 8) #define PROTOCOL_MASK (0xf << 8)
#define PROTOCOL_SINGLE_TMDS_A (0x1 << 8) #define PROTOCOL_SINGLE_TMDS_A (0x1 << 8)
#define DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR 0x442
#define DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR 0x446
#define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL 0x702 #define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL 0x702
#define OWNER_MASK (0xf << 0) #define OWNER_MASK (0xf << 0)
#define OWNER(x) (((x) & 0xf) << 0) #define OWNER(x) (((x) & 0xf) << 0)
......
...@@ -174,7 +174,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, ...@@ -174,7 +174,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_syncpt syncpt; struct drm_tegra_syncpt syncpt;
struct host1x *host1x = dev_get_drvdata(drm->dev->parent); struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
struct drm_gem_object **refs; struct drm_gem_object **refs;
struct host1x_syncpt *sp; struct host1x_syncpt *sp = NULL;
struct host1x_job *job; struct host1x_job *job;
unsigned int num_refs; unsigned int num_refs;
int err; int err;
...@@ -301,8 +301,8 @@ int tegra_drm_submit(struct tegra_drm_context *context, ...@@ -301,8 +301,8 @@ int tegra_drm_submit(struct tegra_drm_context *context,
goto fail; goto fail;
} }
/* check whether syncpoint ID is valid */ /* Syncpoint ref will be dropped on job release. */
sp = host1x_syncpt_get(host1x, syncpt.id); sp = host1x_syncpt_get_by_id(host1x, syncpt.id);
if (!sp) { if (!sp) {
err = -ENOENT; err = -ENOENT;
goto fail; goto fail;
...@@ -311,7 +311,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, ...@@ -311,7 +311,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
job->is_addr_reg = context->client->ops->is_addr_reg; job->is_addr_reg = context->client->ops->is_addr_reg;
job->is_valid_class = context->client->ops->is_valid_class; job->is_valid_class = context->client->ops->is_valid_class;
job->syncpt_incrs = syncpt.incrs; job->syncpt_incrs = syncpt.incrs;
job->syncpt_id = syncpt.id; job->syncpt = sp;
job->timeout = 10000; job->timeout = 10000;
if (args->timeout && args->timeout < 10000) if (args->timeout && args->timeout < 10000)
...@@ -383,7 +383,7 @@ static int tegra_syncpt_read(struct drm_device *drm, void *data, ...@@ -383,7 +383,7 @@ static int tegra_syncpt_read(struct drm_device *drm, void *data,
struct drm_tegra_syncpt_read *args = data; struct drm_tegra_syncpt_read *args = data;
struct host1x_syncpt *sp; struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host, args->id); sp = host1x_syncpt_get_by_id_noref(host, args->id);
if (!sp) if (!sp)
return -EINVAL; return -EINVAL;
...@@ -398,7 +398,7 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data, ...@@ -398,7 +398,7 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data,
struct drm_tegra_syncpt_incr *args = data; struct drm_tegra_syncpt_incr *args = data;
struct host1x_syncpt *sp; struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host1x, args->id); sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
if (!sp) if (!sp)
return -EINVAL; return -EINVAL;
...@@ -412,7 +412,7 @@ static int tegra_syncpt_wait(struct drm_device *drm, void *data, ...@@ -412,7 +412,7 @@ static int tegra_syncpt_wait(struct drm_device *drm, void *data,
struct drm_tegra_syncpt_wait *args = data; struct drm_tegra_syncpt_wait *args = data;
struct host1x_syncpt *sp; struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host1x, args->id); sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
if (!sp) if (!sp)
return -EINVAL; return -EINVAL;
...@@ -1121,9 +1121,8 @@ static int host1x_drm_probe(struct host1x_device *dev) ...@@ -1121,9 +1121,8 @@ static int host1x_drm_probe(struct host1x_device *dev)
drm->mode_config.min_width = 0; drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0; drm->mode_config.min_height = 0;
drm->mode_config.max_width = 0;
drm->mode_config.max_width = 4096; drm->mode_config.max_height = 0;
drm->mode_config.max_height = 4096;
drm->mode_config.allow_fb_modifiers = true; drm->mode_config.allow_fb_modifiers = true;
...@@ -1142,6 +1141,14 @@ static int host1x_drm_probe(struct host1x_device *dev) ...@@ -1142,6 +1141,14 @@ static int host1x_drm_probe(struct host1x_device *dev)
if (err < 0) if (err < 0)
goto fbdev; goto fbdev;
/*
* Now that all display controller have been initialized, the maximum
* supported resolution is known and the bitmask for horizontal and
* vertical bitfields can be computed.
*/
tegra->hmask = drm->mode_config.max_width - 1;
tegra->vmask = drm->mode_config.max_height - 1;
if (tegra->use_explicit_iommu) { if (tegra->use_explicit_iommu) {
u64 carveout_start, carveout_end, gem_start, gem_end; u64 carveout_start, carveout_end, gem_start, gem_end;
u64 dma_mask = dma_get_mask(&dev->dev); u64 dma_mask = dma_get_mask(&dev->dev);
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
#include "hub.h" #include "hub.h"
#include "trace.h" #include "trace.h"
/* XXX move to include/uapi/drm/drm_fourcc.h? */
#define DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT BIT(22)
struct reset_control; struct reset_control;
#ifdef CONFIG_DRM_FBDEV_EMULATION #ifdef CONFIG_DRM_FBDEV_EMULATION
...@@ -54,7 +57,9 @@ struct tegra_drm { ...@@ -54,7 +57,9 @@ struct tegra_drm {
struct tegra_fbdev *fbdev; struct tegra_fbdev *fbdev;
#endif #endif
unsigned int hmask, vmask;
unsigned int pitch_align; unsigned int pitch_align;
unsigned int num_crtcs;
struct tegra_display_hub *hub; struct tegra_display_hub *hub;
}; };
......
...@@ -44,6 +44,15 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, ...@@ -44,6 +44,15 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
{ {
uint64_t modifier = framebuffer->modifier; uint64_t modifier = framebuffer->modifier;
if ((modifier >> 56) == DRM_FORMAT_MOD_VENDOR_NVIDIA) {
if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0)
tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA;
else
tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_GPU;
modifier &= ~DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT;
}
switch (modifier) { switch (modifier) {
case DRM_FORMAT_MOD_LINEAR: case DRM_FORMAT_MOD_LINEAR:
tiling->mode = TEGRA_BO_TILING_MODE_PITCH; tiling->mode = TEGRA_BO_TILING_MODE_PITCH;
...@@ -86,6 +95,7 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, ...@@ -86,6 +95,7 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
break; break;
default: default:
DRM_DEBUG_KMS("unknown format modifier: %llx\n", modifier);
return -EINVAL; return -EINVAL;
} }
......
...@@ -21,9 +21,15 @@ enum tegra_bo_tiling_mode { ...@@ -21,9 +21,15 @@ enum tegra_bo_tiling_mode {
TEGRA_BO_TILING_MODE_BLOCK, TEGRA_BO_TILING_MODE_BLOCK,
}; };
enum tegra_bo_sector_layout {
TEGRA_BO_SECTOR_LAYOUT_TEGRA,
TEGRA_BO_SECTOR_LAYOUT_GPU,
};
struct tegra_bo_tiling { struct tegra_bo_tiling {
enum tegra_bo_tiling_mode mode; enum tegra_bo_tiling_mode mode;
unsigned long value; unsigned long value;
enum tegra_bo_sector_layout sector_layout;
}; };
struct tegra_bo { struct tegra_bo {
......
...@@ -67,7 +67,7 @@ static int gr2d_init(struct host1x_client *client) ...@@ -67,7 +67,7 @@ static int gr2d_init(struct host1x_client *client)
detach: detach:
host1x_client_iommu_detach(client); host1x_client_iommu_detach(client);
free: free:
host1x_syncpt_free(client->syncpts[0]); host1x_syncpt_put(client->syncpts[0]);
put: put:
host1x_channel_put(gr2d->channel); host1x_channel_put(gr2d->channel);
return err; return err;
...@@ -86,7 +86,7 @@ static int gr2d_exit(struct host1x_client *client) ...@@ -86,7 +86,7 @@ static int gr2d_exit(struct host1x_client *client)
return err; return err;
host1x_client_iommu_detach(client); host1x_client_iommu_detach(client);
host1x_syncpt_free(client->syncpts[0]); host1x_syncpt_put(client->syncpts[0]);
host1x_channel_put(gr2d->channel); host1x_channel_put(gr2d->channel);
return 0; return 0;
......
...@@ -76,7 +76,7 @@ static int gr3d_init(struct host1x_client *client) ...@@ -76,7 +76,7 @@ static int gr3d_init(struct host1x_client *client)
detach: detach:
host1x_client_iommu_detach(client); host1x_client_iommu_detach(client);
free: free:
host1x_syncpt_free(client->syncpts[0]); host1x_syncpt_put(client->syncpts[0]);
put: put:
host1x_channel_put(gr3d->channel); host1x_channel_put(gr3d->channel);
return err; return err;
...@@ -94,7 +94,7 @@ static int gr3d_exit(struct host1x_client *client) ...@@ -94,7 +94,7 @@ static int gr3d_exit(struct host1x_client *client)
return err; return err;
host1x_client_iommu_detach(client); host1x_client_iommu_detach(client);
host1x_syncpt_free(client->syncpts[0]); host1x_syncpt_put(client->syncpts[0]);
host1x_channel_put(gr3d->channel); host1x_channel_put(gr3d->channel);
return 0; return 0;
......
...@@ -55,6 +55,18 @@ static const u64 tegra_shared_plane_modifiers[] = { ...@@ -55,6 +55,18 @@ static const u64 tegra_shared_plane_modifiers[] = {
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
/*
* The GPU sector layout is only supported on Tegra194, but these will
* be filtered out later on by ->format_mod_supported() on SoCs where
* it isn't supported.
*/
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
/* sentinel */
DRM_FORMAT_MOD_INVALID DRM_FORMAT_MOD_INVALID
}; };
...@@ -366,6 +378,12 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane, ...@@ -366,6 +378,12 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
return -EINVAL; return -EINVAL;
} }
if (tiling->sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU &&
!dc->soc->supports_sector_layout) {
DRM_ERROR("hardware doesn't support GPU sector layout\n");
return -EINVAL;
}
/* /*
* Tegra doesn't support different strides for U and V planes so we * Tegra doesn't support different strides for U and V planes so we
* error out if the user tries to display a framebuffer with such a * error out if the user tries to display a framebuffer with such a
...@@ -485,6 +503,16 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane, ...@@ -485,6 +503,16 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
base = tegra_plane_state->iova[0] + fb->offsets[0]; base = tegra_plane_state->iova[0] + fb->offsets[0];
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
/*
* Physical address bit 39 in Tegra194 is used as a switch for special
* logic that swizzles the memory using either the legacy Tegra or the
* dGPU sector layout.
*/
if (tegra_plane_state->tiling.sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU)
base |= BIT(39);
#endif
tegra_plane_writel(p, tegra_plane_state->format, DC_WIN_COLOR_DEPTH); tegra_plane_writel(p, tegra_plane_state->format, DC_WIN_COLOR_DEPTH);
tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS); tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
...@@ -562,9 +590,8 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, ...@@ -562,9 +590,8 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
struct tegra_drm *tegra = drm->dev_private; struct tegra_drm *tegra = drm->dev_private;
struct tegra_display_hub *hub = tegra->hub; struct tegra_display_hub *hub = tegra->hub;
/* planes can be assigned to arbitrary CRTCs */
unsigned int possible_crtcs = 0x7;
struct tegra_shared_plane *plane; struct tegra_shared_plane *plane;
unsigned int possible_crtcs;
unsigned int num_formats; unsigned int num_formats;
const u64 *modifiers; const u64 *modifiers;
struct drm_plane *p; struct drm_plane *p;
...@@ -583,6 +610,9 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, ...@@ -583,6 +610,9 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
p = &plane->base.base; p = &plane->base.base;
/* planes can be assigned to arbitrary CRTCs */
possible_crtcs = BIT(tegra->num_crtcs) - 1;
num_formats = ARRAY_SIZE(tegra_shared_plane_formats); num_formats = ARRAY_SIZE(tegra_shared_plane_formats);
formats = tegra_shared_plane_formats; formats = tegra_shared_plane_formats;
modifiers = tegra_shared_plane_modifiers; modifiers = tegra_shared_plane_modifiers;
...@@ -848,12 +878,19 @@ static const struct host1x_client_ops tegra_display_hub_ops = { ...@@ -848,12 +878,19 @@ static const struct host1x_client_ops tegra_display_hub_ops = {
static int tegra_display_hub_probe(struct platform_device *pdev) static int tegra_display_hub_probe(struct platform_device *pdev)
{ {
u64 dma_mask = dma_get_mask(pdev->dev.parent);
struct device_node *child = NULL; struct device_node *child = NULL;
struct tegra_display_hub *hub; struct tegra_display_hub *hub;
struct clk *clk; struct clk *clk;
unsigned int i; unsigned int i;
int err; int err;
err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
return err;
}
hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL); hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
if (!hub) if (!hub)
return -ENOMEM; return -ENOMEM;
......
...@@ -83,6 +83,22 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, ...@@ -83,6 +83,22 @@ static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
kfree(state); kfree(state);
} }
static bool tegra_plane_supports_sector_layout(struct drm_plane *plane)
{
struct drm_crtc *crtc;
drm_for_each_crtc(crtc, plane->dev) {
if (plane->possible_crtcs & drm_crtc_mask(crtc)) {
struct tegra_dc *dc = to_tegra_dc(crtc);
if (!dc->soc->supports_sector_layout)
return false;
}
}
return true;
}
static bool tegra_plane_format_mod_supported(struct drm_plane *plane, static bool tegra_plane_format_mod_supported(struct drm_plane *plane,
uint32_t format, uint32_t format,
uint64_t modifier) uint64_t modifier)
...@@ -92,6 +108,14 @@ static bool tegra_plane_format_mod_supported(struct drm_plane *plane, ...@@ -92,6 +108,14 @@ static bool tegra_plane_format_mod_supported(struct drm_plane *plane,
if (modifier == DRM_FORMAT_MOD_LINEAR) if (modifier == DRM_FORMAT_MOD_LINEAR)
return true; return true;
/* check for the sector layout bit */
if ((modifier >> 56) == DRM_FORMAT_MOD_VENDOR_NVIDIA) {
if (modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) {
if (!tegra_plane_supports_sector_layout(plane))
return false;
}
}
if (info->num_planes == 1) if (info->num_planes == 1)
return true; return true;
...@@ -119,6 +143,14 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state) ...@@ -119,6 +143,14 @@ static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
dma_addr_t phys_addr, *phys; dma_addr_t phys_addr, *phys;
struct sg_table *sgt; struct sg_table *sgt;
/*
* If we're not attached to a domain, we already stored the
* physical address when the buffer was allocated. If we're
* part of a group that's shared between all display
* controllers, we've also already mapped the framebuffer
* through the SMMU. In both cases we can short-circuit the
* code below and retrieve the stored IOV address.
*/
if (!domain || dc->client.group) if (!domain || dc->client.group)
phys = &phys_addr; phys = &phys_addr;
else else
......
...@@ -214,7 +214,7 @@ static int vic_init(struct host1x_client *client) ...@@ -214,7 +214,7 @@ static int vic_init(struct host1x_client *client)
return 0; return 0;
free_syncpt: free_syncpt:
host1x_syncpt_free(client->syncpts[0]); host1x_syncpt_put(client->syncpts[0]);
free_channel: free_channel:
host1x_channel_put(vic->channel); host1x_channel_put(vic->channel);
detach: detach:
...@@ -238,7 +238,7 @@ static int vic_exit(struct host1x_client *client) ...@@ -238,7 +238,7 @@ static int vic_exit(struct host1x_client *client)
if (err < 0) if (err < 0)
return err; return err;
host1x_syncpt_free(client->syncpts[0]); host1x_syncpt_put(client->syncpts[0]);
host1x_channel_put(vic->channel); host1x_channel_put(vic->channel);
host1x_client_iommu_detach(client); host1x_client_iommu_detach(client);
......
...@@ -196,6 +196,17 @@ int host1x_device_init(struct host1x_device *device) ...@@ -196,6 +196,17 @@ int host1x_device_init(struct host1x_device *device)
mutex_lock(&device->clients_lock); mutex_lock(&device->clients_lock);
list_for_each_entry(client, &device->clients, list) {
if (client->ops && client->ops->early_init) {
err = client->ops->early_init(client);
if (err < 0) {
dev_err(&device->dev, "failed to early initialize %s: %d\n",
dev_name(client->dev), err);
goto teardown_late;
}
}
}
list_for_each_entry(client, &device->clients, list) { list_for_each_entry(client, &device->clients, list) {
if (client->ops && client->ops->init) { if (client->ops && client->ops->init) {
err = client->ops->init(client); err = client->ops->init(client);
...@@ -217,6 +228,14 @@ int host1x_device_init(struct host1x_device *device) ...@@ -217,6 +228,14 @@ int host1x_device_init(struct host1x_device *device)
if (client->ops->exit) if (client->ops->exit)
client->ops->exit(client); client->ops->exit(client);
/* reset client to end of list for late teardown */
client = list_entry(&device->clients, struct host1x_client, list);
teardown_late:
list_for_each_entry_continue_reverse(client, &device->clients, list)
if (client->ops->late_exit)
client->ops->late_exit(client);
mutex_unlock(&device->clients_lock); mutex_unlock(&device->clients_lock);
return err; return err;
} }
...@@ -251,6 +270,18 @@ int host1x_device_exit(struct host1x_device *device) ...@@ -251,6 +270,18 @@ int host1x_device_exit(struct host1x_device *device)
} }
} }
list_for_each_entry_reverse(client, &device->clients, list) {
if (client->ops && client->ops->late_exit) {
err = client->ops->late_exit(client);
if (err < 0) {
dev_err(&device->dev, "failed to late cleanup %s: %d\n",
dev_name(client->dev), err);
mutex_unlock(&device->clients_lock);
return err;
}
}
}
mutex_unlock(&device->clients_lock); mutex_unlock(&device->clients_lock);
return 0; return 0;
......
...@@ -273,15 +273,13 @@ static int host1x_cdma_wait_pushbuffer_space(struct host1x *host1x, ...@@ -273,15 +273,13 @@ static int host1x_cdma_wait_pushbuffer_space(struct host1x *host1x,
static void cdma_start_timer_locked(struct host1x_cdma *cdma, static void cdma_start_timer_locked(struct host1x_cdma *cdma,
struct host1x_job *job) struct host1x_job *job)
{ {
struct host1x *host = cdma_to_host1x(cdma);
if (cdma->timeout.client) { if (cdma->timeout.client) {
/* timer already started */ /* timer already started */
return; return;
} }
cdma->timeout.client = job->client; cdma->timeout.client = job->client;
cdma->timeout.syncpt = host1x_syncpt_get(host, job->syncpt_id); cdma->timeout.syncpt = job->syncpt;
cdma->timeout.syncpt_val = job->syncpt_end; cdma->timeout.syncpt_val = job->syncpt_end;
cdma->timeout.start_ktime = ktime_get(); cdma->timeout.start_ktime = ktime_get();
...@@ -312,7 +310,6 @@ static void stop_cdma_timer_locked(struct host1x_cdma *cdma) ...@@ -312,7 +310,6 @@ static void stop_cdma_timer_locked(struct host1x_cdma *cdma)
static void update_cdma_locked(struct host1x_cdma *cdma) static void update_cdma_locked(struct host1x_cdma *cdma)
{ {
bool signal = false; bool signal = false;
struct host1x *host1x = cdma_to_host1x(cdma);
struct host1x_job *job, *n; struct host1x_job *job, *n;
/* If CDMA is stopped, queue is cleared and we can return */ /* If CDMA is stopped, queue is cleared and we can return */
...@@ -324,8 +321,7 @@ static void update_cdma_locked(struct host1x_cdma *cdma) ...@@ -324,8 +321,7 @@ static void update_cdma_locked(struct host1x_cdma *cdma)
* to consume as many sync queue entries as possible without blocking * to consume as many sync queue entries as possible without blocking
*/ */
list_for_each_entry_safe(job, n, &cdma->sync_queue, list) { list_for_each_entry_safe(job, n, &cdma->sync_queue, list) {
struct host1x_syncpt *sp = struct host1x_syncpt *sp = job->syncpt;
host1x_syncpt_get(host1x, job->syncpt_id);
/* Check whether this syncpt has completed, and bail if not */ /* Check whether this syncpt has completed, and bail if not */
if (!host1x_syncpt_is_expired(sp, job->syncpt_end)) { if (!host1x_syncpt_is_expired(sp, job->syncpt_end)) {
...@@ -499,8 +495,7 @@ int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job) ...@@ -499,8 +495,7 @@ int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job)
if (!cdma->timeout.initialized) { if (!cdma->timeout.initialized) {
int err; int err;
err = host1x_hw_cdma_timeout_init(host1x, cdma, err = host1x_hw_cdma_timeout_init(host1x, cdma);
job->syncpt_id);
if (err) { if (err) {
mutex_unlock(&cdma->lock); mutex_unlock(&cdma->lock);
return err; return err;
......
...@@ -69,6 +69,7 @@ static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo) ...@@ -69,6 +69,7 @@ static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
static void show_syncpts(struct host1x *m, struct output *o) static void show_syncpts(struct host1x *m, struct output *o)
{ {
struct list_head *pos;
unsigned int i; unsigned int i;
host1x_debug_output(o, "---- syncpts ----\n"); host1x_debug_output(o, "---- syncpts ----\n");
...@@ -76,12 +77,19 @@ static void show_syncpts(struct host1x *m, struct output *o) ...@@ -76,12 +77,19 @@ static void show_syncpts(struct host1x *m, struct output *o)
for (i = 0; i < host1x_syncpt_nb_pts(m); i++) { for (i = 0; i < host1x_syncpt_nb_pts(m); i++) {
u32 max = host1x_syncpt_read_max(m->syncpt + i); u32 max = host1x_syncpt_read_max(m->syncpt + i);
u32 min = host1x_syncpt_load(m->syncpt + i); u32 min = host1x_syncpt_load(m->syncpt + i);
unsigned int waiters = 0;
if (!min && !max) spin_lock(&m->syncpt[i].intr.lock);
list_for_each(pos, &m->syncpt[i].intr.wait_head)
waiters++;
spin_unlock(&m->syncpt[i].intr.lock);
if (!min && !max && !waiters)
continue; continue;
host1x_debug_output(o, "id %u (%s) min %d max %d\n", host1x_debug_output(o,
i, m->syncpt[i].name, min, max); "id %u (%s) min %d max %d (%d waiters)\n",
i, m->syncpt[i].name, min, max, waiters);
} }
for (i = 0; i < host1x_syncpt_nb_bases(m); i++) { for (i = 0; i < host1x_syncpt_nb_bases(m); i++) {
......
...@@ -77,6 +77,7 @@ static const struct host1x_info host1x01_info = { ...@@ -77,6 +77,7 @@ static const struct host1x_info host1x01_info = {
.has_hypervisor = false, .has_hypervisor = false,
.num_sid_entries = 0, .num_sid_entries = 0,
.sid_table = NULL, .sid_table = NULL,
.reserve_vblank_syncpts = true,
}; };
static const struct host1x_info host1x02_info = { static const struct host1x_info host1x02_info = {
...@@ -91,6 +92,7 @@ static const struct host1x_info host1x02_info = { ...@@ -91,6 +92,7 @@ static const struct host1x_info host1x02_info = {
.has_hypervisor = false, .has_hypervisor = false,
.num_sid_entries = 0, .num_sid_entries = 0,
.sid_table = NULL, .sid_table = NULL,
.reserve_vblank_syncpts = true,
}; };
static const struct host1x_info host1x04_info = { static const struct host1x_info host1x04_info = {
...@@ -105,6 +107,7 @@ static const struct host1x_info host1x04_info = { ...@@ -105,6 +107,7 @@ static const struct host1x_info host1x04_info = {
.has_hypervisor = false, .has_hypervisor = false,
.num_sid_entries = 0, .num_sid_entries = 0,
.sid_table = NULL, .sid_table = NULL,
.reserve_vblank_syncpts = false,
}; };
static const struct host1x_info host1x05_info = { static const struct host1x_info host1x05_info = {
...@@ -119,6 +122,7 @@ static const struct host1x_info host1x05_info = { ...@@ -119,6 +122,7 @@ static const struct host1x_info host1x05_info = {
.has_hypervisor = false, .has_hypervisor = false,
.num_sid_entries = 0, .num_sid_entries = 0,
.sid_table = NULL, .sid_table = NULL,
.reserve_vblank_syncpts = false,
}; };
static const struct host1x_sid_entry tegra186_sid_table[] = { static const struct host1x_sid_entry tegra186_sid_table[] = {
...@@ -142,6 +146,7 @@ static const struct host1x_info host1x06_info = { ...@@ -142,6 +146,7 @@ static const struct host1x_info host1x06_info = {
.has_hypervisor = true, .has_hypervisor = true,
.num_sid_entries = ARRAY_SIZE(tegra186_sid_table), .num_sid_entries = ARRAY_SIZE(tegra186_sid_table),
.sid_table = tegra186_sid_table, .sid_table = tegra186_sid_table,
.reserve_vblank_syncpts = false,
}; };
static const struct host1x_sid_entry tegra194_sid_table[] = { static const struct host1x_sid_entry tegra194_sid_table[] = {
...@@ -165,6 +170,7 @@ static const struct host1x_info host1x07_info = { ...@@ -165,6 +170,7 @@ static const struct host1x_info host1x07_info = {
.has_hypervisor = true, .has_hypervisor = true,
.num_sid_entries = ARRAY_SIZE(tegra194_sid_table), .num_sid_entries = ARRAY_SIZE(tegra194_sid_table),
.sid_table = tegra194_sid_table, .sid_table = tegra194_sid_table,
.reserve_vblank_syncpts = false,
}; };
static const struct of_device_id host1x_of_match[] = { static const struct of_device_id host1x_of_match[] = {
......
...@@ -37,7 +37,7 @@ struct host1x_cdma_ops { ...@@ -37,7 +37,7 @@ struct host1x_cdma_ops {
void (*start)(struct host1x_cdma *cdma); void (*start)(struct host1x_cdma *cdma);
void (*stop)(struct host1x_cdma *cdma); void (*stop)(struct host1x_cdma *cdma);
void (*flush)(struct host1x_cdma *cdma); void (*flush)(struct host1x_cdma *cdma);
int (*timeout_init)(struct host1x_cdma *cdma, unsigned int syncpt); int (*timeout_init)(struct host1x_cdma *cdma);
void (*timeout_destroy)(struct host1x_cdma *cdma); void (*timeout_destroy)(struct host1x_cdma *cdma);
void (*freeze)(struct host1x_cdma *cdma); void (*freeze)(struct host1x_cdma *cdma);
void (*resume)(struct host1x_cdma *cdma, u32 getptr); void (*resume)(struct host1x_cdma *cdma, u32 getptr);
...@@ -101,6 +101,12 @@ struct host1x_info { ...@@ -101,6 +101,12 @@ struct host1x_info {
bool has_hypervisor; /* has hypervisor registers */ bool has_hypervisor; /* has hypervisor registers */
unsigned int num_sid_entries; unsigned int num_sid_entries;
const struct host1x_sid_entry *sid_table; const struct host1x_sid_entry *sid_table;
/*
* On T20-T148, the boot chain may setup DC to increment syncpoints
* 26/27 on VBLANK. As such we cannot use these syncpoints until
* the display driver disables VBLANK increments.
*/
bool reserve_vblank_syncpts;
}; };
struct host1x { struct host1x {
...@@ -261,10 +267,9 @@ static inline void host1x_hw_cdma_flush(struct host1x *host, ...@@ -261,10 +267,9 @@ static inline void host1x_hw_cdma_flush(struct host1x *host,
} }
static inline int host1x_hw_cdma_timeout_init(struct host1x *host, static inline int host1x_hw_cdma_timeout_init(struct host1x *host,
struct host1x_cdma *cdma, struct host1x_cdma *cdma)
unsigned int syncpt)
{ {
return host->cdma_op->timeout_init(cdma, syncpt); return host->cdma_op->timeout_init(cdma);
} }
static inline void host1x_hw_cdma_timeout_destroy(struct host1x *host, static inline void host1x_hw_cdma_timeout_destroy(struct host1x *host,
......
...@@ -295,7 +295,7 @@ static void cdma_timeout_handler(struct work_struct *work) ...@@ -295,7 +295,7 @@ static void cdma_timeout_handler(struct work_struct *work)
/* /*
* Init timeout resources * Init timeout resources
*/ */
static int cdma_timeout_init(struct host1x_cdma *cdma, unsigned int syncpt) static int cdma_timeout_init(struct host1x_cdma *cdma)
{ {
INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler); INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler);
cdma->timeout.initialized = true; cdma->timeout.initialized = true;
......
...@@ -86,8 +86,7 @@ static void submit_gathers(struct host1x_job *job) ...@@ -86,8 +86,7 @@ static void submit_gathers(struct host1x_job *job)
static inline void synchronize_syncpt_base(struct host1x_job *job) static inline void synchronize_syncpt_base(struct host1x_job *job)
{ {
struct host1x *host = dev_get_drvdata(job->channel->dev->parent); struct host1x_syncpt *sp = job->syncpt;
struct host1x_syncpt *sp = host->syncpt + job->syncpt_id;
unsigned int id; unsigned int id;
u32 value; u32 value;
...@@ -118,7 +117,7 @@ static void host1x_channel_set_streamid(struct host1x_channel *channel) ...@@ -118,7 +117,7 @@ static void host1x_channel_set_streamid(struct host1x_channel *channel)
static int channel_submit(struct host1x_job *job) static int channel_submit(struct host1x_job *job)
{ {
struct host1x_channel *ch = job->channel; struct host1x_channel *ch = job->channel;
struct host1x_syncpt *sp; struct host1x_syncpt *sp = job->syncpt;
u32 user_syncpt_incrs = job->syncpt_incrs; u32 user_syncpt_incrs = job->syncpt_incrs;
u32 prev_max = 0; u32 prev_max = 0;
u32 syncval; u32 syncval;
...@@ -126,10 +125,9 @@ static int channel_submit(struct host1x_job *job) ...@@ -126,10 +125,9 @@ static int channel_submit(struct host1x_job *job)
struct host1x_waitlist *completed_waiter = NULL; struct host1x_waitlist *completed_waiter = NULL;
struct host1x *host = dev_get_drvdata(ch->dev->parent); struct host1x *host = dev_get_drvdata(ch->dev->parent);
sp = host->syncpt + job->syncpt_id;
trace_host1x_channel_submit(dev_name(ch->dev), trace_host1x_channel_submit(dev_name(ch->dev),
job->num_gathers, job->num_relocs, job->num_gathers, job->num_relocs,
job->syncpt_id, job->syncpt_incrs); job->syncpt->id, job->syncpt_incrs);
/* before error checks, return current max */ /* before error checks, return current max */
prev_max = job->syncpt_end = host1x_syncpt_read_max(sp); prev_max = job->syncpt_end = host1x_syncpt_read_max(sp);
...@@ -163,7 +161,7 @@ static int channel_submit(struct host1x_job *job) ...@@ -163,7 +161,7 @@ static int channel_submit(struct host1x_job *job)
host1x_cdma_push(&ch->cdma, host1x_cdma_push(&ch->cdma,
host1x_opcode_setclass(HOST1X_CLASS_HOST1X, host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
host1x_uclass_wait_syncpt_r(), 1), host1x_uclass_wait_syncpt_r(), 1),
host1x_class_host_wait_syncpt(job->syncpt_id, host1x_class_host_wait_syncpt(job->syncpt->id,
host1x_syncpt_read_max(sp))); host1x_syncpt_read_max(sp)));
} }
......
...@@ -204,7 +204,7 @@ static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma) ...@@ -204,7 +204,7 @@ static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma)
unsigned int i; unsigned int i;
host1x_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d, first_get=%08x, timeout=%d num_slots=%d, num_handles=%d\n", host1x_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d, first_get=%08x, timeout=%d num_slots=%d, num_handles=%d\n",
job, job->syncpt_id, job->syncpt_end, job, job->syncpt->id, job->syncpt_end,
job->first_get, job->timeout, job->first_get, job->timeout,
job->num_slots, job->num_unpins); job->num_slots, job->num_unpins);
......
...@@ -29,6 +29,6 @@ ...@@ -29,6 +29,6 @@
#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(x) (0x652c + 4 * (x)) #define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(x) (0x652c + 4 * (x))
#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(x) (0x6590 + 4 * (x)) #define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(x) (0x6590 + 4 * (x))
#define HOST1X_SYNC_SYNCPT(x) (0x8080 + 4 * (x)) #define HOST1X_SYNC_SYNCPT(x) (0x8080 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_INT_THRESH(x) (0x8d00 + 4 * (x)) #define HOST1X_SYNC_SYNCPT_INT_THRESH(x) (0x9980 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_CH_APP(x) (0xa604 + 4 * (x)) #define HOST1X_SYNC_SYNCPT_CH_APP(x) (0xa604 + 4 * (x))
#define HOST1X_SYNC_SYNCPT_CH_APP_CH(v) (((v) & 0x3f) << 8) #define HOST1X_SYNC_SYNCPT_CH_APP_CH(v) (((v) & 0x3f) << 8)
...@@ -235,25 +235,37 @@ int host1x_intr_add_action(struct host1x *host, struct host1x_syncpt *syncpt, ...@@ -235,25 +235,37 @@ int host1x_intr_add_action(struct host1x *host, struct host1x_syncpt *syncpt,
host1x_hw_intr_enable_syncpt_intr(host, syncpt->id); host1x_hw_intr_enable_syncpt_intr(host, syncpt->id);
} }
spin_unlock(&syncpt->intr.lock);
if (ref) if (ref)
*ref = waiter; *ref = waiter;
spin_unlock(&syncpt->intr.lock);
return 0; return 0;
} }
void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref) void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref,
bool flush)
{ {
struct host1x_waitlist *waiter = ref; struct host1x_waitlist *waiter = ref;
struct host1x_syncpt *syncpt; struct host1x_syncpt *syncpt;
while (atomic_cmpxchg(&waiter->state, WLS_PENDING, WLS_CANCELLED) == atomic_cmpxchg(&waiter->state, WLS_PENDING, WLS_CANCELLED);
WLS_REMOVED)
schedule();
syncpt = host->syncpt + id; syncpt = host->syncpt + id;
(void)process_wait_list(host, syncpt,
host1x_syncpt_load(host->syncpt + id)); spin_lock(&syncpt->intr.lock);
if (atomic_cmpxchg(&waiter->state, WLS_CANCELLED, WLS_HANDLED) ==
WLS_CANCELLED) {
list_del(&waiter->list);
kref_put(&waiter->refcount, waiter_release);
}
spin_unlock(&syncpt->intr.lock);
if (flush) {
/* Wait until any concurrently executing handler has finished. */
while (atomic_read(&waiter->state) != WLS_HANDLED)
schedule();
}
kref_put(&waiter->refcount, waiter_release); kref_put(&waiter->refcount, waiter_release);
} }
......
...@@ -74,8 +74,10 @@ int host1x_intr_add_action(struct host1x *host, struct host1x_syncpt *syncpt, ...@@ -74,8 +74,10 @@ int host1x_intr_add_action(struct host1x *host, struct host1x_syncpt *syncpt,
* Unreference an action submitted to host1x_intr_add_action(). * Unreference an action submitted to host1x_intr_add_action().
* You must call this if you passed non-NULL as ref. * You must call this if you passed non-NULL as ref.
* @ref the ref returned from host1x_intr_add_action() * @ref the ref returned from host1x_intr_add_action()
* @flush wait until any pending handlers have completed before returning.
*/ */
void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref); void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref,
bool flush);
/* Initialize host1x sync point interrupt */ /* Initialize host1x sync point interrupt */
int host1x_intr_init(struct host1x *host, unsigned int irq_sync); int host1x_intr_init(struct host1x *host, unsigned int irq_sync);
......
...@@ -79,6 +79,9 @@ static void job_free(struct kref *ref) ...@@ -79,6 +79,9 @@ static void job_free(struct kref *ref)
{ {
struct host1x_job *job = container_of(ref, struct host1x_job, ref); struct host1x_job *job = container_of(ref, struct host1x_job, ref);
if (job->syncpt)
host1x_syncpt_put(job->syncpt);
kfree(job); kfree(job);
} }
...@@ -674,7 +677,7 @@ EXPORT_SYMBOL(host1x_job_unpin); ...@@ -674,7 +677,7 @@ EXPORT_SYMBOL(host1x_job_unpin);
*/ */
void host1x_job_dump(struct device *dev, struct host1x_job *job) void host1x_job_dump(struct device *dev, struct host1x_job *job)
{ {
dev_dbg(dev, " SYNCPT_ID %d\n", job->syncpt_id); dev_dbg(dev, " SYNCPT_ID %d\n", job->syncpt->id);
dev_dbg(dev, " SYNCPT_VAL %d\n", job->syncpt_end); dev_dbg(dev, " SYNCPT_VAL %d\n", job->syncpt_end);
dev_dbg(dev, " FIRST_GET 0x%x\n", job->first_get); dev_dbg(dev, " FIRST_GET 0x%x\n", job->first_get);
dev_dbg(dev, " TIMEOUT %d\n", job->timeout); dev_dbg(dev, " TIMEOUT %d\n", job->timeout);
......
...@@ -42,17 +42,32 @@ static void host1x_syncpt_base_free(struct host1x_syncpt_base *base) ...@@ -42,17 +42,32 @@ static void host1x_syncpt_base_free(struct host1x_syncpt_base *base)
base->requested = false; base->requested = false;
} }
static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, /**
struct host1x_client *client, * host1x_syncpt_alloc() - allocate a syncpoint
unsigned long flags) * @host: host1x device data
* @flags: bitfield of HOST1X_SYNCPT_* flags
* @name: name for the syncpoint for use in debug prints
*
* Allocates a hardware syncpoint for the caller's use. The caller then has
* the sole authority to mutate the syncpoint's value until it is freed again.
*
* If no free syncpoints are available, or a NULL name was specified, returns
* NULL.
*/
struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
unsigned long flags,
const char *name)
{ {
struct host1x_syncpt *sp = host->syncpt; struct host1x_syncpt *sp = host->syncpt;
char *full_name;
unsigned int i; unsigned int i;
char *name;
if (!name)
return NULL;
mutex_lock(&host->syncpt_mutex); mutex_lock(&host->syncpt_mutex);
for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++) for (i = 0; i < host->info->nb_pts && kref_read(&sp->ref); i++, sp++)
; ;
if (i >= host->info->nb_pts) if (i >= host->info->nb_pts)
...@@ -64,19 +79,19 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, ...@@ -64,19 +79,19 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
goto unlock; goto unlock;
} }
name = kasprintf(GFP_KERNEL, "%02u-%s", sp->id, full_name = kasprintf(GFP_KERNEL, "%u-%s", sp->id, name);
client ? dev_name(client->dev) : NULL); if (!full_name)
if (!name)
goto free_base; goto free_base;
sp->client = client; sp->name = full_name;
sp->name = name;
if (flags & HOST1X_SYNCPT_CLIENT_MANAGED) if (flags & HOST1X_SYNCPT_CLIENT_MANAGED)
sp->client_managed = true; sp->client_managed = true;
else else
sp->client_managed = false; sp->client_managed = false;
kref_init(&sp->ref);
mutex_unlock(&host->syncpt_mutex); mutex_unlock(&host->syncpt_mutex);
return sp; return sp;
...@@ -87,6 +102,7 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, ...@@ -87,6 +102,7 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
mutex_unlock(&host->syncpt_mutex); mutex_unlock(&host->syncpt_mutex);
return NULL; return NULL;
} }
EXPORT_SYMBOL(host1x_syncpt_alloc);
/** /**
* host1x_syncpt_id() - retrieve syncpoint ID * host1x_syncpt_id() - retrieve syncpoint ID
...@@ -294,7 +310,7 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, ...@@ -294,7 +310,7 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
} }
} }
host1x_intr_put_ref(sp->host, sp->id, ref); host1x_intr_put_ref(sp->host, sp->id, ref, true);
done: done:
return err; return err;
...@@ -307,59 +323,12 @@ EXPORT_SYMBOL(host1x_syncpt_wait); ...@@ -307,59 +323,12 @@ EXPORT_SYMBOL(host1x_syncpt_wait);
bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh) bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh)
{ {
u32 current_val; u32 current_val;
u32 future_val;
smp_rmb(); smp_rmb();
current_val = (u32)atomic_read(&sp->min_val); current_val = (u32)atomic_read(&sp->min_val);
future_val = (u32)atomic_read(&sp->max_val);
return ((current_val - thresh) & 0x80000000U) == 0U;
/* Note the use of unsigned arithmetic here (mod 1<<32).
*
* c = current_val = min_val = the current value of the syncpoint.
* t = thresh = the value we are checking
* f = future_val = max_val = the value c will reach when all
* outstanding increments have completed.
*
* Note that c always chases f until it reaches f.
*
* Dtf = (f - t)
* Dtc = (c - t)
*
* Consider all cases:
*
* A) .....c..t..f..... Dtf < Dtc need to wait
* B) .....c.....f..t.. Dtf > Dtc expired
* C) ..t..c.....f..... Dtf > Dtc expired (Dct very large)
*
* Any case where f==c: always expired (for any t). Dtf == Dcf
* Any case where t==c: always expired (for any f). Dtf >= Dtc (because Dtc==0)
* Any case where t==f!=c: always wait. Dtf < Dtc (because Dtf==0,
* Dtc!=0)
*
* Other cases:
*
* A) .....t..f..c..... Dtf < Dtc need to wait
* A) .....f..c..t..... Dtf < Dtc need to wait
* A) .....f..t..c..... Dtf > Dtc expired
*
* So:
* Dtf >= Dtc implies EXPIRED (return true)
* Dtf < Dtc implies WAIT (return false)
*
* Note: If t is expired then we *cannot* wait on it. We would wait
* forever (hang the system).
*
* Note: do NOT get clever and remove the -thresh from both sides. It
* is NOT the same.
*
* If future valueis zero, we have a client managed sync point. In that
* case we do a direct comparison.
*/
if (!host1x_syncpt_client_managed(sp))
return future_val - thresh >= current_val - thresh;
else
return (s32)(current_val - thresh) >= 0;
} }
int host1x_syncpt_init(struct host1x *host) int host1x_syncpt_init(struct host1x *host)
...@@ -401,10 +370,15 @@ int host1x_syncpt_init(struct host1x *host) ...@@ -401,10 +370,15 @@ int host1x_syncpt_init(struct host1x *host)
host1x_hw_syncpt_enable_protection(host); host1x_hw_syncpt_enable_protection(host);
/* Allocate sync point to use for clearing waits for expired fences */ /* Allocate sync point to use for clearing waits for expired fences */
host->nop_sp = host1x_syncpt_alloc(host, NULL, 0); host->nop_sp = host1x_syncpt_alloc(host, 0, "reserved-nop");
if (!host->nop_sp) if (!host->nop_sp)
return -ENOMEM; return -ENOMEM;
if (host->info->reserve_vblank_syncpts) {
kref_init(&host->syncpt[26].ref);
kref_init(&host->syncpt[27].ref);
}
return 0; return 0;
} }
...@@ -416,44 +390,50 @@ int host1x_syncpt_init(struct host1x *host) ...@@ -416,44 +390,50 @@ int host1x_syncpt_init(struct host1x *host)
* host1x client drivers can use this function to allocate a syncpoint for * host1x client drivers can use this function to allocate a syncpoint for
* subsequent use. A syncpoint returned by this function will be reserved for * subsequent use. A syncpoint returned by this function will be reserved for
* use by the client exclusively. When no longer using a syncpoint, a host1x * use by the client exclusively. When no longer using a syncpoint, a host1x
* client driver needs to release it using host1x_syncpt_free(). * client driver needs to release it using host1x_syncpt_put().
*/ */
struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client, struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client,
unsigned long flags) unsigned long flags)
{ {
struct host1x *host = dev_get_drvdata(client->host->parent); struct host1x *host = dev_get_drvdata(client->host->parent);
return host1x_syncpt_alloc(host, client, flags); return host1x_syncpt_alloc(host, flags, dev_name(client->dev));
} }
EXPORT_SYMBOL(host1x_syncpt_request); EXPORT_SYMBOL(host1x_syncpt_request);
/** static void syncpt_release(struct kref *ref)
* host1x_syncpt_free() - free a requested syncpoint
* @sp: host1x syncpoint
*
* Release a syncpoint previously allocated using host1x_syncpt_request(). A
* host1x client driver should call this when the syncpoint is no longer in
* use. Note that client drivers must ensure that the syncpoint doesn't remain
* under the control of hardware after calling this function, otherwise two
* clients may end up trying to access the same syncpoint concurrently.
*/
void host1x_syncpt_free(struct host1x_syncpt *sp)
{ {
if (!sp) struct host1x_syncpt *sp = container_of(ref, struct host1x_syncpt, ref);
return;
atomic_set(&sp->max_val, host1x_syncpt_read(sp));
mutex_lock(&sp->host->syncpt_mutex); mutex_lock(&sp->host->syncpt_mutex);
host1x_syncpt_base_free(sp->base); host1x_syncpt_base_free(sp->base);
kfree(sp->name); kfree(sp->name);
sp->base = NULL; sp->base = NULL;
sp->client = NULL;
sp->name = NULL; sp->name = NULL;
sp->client_managed = false; sp->client_managed = false;
mutex_unlock(&sp->host->syncpt_mutex); mutex_unlock(&sp->host->syncpt_mutex);
} }
EXPORT_SYMBOL(host1x_syncpt_free);
/**
* host1x_syncpt_put() - free a requested syncpoint
* @sp: host1x syncpoint
*
* Release a syncpoint previously allocated using host1x_syncpt_request(). A
* host1x client driver should call this when the syncpoint is no longer in
* use.
*/
void host1x_syncpt_put(struct host1x_syncpt *sp)
{
if (!sp)
return;
kref_put(&sp->ref, syncpt_release);
}
EXPORT_SYMBOL(host1x_syncpt_put);
void host1x_syncpt_deinit(struct host1x *host) void host1x_syncpt_deinit(struct host1x *host)
{ {
...@@ -520,16 +500,48 @@ unsigned int host1x_syncpt_nb_mlocks(struct host1x *host) ...@@ -520,16 +500,48 @@ unsigned int host1x_syncpt_nb_mlocks(struct host1x *host)
} }
/** /**
* host1x_syncpt_get() - obtain a syncpoint by ID * host1x_syncpt_get_by_id() - obtain a syncpoint by ID
* @host: host1x controller * @host: host1x controller
* @id: syncpoint ID * @id: syncpoint ID
*/ */
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id) struct host1x_syncpt *host1x_syncpt_get_by_id(struct host1x *host,
unsigned int id)
{ {
if (id >= host->info->nb_pts) if (id >= host->info->nb_pts)
return NULL; return NULL;
return host->syncpt + id; if (kref_get_unless_zero(&host->syncpt[id].ref))
return &host->syncpt[id];
else
return NULL;
}
EXPORT_SYMBOL(host1x_syncpt_get_by_id);
/**
* host1x_syncpt_get_by_id_noref() - obtain a syncpoint by ID but don't
* increase the refcount.
* @host: host1x controller
* @id: syncpoint ID
*/
struct host1x_syncpt *host1x_syncpt_get_by_id_noref(struct host1x *host,
unsigned int id)
{
if (id >= host->info->nb_pts)
return NULL;
return &host->syncpt[id];
}
EXPORT_SYMBOL(host1x_syncpt_get_by_id_noref);
/**
* host1x_syncpt_get() - increment syncpoint refcount
* @sp: syncpoint
*/
struct host1x_syncpt *host1x_syncpt_get(struct host1x_syncpt *sp)
{
kref_get(&sp->ref);
return sp;
} }
EXPORT_SYMBOL(host1x_syncpt_get); EXPORT_SYMBOL(host1x_syncpt_get);
...@@ -552,3 +564,31 @@ u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base) ...@@ -552,3 +564,31 @@ u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base)
return base->id; return base->id;
} }
EXPORT_SYMBOL(host1x_syncpt_base_id); EXPORT_SYMBOL(host1x_syncpt_base_id);
static void do_nothing(struct kref *ref)
{
}
/**
* host1x_syncpt_release_vblank_reservation() - Make VBLANK syncpoint
* available for allocation
*
* @client: host1x bus client
* @syncpt_id: syncpoint ID to make available
*
* Makes VBLANK<i> syncpoint available for allocatation if it was
* reserved at initialization time. This should be called by the display
* driver after it has ensured that any VBLANK increment programming configured
* by the boot chain has been disabled.
*/
void host1x_syncpt_release_vblank_reservation(struct host1x_client *client,
u32 syncpt_id)
{
struct host1x *host = dev_get_drvdata(client->host->parent);
if (!host->info->reserve_vblank_syncpts)
return;
kref_put(&host->syncpt[syncpt_id].ref, do_nothing);
}
EXPORT_SYMBOL(host1x_syncpt_release_vblank_reservation);
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/host1x.h> #include <linux/host1x.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/sched.h> #include <linux/sched.h>
#include "intr.h" #include "intr.h"
...@@ -26,6 +27,8 @@ struct host1x_syncpt_base { ...@@ -26,6 +27,8 @@ struct host1x_syncpt_base {
}; };
struct host1x_syncpt { struct host1x_syncpt {
struct kref ref;
unsigned int id; unsigned int id;
atomic_t min_val; atomic_t min_val;
atomic_t max_val; atomic_t max_val;
...@@ -33,7 +36,6 @@ struct host1x_syncpt { ...@@ -33,7 +36,6 @@ struct host1x_syncpt {
const char *name; const char *name;
bool client_managed; bool client_managed;
struct host1x *host; struct host1x *host;
struct host1x_client *client;
struct host1x_syncpt_base *base; struct host1x_syncpt_base *base;
/* interrupt data */ /* interrupt data */
......
...@@ -1131,8 +1131,8 @@ static void tegra_channel_host1x_syncpts_free(struct tegra_vi_channel *chan) ...@@ -1131,8 +1131,8 @@ static void tegra_channel_host1x_syncpts_free(struct tegra_vi_channel *chan)
int i; int i;
for (i = 0; i < chan->numgangports; i++) { for (i = 0; i < chan->numgangports; i++) {
host1x_syncpt_free(chan->mw_ack_sp[i]); host1x_syncpt_put(chan->mw_ack_sp[i]);
host1x_syncpt_free(chan->frame_start_sp[i]); host1x_syncpt_put(chan->frame_start_sp[i]);
} }
} }
...@@ -1177,7 +1177,7 @@ static int tegra_channel_host1x_syncpt_init(struct tegra_vi_channel *chan) ...@@ -1177,7 +1177,7 @@ static int tegra_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
mw_sp = host1x_syncpt_request(&vi->client, flags); mw_sp = host1x_syncpt_request(&vi->client, flags);
if (!mw_sp) { if (!mw_sp) {
dev_err(vi->dev, "failed to request memory ack syncpoint\n"); dev_err(vi->dev, "failed to request memory ack syncpoint\n");
host1x_syncpt_free(fs_sp); host1x_syncpt_put(fs_sp);
ret = -ENOMEM; ret = -ENOMEM;
goto free_syncpts; goto free_syncpts;
} }
......
...@@ -25,14 +25,18 @@ u64 host1x_get_dma_mask(struct host1x *host1x); ...@@ -25,14 +25,18 @@ u64 host1x_get_dma_mask(struct host1x *host1x);
/** /**
* struct host1x_client_ops - host1x client operations * struct host1x_client_ops - host1x client operations
* @early_init: host1x client early initialization code
* @init: host1x client initialization code * @init: host1x client initialization code
* @exit: host1x client tear down code * @exit: host1x client tear down code
* @late_exit: host1x client late tear down code
* @suspend: host1x client suspend code * @suspend: host1x client suspend code
* @resume: host1x client resume code * @resume: host1x client resume code
*/ */
struct host1x_client_ops { struct host1x_client_ops {
int (*early_init)(struct host1x_client *client);
int (*init)(struct host1x_client *client); int (*init)(struct host1x_client *client);
int (*exit)(struct host1x_client *client); int (*exit)(struct host1x_client *client);
int (*late_exit)(struct host1x_client *client);
int (*suspend)(struct host1x_client *client); int (*suspend)(struct host1x_client *client);
int (*resume)(struct host1x_client *client); int (*resume)(struct host1x_client *client);
}; };
...@@ -142,7 +146,9 @@ struct host1x_syncpt_base; ...@@ -142,7 +146,9 @@ struct host1x_syncpt_base;
struct host1x_syncpt; struct host1x_syncpt;
struct host1x; struct host1x;
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id); struct host1x_syncpt *host1x_syncpt_get_by_id(struct host1x *host, u32 id);
struct host1x_syncpt *host1x_syncpt_get_by_id_noref(struct host1x *host, u32 id);
struct host1x_syncpt *host1x_syncpt_get(struct host1x_syncpt *sp);
u32 host1x_syncpt_id(struct host1x_syncpt *sp); u32 host1x_syncpt_id(struct host1x_syncpt *sp);
u32 host1x_syncpt_read_min(struct host1x_syncpt *sp); u32 host1x_syncpt_read_min(struct host1x_syncpt *sp);
u32 host1x_syncpt_read_max(struct host1x_syncpt *sp); u32 host1x_syncpt_read_max(struct host1x_syncpt *sp);
...@@ -153,11 +159,17 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, ...@@ -153,11 +159,17 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
u32 *value); u32 *value);
struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client, struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client,
unsigned long flags); unsigned long flags);
void host1x_syncpt_free(struct host1x_syncpt *sp); void host1x_syncpt_put(struct host1x_syncpt *sp);
struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
unsigned long flags,
const char *name);
struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp); struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp);
u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base); u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base);
void host1x_syncpt_release_vblank_reservation(struct host1x_client *client,
u32 syncpt_id);
/* /*
* host1x channel * host1x channel
*/ */
...@@ -218,7 +230,7 @@ struct host1x_job { ...@@ -218,7 +230,7 @@ struct host1x_job {
dma_addr_t *reloc_addr_phys; dma_addr_t *reloc_addr_phys;
/* Sync point id, number of increments and end related to the submit */ /* Sync point id, number of increments and end related to the submit */
u32 syncpt_id; struct host1x_syncpt *syncpt;
u32 syncpt_incrs; u32 syncpt_incrs;
u32 syncpt_end; u32 syncpt_end;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment