#include "drm.h"
#include "gem.h"
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_plane_helper.h>
struct tegra_dc_soc_info {
return dfixed_frac(inf);
}
-static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
- const struct tegra_dc_window *window)
+static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
+ const struct tegra_dc_window *window)
{
unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
unsigned long value, flags;
break;
case TEGRA_BO_TILING_MODE_BLOCK:
- DRM_ERROR("hardware doesn't support block linear mode\n");
- spin_unlock_irqrestore(&dc->lock, flags);
- return -EINVAL;
+ /*
+ * No need to handle this here because ->atomic_check
+ * will already have filtered it out.
+ */
+ break;
}
tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
tegra_dc_window_commit(dc, index);
spin_unlock_irqrestore(&dc->lock, flags);
-
- return 0;
-}
-
-static int tegra_window_plane_disable(struct drm_plane *plane)
-{
- struct tegra_dc *dc = to_tegra_dc(plane->crtc);
- struct tegra_plane *p = to_tegra_plane(plane);
- unsigned long flags;
- u32 value;
-
- if (!plane->crtc)
- return 0;
-
- spin_lock_irqsave(&dc->lock, flags);
-
- value = WINDOW_A_SELECT << p->index;
- tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
-
- value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
- value &= ~WIN_ENABLE;
- tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
- tegra_dc_window_commit(dc, p->index);
-
- spin_unlock_irqrestore(&dc->lock, flags);
-
- return 0;
}
static void tegra_plane_destroy(struct drm_plane *plane)
DRM_FORMAT_RGB565,
};
-static int tegra_primary_plane_update(struct drm_plane *plane,
- struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int crtc_x,
- int crtc_y, unsigned int crtc_w,
- unsigned int crtc_h, uint32_t src_x,
- uint32_t src_y, uint32_t src_w,
- uint32_t src_h)
+static void tegra_primary_plane_destroy(struct drm_plane *plane)
{
- struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
+ tegra_plane_destroy(plane);
+}
+
+static const struct drm_plane_funcs tegra_primary_plane_funcs = {
+ .update_plane = drm_plane_helper_update,
+ .disable_plane = drm_plane_helper_disable,
+ .destroy = tegra_primary_plane_destroy,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int tegra_plane_prepare_fb(struct drm_plane *plane,
+ struct drm_framebuffer *fb)
+{
+ return 0;
+}
+
+static void tegra_plane_cleanup_fb(struct drm_plane *plane,
+ struct drm_framebuffer *fb)
+{
+}
+
+static int tegra_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct tegra_dc *dc = to_tegra_dc(state->crtc);
+ struct tegra_bo_tiling tiling;
+ int err;
+
+ /* no need for further checks if the plane is being disabled */
+ if (!state->crtc)
+ return 0;
+
+ err = tegra_fb_get_tiling(state->fb, &tiling);
+ if (err < 0)
+ return err;
+
+ if (tiling.mode == TEGRA_BO_TILING_MODE_BLOCK &&
+ !dc->soc->supports_block_linear) {
+ DRM_ERROR("hardware doesn't support block linear mode\n");
+ return -EINVAL;
+ }
+
+ /*
+ * 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
+ * configuration.
+ */
+ if (drm_format_num_planes(state->fb->pixel_format) > 2) {
+ if (state->fb->pitches[2] != state->fb->pitches[1]) {
+ DRM_ERROR("unsupported UV-plane configuration\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void tegra_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
+ struct drm_framebuffer *fb = plane->state->fb;
struct tegra_plane *p = to_tegra_plane(plane);
- struct tegra_dc *dc = to_tegra_dc(crtc);
struct tegra_dc_window window;
+ unsigned int i;
int err;
+ /* rien ne va plus */
+ if (!plane->state->crtc || !plane->state->fb)
+ return;
+
memset(&window, 0, sizeof(window));
- window.src.x = src_x >> 16;
- window.src.y = src_y >> 16;
- window.src.w = src_w >> 16;
- window.src.h = src_h >> 16;
- window.dst.x = crtc_x;
- window.dst.y = crtc_y;
- window.dst.w = crtc_w;
- window.dst.h = crtc_h;
+ window.src.x = plane->state->src_x >> 16;
+ window.src.y = plane->state->src_y >> 16;
+ window.src.w = plane->state->src_w >> 16;
+ window.src.h = plane->state->src_h >> 16;
+ window.dst.x = plane->state->crtc_x;
+ window.dst.y = plane->state->crtc_y;
+ window.dst.w = plane->state->crtc_w;
+ window.dst.h = plane->state->crtc_h;
window.format = tegra_dc_format(fb->pixel_format, &window.swap);
window.bits_per_pixel = fb->bits_per_pixel;
window.bottom_up = tegra_fb_is_bottom_up(fb);
err = tegra_fb_get_tiling(fb, &window.tiling);
- if (err < 0)
- return err;
+ WARN_ON(err < 0);
- window.base[0] = bo->paddr + fb->offsets[0];
- window.stride[0] = fb->pitches[0];
+ for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
+ struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
- err = tegra_dc_setup_window(dc, p->index, &window);
- if (err < 0)
- return err;
+ window.base[i] = bo->paddr + fb->offsets[i];
+ window.stride[i] = fb->pitches[i];
+ }
- return 0;
+ tegra_dc_setup_window(dc, p->index, &window);
}
-static void tegra_primary_plane_destroy(struct drm_plane *plane)
+static void tegra_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
{
- tegra_window_plane_disable(plane);
- tegra_plane_destroy(plane);
+ struct tegra_plane *p = to_tegra_plane(plane);
+ struct tegra_dc *dc;
+ unsigned long flags;
+ u32 value;
+
+ /* rien ne va plus */
+ if (!old_state || !old_state->crtc)
+ return;
+
+ dc = to_tegra_dc(old_state->crtc);
+
+ spin_lock_irqsave(&dc->lock, flags);
+
+ value = WINDOW_A_SELECT << p->index;
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
+
+ value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+ value &= ~WIN_ENABLE;
+ tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+ tegra_dc_window_commit(dc, p->index);
+
+ spin_unlock_irqrestore(&dc->lock, flags);
}
-static const struct drm_plane_funcs tegra_primary_plane_funcs = {
- .update_plane = tegra_primary_plane_update,
- .disable_plane = tegra_window_plane_disable,
- .destroy = tegra_primary_plane_destroy,
+static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = {
+ .prepare_fb = tegra_plane_prepare_fb,
+ .cleanup_fb = tegra_plane_cleanup_fb,
+ .atomic_check = tegra_plane_atomic_check,
+ .atomic_update = tegra_plane_atomic_update,
+ .atomic_disable = tegra_plane_atomic_disable,
};
static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
return ERR_PTR(err);
}
+ drm_plane_helper_add(&plane->base, &tegra_primary_plane_helper_funcs);
+
return &plane->base;
}
DRM_FORMAT_RGBA8888,
};
-static int tegra_cursor_plane_update(struct drm_plane *plane,
- struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int crtc_x,
- int crtc_y, unsigned int crtc_w,
- unsigned int crtc_h, uint32_t src_x,
- uint32_t src_y, uint32_t src_w,
- uint32_t src_h)
+static int tegra_cursor_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
{
- struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
- struct tegra_dc *dc = to_tegra_dc(crtc);
- u32 value = CURSOR_CLIP_DISPLAY;
+ /* no need for further checks if the plane is being disabled */
+ if (!state->crtc)
+ return 0;
/* scaling not supported for cursor */
- if ((src_w >> 16 != crtc_w) || (src_h >> 16 != crtc_h))
+ if ((state->src_w >> 16 != state->crtc_w) ||
+ (state->src_h >> 16 != state->crtc_h))
return -EINVAL;
/* only square cursors supported */
- if (src_w != src_h)
+ if (state->src_w != state->src_h)
+ return -EINVAL;
+
+ if (state->crtc_w != 32 && state->crtc_w != 64 &&
+ state->crtc_w != 128 && state->crtc_w != 256)
return -EINVAL;
- switch (crtc_w) {
+ return 0;
+}
+
+static void tegra_cursor_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0);
+ struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
+ struct drm_plane_state *state = plane->state;
+ u32 value = CURSOR_CLIP_DISPLAY;
+
+ /* rien ne va plus */
+ if (!plane->state->crtc || !plane->state->fb)
+ return;
+
+ switch (state->crtc_w) {
case 32:
value |= CURSOR_SIZE_32x32;
break;
break;
default:
- return -EINVAL;
+ WARN(1, "cursor size %ux%u not supported\n", state->crtc_w,
+ state->crtc_h);
+ return;
}
value |= (bo->paddr >> 10) & 0x3fffff;
tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
/* position the cursor */
- value = (crtc_y & 0x3fff) << 16 | (crtc_x & 0x3fff);
+ value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
/* apply changes */
tegra_dc_cursor_commit(dc);
tegra_dc_commit(dc);
-
- return 0;
}
-static int tegra_cursor_plane_disable(struct drm_plane *plane)
+static void tegra_cursor_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
{
- struct tegra_dc *dc = to_tegra_dc(plane->crtc);
+ struct tegra_dc *dc;
u32 value;
- if (!plane->crtc)
- return 0;
+ /* rien ne va plus */
+ if (!old_state || !old_state->crtc)
+ return;
+
+ dc = to_tegra_dc(old_state->crtc);
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
value &= ~CURSOR_ENABLE;
tegra_dc_cursor_commit(dc);
tegra_dc_commit(dc);
-
- return 0;
}
static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
- .update_plane = tegra_cursor_plane_update,
- .disable_plane = tegra_cursor_plane_disable,
+ .update_plane = drm_plane_helper_update,
+ .disable_plane = drm_plane_helper_disable,
.destroy = tegra_plane_destroy,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
+ .prepare_fb = tegra_plane_prepare_fb,
+ .cleanup_fb = tegra_plane_cleanup_fb,
+ .atomic_check = tegra_cursor_atomic_check,
+ .atomic_update = tegra_cursor_atomic_update,
+ .atomic_disable = tegra_cursor_atomic_disable,
};
static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
return ERR_PTR(err);
}
- return &plane->base;
-}
-
-static int tegra_overlay_plane_update(struct drm_plane *plane,
- struct drm_crtc *crtc,
- struct drm_framebuffer *fb, int crtc_x,
- int crtc_y, unsigned int crtc_w,
- unsigned int crtc_h, uint32_t src_x,
- uint32_t src_y, uint32_t src_w,
- uint32_t src_h)
-{
- struct tegra_plane *p = to_tegra_plane(plane);
- struct tegra_dc *dc = to_tegra_dc(crtc);
- struct tegra_dc_window window;
- unsigned int i;
- int err;
-
- memset(&window, 0, sizeof(window));
- window.src.x = src_x >> 16;
- window.src.y = src_y >> 16;
- window.src.w = src_w >> 16;
- window.src.h = src_h >> 16;
- window.dst.x = crtc_x;
- window.dst.y = crtc_y;
- window.dst.w = crtc_w;
- window.dst.h = crtc_h;
- window.format = tegra_dc_format(fb->pixel_format, &window.swap);
- window.bits_per_pixel = fb->bits_per_pixel;
- window.bottom_up = tegra_fb_is_bottom_up(fb);
-
- err = tegra_fb_get_tiling(fb, &window.tiling);
- if (err < 0)
- return err;
+ drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
- for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
- struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
-
- window.base[i] = bo->paddr + fb->offsets[i];
-
- /*
- * Tegra doesn't support different strides for U and V planes
- * so we display a warning if the user tries to display a
- * framebuffer with such a configuration.
- */
- if (i >= 2) {
- if (fb->pitches[i] != window.stride[1])
- DRM_ERROR("unsupported UV-plane configuration\n");
- } else {
- window.stride[i] = fb->pitches[i];
- }
- }
-
- return tegra_dc_setup_window(dc, p->index, &window);
+ return &plane->base;
}
static void tegra_overlay_plane_destroy(struct drm_plane *plane)
{
- tegra_window_plane_disable(plane);
tegra_plane_destroy(plane);
}
static const struct drm_plane_funcs tegra_overlay_plane_funcs = {
- .update_plane = tegra_overlay_plane_update,
- .disable_plane = tegra_window_plane_disable,
+ .update_plane = drm_plane_helper_update,
+ .disable_plane = drm_plane_helper_disable,
.destroy = tegra_overlay_plane_destroy,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
};
static const uint32_t tegra_overlay_plane_formats[] = {
DRM_FORMAT_YUV422,
};
+static const struct drm_plane_helper_funcs tegra_overlay_plane_helper_funcs = {
+ .prepare_fb = tegra_plane_prepare_fb,
+ .cleanup_fb = tegra_plane_cleanup_fb,
+ .atomic_check = tegra_plane_atomic_check,
+ .atomic_update = tegra_plane_atomic_update,
+ .atomic_disable = tegra_plane_atomic_disable,
+};
+
static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
struct tegra_dc *dc,
unsigned int index)
return ERR_PTR(err);
}
+ drm_plane_helper_add(&plane->base, &tegra_overlay_plane_helper_funcs);
+
return &plane->base;
}
.page_flip = tegra_dc_page_flip,
.set_config = drm_crtc_helper_set_config,
.destroy = tegra_dc_destroy,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
static void tegra_dc_stop(struct tegra_dc *dc)
return 0;
}
-static int tegra_crtc_mode_set(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted,
- int x, int y, struct drm_framebuffer *old_fb)
+static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
- struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct tegra_dc *dc = to_tegra_dc(crtc);
- struct tegra_dc_window window;
u32 value;
- int err;
/* program display mode */
tegra_dc_set_timings(dc, mode);
value &= ~INTERLACE_ENABLE;
tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
}
-
- /* setup window parameters */
- memset(&window, 0, sizeof(window));
- window.src.x = 0;
- window.src.y = 0;
- window.src.w = mode->hdisplay;
- window.src.h = mode->vdisplay;
- window.dst.x = 0;
- window.dst.y = 0;
- window.dst.w = mode->hdisplay;
- window.dst.h = mode->vdisplay;
- window.format = tegra_dc_format(crtc->primary->fb->pixel_format,
- &window.swap);
- window.bits_per_pixel = crtc->primary->fb->bits_per_pixel;
- window.stride[0] = crtc->primary->fb->pitches[0];
- window.base[0] = bo->paddr;
-
- err = tegra_dc_setup_window(dc, 0, &window);
- if (err < 0)
- dev_err(dc->dev, "failed to enable root plane\n");
-
- return 0;
-}
-
-static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
- struct drm_framebuffer *old_fb)
-{
- struct tegra_dc *dc = to_tegra_dc(crtc);
-
- return tegra_dc_set_base(dc, x, y, crtc->primary->fb);
}
static void tegra_crtc_prepare(struct drm_crtc *crtc)
tegra_dc_commit(dc);
}
+static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ return 0;
+}
+
+static void tegra_crtc_atomic_begin(struct drm_crtc *crtc)
+{
+}
+
+static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
+{
+}
+
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
.disable = tegra_crtc_disable,
.mode_fixup = tegra_crtc_mode_fixup,
- .mode_set = tegra_crtc_mode_set,
- .mode_set_base = tegra_crtc_mode_set_base,
+ .mode_set = drm_helper_crtc_mode_set,
+ .mode_set_nofb = tegra_crtc_mode_set_nofb,
+ .mode_set_base = drm_helper_crtc_mode_set_base,
.prepare = tegra_crtc_prepare,
.commit = tegra_crtc_commit,
+ .atomic_check = tegra_crtc_atomic_check,
+ .atomic_begin = tegra_crtc_atomic_begin,
+ .atomic_flush = tegra_crtc_atomic_flush,
};
static irqreturn_t tegra_dc_irq(int irq, void *data)