Commit a3b1097c authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm/tegra-for-3.9' of git://anongit.freedesktop.org/tegra/linux into drm-next

Thierry writes:
"Add support for 2 hardware overlays found on Tegra. These support YUV
pixel formats and can be used as video overlays. .mode_set_base() is
implemented and support for VBLANK and page-flipping is added.

A few minor bug fixes are also included and a new debugfs file allows
to inspect the framebuffers attached to the Tegra DRM device."

* 'drm/tegra-for-3.9' of git://anongit.freedesktop.org/tegra/linux:
  drm/tegra: Add list of framebuffers to debugfs
  drm/tegra: Fix color expansion
  drm/tegra: Split DC_CMD_STATE_CONTROL register write
  drm/tegra: Implement page-flipping support
  drm/tegra: Implement VBLANK support
  drm/tegra: Implement .mode_set_base()
  drm/tegra: Add plane support
  drm/tegra: Remove bogus tegra_framebuffer structure
  drm: Add consistency check for page-flipping
parents c976cb37 e450fcc6
...@@ -1160,6 +1160,12 @@ int max_width, max_height;</synopsis> ...@@ -1160,6 +1160,12 @@ int max_width, max_height;</synopsis>
without waiting for rendering or page flip to complete and must block without waiting for rendering or page flip to complete and must block
any new rendering to the frame buffer until the page flip completes. any new rendering to the frame buffer until the page flip completes.
</para> </para>
<para>
If a page flip can be successfully scheduled the driver must set the
<code>drm_crtc-&lt;fb</code> field to the new framebuffer pointed to
by <code>fb</code>. This is important so that the reference counting
on framebuffers stays balanced.
</para>
<para> <para>
If a page flip is already pending, the If a page flip is already pending, the
<methodname>page_flip</methodname> operation must return <methodname>page_flip</methodname> operation must return
......
...@@ -3792,6 +3792,13 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, ...@@ -3792,6 +3792,13 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
/* Keep the old fb, don't unref it. */ /* Keep the old fb, don't unref it. */
old_fb = NULL; old_fb = NULL;
} else { } else {
/*
* Warn if the driver hasn't properly updated the crtc->fb
* field to reflect that the new framebuffer is now used.
* Failing to do so will screw with the reference counting
* on framebuffers.
*/
WARN_ON(crtc->fb != fb);
/* Unref only the old framebuffer. */ /* Unref only the old framebuffer. */
fb = NULL; fb = NULL;
} }
......
...@@ -18,26 +18,257 @@ ...@@ -18,26 +18,257 @@
#include "drm.h" #include "drm.h"
#include "dc.h" #include "dc.h"
struct tegra_dc_window { struct tegra_plane {
fixed20_12 x; struct drm_plane base;
fixed20_12 y; unsigned int index;
fixed20_12 w;
fixed20_12 h;
unsigned int outx;
unsigned int outy;
unsigned int outw;
unsigned int outh;
unsigned int stride;
unsigned int fmt;
}; };
static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
{
return container_of(plane, struct tegra_plane, base);
}
static int tegra_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;
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.bits_per_pixel = fb->bits_per_pixel;
for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
window.base[i] = gem->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);
}
static int tegra_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 value;
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_writel(dc, WIN_A_UPDATE << p->index, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, WIN_A_ACT_REQ << p->index, DC_CMD_STATE_CONTROL);
return 0;
}
static void tegra_plane_destroy(struct drm_plane *plane)
{
tegra_plane_disable(plane);
drm_plane_cleanup(plane);
}
static const struct drm_plane_funcs tegra_plane_funcs = {
.update_plane = tegra_plane_update,
.disable_plane = tegra_plane_disable,
.destroy = tegra_plane_destroy,
};
static const uint32_t plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_UYVY,
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV422,
};
static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
{
unsigned int i;
int err = 0;
for (i = 0; i < 2; i++) {
struct tegra_plane *plane;
plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
if (!plane)
return -ENOMEM;
plane->index = 1 + i;
err = drm_plane_init(drm, &plane->base, 1 << dc->pipe,
&tegra_plane_funcs, plane_formats,
ARRAY_SIZE(plane_formats), false);
if (err < 0)
return err;
}
return 0;
}
static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
struct drm_framebuffer *fb)
{
struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, 0);
unsigned long value;
tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
value = fb->offsets[0] + y * fb->pitches[0] +
x * fb->bits_per_pixel / 8;
tegra_dc_writel(dc, gem->paddr + value, DC_WINBUF_START_ADDR);
tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
value = GENERAL_UPDATE | WIN_A_UPDATE;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
return 0;
}
void tegra_dc_enable_vblank(struct tegra_dc *dc)
{
unsigned long value, flags;
spin_lock_irqsave(&dc->lock, flags);
value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
value |= VBLANK_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
spin_unlock_irqrestore(&dc->lock, flags);
}
void tegra_dc_disable_vblank(struct tegra_dc *dc)
{
unsigned long value, flags;
spin_lock_irqsave(&dc->lock, flags);
value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
value &= ~VBLANK_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
spin_unlock_irqrestore(&dc->lock, flags);
}
static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
{
struct drm_device *drm = dc->base.dev;
struct drm_crtc *crtc = &dc->base;
struct drm_gem_cma_object *gem;
unsigned long flags, base;
if (!dc->event)
return;
gem = drm_fb_cma_get_gem_obj(crtc->fb, 0);
/* check if new start address has been latched */
tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR);
tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
if (base == gem->paddr + crtc->fb->offsets[0]) {
spin_lock_irqsave(&drm->event_lock, flags);
drm_send_vblank_event(drm, dc->pipe, dc->event);
drm_vblank_put(drm, dc->pipe);
dc->event = NULL;
spin_unlock_irqrestore(&drm->event_lock, flags);
}
}
void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
{
struct tegra_dc *dc = to_tegra_dc(crtc);
struct drm_device *drm = crtc->dev;
unsigned long flags;
spin_lock_irqsave(&drm->event_lock, flags);
if (dc->event && dc->event->base.file_priv == file) {
dc->event->base.destroy(&dc->event->base);
drm_vblank_put(drm, dc->pipe);
dc->event = NULL;
}
spin_unlock_irqrestore(&drm->event_lock, flags);
}
static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event)
{
struct tegra_dc *dc = to_tegra_dc(crtc);
struct drm_device *drm = crtc->dev;
if (dc->event)
return -EBUSY;
if (event) {
event->pipe = dc->pipe;
dc->event = event;
drm_vblank_get(drm, dc->pipe);
}
tegra_dc_set_base(dc, 0, 0, fb);
crtc->fb = fb;
return 0;
}
static const struct drm_crtc_funcs tegra_crtc_funcs = { static const struct drm_crtc_funcs tegra_crtc_funcs = {
.page_flip = tegra_dc_page_flip,
.set_config = drm_crtc_helper_set_config, .set_config = drm_crtc_helper_set_config,
.destroy = drm_crtc_cleanup, .destroy = drm_crtc_cleanup,
}; };
static void tegra_crtc_dpms(struct drm_crtc *crtc, int mode) static void tegra_crtc_disable(struct drm_crtc *crtc)
{ {
struct drm_device *drm = crtc->dev;
struct drm_plane *plane;
list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
if (plane->crtc == crtc) {
tegra_plane_disable(plane);
plane->crtc = NULL;
if (plane->fb) {
drm_framebuffer_unreference(plane->fb);
plane->fb = NULL;
}
}
}
} }
static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
...@@ -47,10 +278,11 @@ static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, ...@@ -47,10 +278,11 @@ static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
return true; return true;
} }
static inline u32 compute_dda_inc(fixed20_12 inf, unsigned int out, bool v, static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
unsigned int bpp) unsigned int bpp)
{ {
fixed20_12 outf = dfixed_init(out); fixed20_12 outf = dfixed_init(out);
fixed20_12 inf = dfixed_init(in);
u32 dda_inc; u32 dda_inc;
int max; int max;
...@@ -80,9 +312,10 @@ static inline u32 compute_dda_inc(fixed20_12 inf, unsigned int out, bool v, ...@@ -80,9 +312,10 @@ static inline u32 compute_dda_inc(fixed20_12 inf, unsigned int out, bool v,
return dda_inc; return dda_inc;
} }
static inline u32 compute_initial_dda(fixed20_12 in) static inline u32 compute_initial_dda(unsigned int in)
{ {
return dfixed_frac(in); fixed20_12 inf = dfixed_init(in);
return dfixed_frac(inf);
} }
static int tegra_dc_set_timings(struct tegra_dc *dc, static int tegra_dc_set_timings(struct tegra_dc *dc,
...@@ -153,18 +386,198 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc, ...@@ -153,18 +386,198 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
return 0; return 0;
} }
static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
{
switch (format) {
case WIN_COLOR_DEPTH_YCbCr422:
case WIN_COLOR_DEPTH_YUV422:
if (planar)
*planar = false;
return true;
case WIN_COLOR_DEPTH_YCbCr420P:
case WIN_COLOR_DEPTH_YUV420P:
case WIN_COLOR_DEPTH_YCbCr422P:
case WIN_COLOR_DEPTH_YUV422P:
case WIN_COLOR_DEPTH_YCbCr422R:
case WIN_COLOR_DEPTH_YUV422R:
case WIN_COLOR_DEPTH_YCbCr422RA:
case WIN_COLOR_DEPTH_YUV422RA:
if (planar)
*planar = true;
return true;
}
return false;
}
int 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;
bool yuv, planar;
/*
* For YUV planar modes, the number of bytes per pixel takes into
* account only the luma component and therefore is 1.
*/
yuv = tegra_dc_format_is_yuv(window->format, &planar);
if (!yuv)
bpp = window->bits_per_pixel / 8;
else
bpp = planar ? 1 : 2;
value = WINDOW_A_SELECT << index;
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
tegra_dc_writel(dc, value, DC_WIN_POSITION);
value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
tegra_dc_writel(dc, value, DC_WIN_SIZE);
h_offset = window->src.x * bpp;
v_offset = window->src.y;
h_size = window->src.w * bpp;
v_size = window->src.h;
value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
/*
* For DDA computations the number of bytes per pixel for YUV planar
* modes needs to take into account all Y, U and V components.
*/
if (yuv && planar)
bpp = 2;
h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
h_dda = compute_initial_dda(window->src.x);
v_dda = compute_initial_dda(window->src.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_UV_BUF_STRIDE);
tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
if (yuv && planar) {
tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
value = window->stride[1] << 16 | window->stride[0];
tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
} else {
tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
}
tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
value = WIN_ENABLE;
if (yuv) {
/* setup default colorspace conversion coefficients */
tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
value |= CSC_ENABLE;
} else if (window->bits_per_pixel < 24) {
value |= COLOR_EXPAND;
}
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
/*
* Disable blending and assume Window A is the bottom-most window,
* Window C is the top-most window and Window B is in the middle.
*/
tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
switch (index) {
case 0:
tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
break;
case 1:
tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
break;
case 2:
tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
break;
}
tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
return 0;
}
unsigned int tegra_dc_format(uint32_t format)
{
switch (format) {
case DRM_FORMAT_XRGB8888:
return WIN_COLOR_DEPTH_B8G8R8A8;
case DRM_FORMAT_RGB565:
return WIN_COLOR_DEPTH_B5G6R5;
case DRM_FORMAT_UYVY:
return WIN_COLOR_DEPTH_YCbCr422;
case DRM_FORMAT_YUV420:
return WIN_COLOR_DEPTH_YCbCr420P;
case DRM_FORMAT_YUV422:
return WIN_COLOR_DEPTH_YCbCr422P;
default:
break;
}
WARN(1, "unsupported pixel format %u, using default\n", format);
return WIN_COLOR_DEPTH_B8G8R8A8;
}
static int tegra_crtc_mode_set(struct drm_crtc *crtc, static int tegra_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted, struct drm_display_mode *adjusted,
int x, int y, struct drm_framebuffer *old_fb) int x, int y, struct drm_framebuffer *old_fb)
{ {
struct tegra_framebuffer *fb = to_tegra_fb(crtc->fb); struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(crtc->fb, 0);
struct tegra_dc *dc = to_tegra_dc(crtc); struct tegra_dc *dc = to_tegra_dc(crtc);
unsigned int h_dda, v_dda, bpp; struct tegra_dc_window window;
struct tegra_dc_window win;
unsigned long div, value; unsigned long div, value;
int err; int err;
drm_vblank_pre_modeset(crtc->dev, dc->pipe);
err = tegra_crtc_setup_clk(crtc, mode, &div); err = tegra_crtc_setup_clk(crtc, mode, &div);
if (err) { if (err) {
dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
...@@ -192,83 +605,33 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, ...@@ -192,83 +605,33 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
/* setup window parameters */ /* setup window parameters */
memset(&win, 0, sizeof(win)); memset(&window, 0, sizeof(window));
win.x.full = dfixed_const(0); window.src.x = 0;
win.y.full = dfixed_const(0); window.src.y = 0;
win.w.full = dfixed_const(mode->hdisplay); window.src.w = mode->hdisplay;
win.h.full = dfixed_const(mode->vdisplay); window.src.h = mode->vdisplay;
win.outx = 0; window.dst.x = 0;
win.outy = 0; window.dst.y = 0;
win.outw = mode->hdisplay; window.dst.w = mode->hdisplay;
win.outh = mode->vdisplay; window.dst.h = mode->vdisplay;
window.format = tegra_dc_format(crtc->fb->pixel_format);
switch (crtc->fb->pixel_format) { window.bits_per_pixel = crtc->fb->bits_per_pixel;
case DRM_FORMAT_XRGB8888: window.stride[0] = crtc->fb->pitches[0];
win.fmt = WIN_COLOR_DEPTH_B8G8R8A8; window.base[0] = gem->paddr;
break;
err = tegra_dc_setup_window(dc, 0, &window);
case DRM_FORMAT_RGB565: if (err < 0)
win.fmt = WIN_COLOR_DEPTH_B5G6R5; dev_err(dc->dev, "failed to enable root plane\n");
break;
default:
win.fmt = WIN_COLOR_DEPTH_B8G8R8A8;
WARN_ON(1);
break;
}
bpp = crtc->fb->bits_per_pixel / 8;
win.stride = crtc->fb->pitches[0];
/* program window registers */
value = WINDOW_A_SELECT;
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
tegra_dc_writel(dc, win.fmt, DC_WIN_COLOR_DEPTH);
tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
value = V_POSITION(win.outy) | H_POSITION(win.outx);
tegra_dc_writel(dc, value, DC_WIN_POSITION);
value = V_SIZE(win.outh) | H_SIZE(win.outw);
tegra_dc_writel(dc, value, DC_WIN_SIZE);
value = V_PRESCALED_SIZE(dfixed_trunc(win.h)) |
H_PRESCALED_SIZE(dfixed_trunc(win.w) * bpp);
tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
h_dda = compute_dda_inc(win.w, win.outw, false, bpp);
v_dda = compute_dda_inc(win.h, win.outh, true, bpp);
value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
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_UV_BUF_STRIDE);
tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
tegra_dc_writel(dc, fb->obj->paddr, DC_WINBUF_START_ADDR);
tegra_dc_writel(dc, win.stride, DC_WIN_LINE_STRIDE);
tegra_dc_writel(dc, dfixed_trunc(win.x) * bpp,
DC_WINBUF_ADDR_H_OFFSET);
tegra_dc_writel(dc, dfixed_trunc(win.y), DC_WINBUF_ADDR_V_OFFSET);
value = WIN_ENABLE;
if (bpp < 24)
value |= COLOR_EXPAND;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); return 0;
}
tegra_dc_writel(dc, 0xff00, DC_WIN_BLEND_NOKEY); static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
tegra_dc_writel(dc, 0xff00, DC_WIN_BLEND_1WIN); struct drm_framebuffer *old_fb)
{
struct tegra_dc *dc = to_tegra_dc(crtc);
return 0; return tegra_dc_set_base(dc, x, y, crtc->fb);
} }
static void tegra_crtc_prepare(struct drm_crtc *crtc) static void tegra_crtc_prepare(struct drm_crtc *crtc)
...@@ -314,32 +677,25 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc) ...@@ -314,32 +677,25 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
} }
static void tegra_crtc_commit(struct drm_crtc *crtc) static void tegra_crtc_commit(struct drm_crtc *crtc)
{ {
struct tegra_dc *dc = to_tegra_dc(crtc); struct tegra_dc *dc = to_tegra_dc(crtc);
unsigned long update_mask;
unsigned long value; unsigned long value;
update_mask = GENERAL_ACT_REQ | WIN_A_ACT_REQ; value = GENERAL_UPDATE | WIN_A_UPDATE;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
value = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
value |= FRAME_END_INT; tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
value |= FRAME_END_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); drm_vblank_post_modeset(crtc->dev, dc->pipe);
} }
static void tegra_crtc_load_lut(struct drm_crtc *crtc) static void tegra_crtc_load_lut(struct drm_crtc *crtc)
...@@ -347,15 +703,16 @@ static void tegra_crtc_load_lut(struct drm_crtc *crtc) ...@@ -347,15 +703,16 @@ static void tegra_crtc_load_lut(struct drm_crtc *crtc)
} }
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
.dpms = tegra_crtc_dpms, .disable = tegra_crtc_disable,
.mode_fixup = tegra_crtc_mode_fixup, .mode_fixup = tegra_crtc_mode_fixup,
.mode_set = tegra_crtc_mode_set, .mode_set = tegra_crtc_mode_set,
.mode_set_base = tegra_crtc_mode_set_base,
.prepare = tegra_crtc_prepare, .prepare = tegra_crtc_prepare,
.commit = tegra_crtc_commit, .commit = tegra_crtc_commit,
.load_lut = tegra_crtc_load_lut, .load_lut = tegra_crtc_load_lut,
}; };
static irqreturn_t tegra_drm_irq(int irq, void *data) static irqreturn_t tegra_dc_irq(int irq, void *data)
{ {
struct tegra_dc *dc = data; struct tegra_dc *dc = data;
unsigned long status; unsigned long status;
...@@ -374,6 +731,7 @@ static irqreturn_t tegra_drm_irq(int irq, void *data) ...@@ -374,6 +731,7 @@ static irqreturn_t tegra_drm_irq(int irq, void *data)
dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
*/ */
drm_handle_vblank(dc->base.dev, dc->pipe); drm_handle_vblank(dc->base.dev, dc->pipe);
tegra_dc_finish_page_flip(dc);
} }
if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
...@@ -588,7 +946,7 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data) ...@@ -588,7 +946,7 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data)
DUMP_REG(DC_WIN_BLEND_1WIN); DUMP_REG(DC_WIN_BLEND_1WIN);
DUMP_REG(DC_WIN_BLEND_2WIN_X); DUMP_REG(DC_WIN_BLEND_2WIN_X);
DUMP_REG(DC_WIN_BLEND_2WIN_Y); DUMP_REG(DC_WIN_BLEND_2WIN_Y);
DUMP_REG(DC_WIN_BLEND32WIN_XY); DUMP_REG(DC_WIN_BLEND_3WIN_XY);
DUMP_REG(DC_WIN_HP_FETCH_CONTROL); DUMP_REG(DC_WIN_HP_FETCH_CONTROL);
DUMP_REG(DC_WINBUF_START_ADDR); DUMP_REG(DC_WINBUF_START_ADDR);
DUMP_REG(DC_WINBUF_START_ADDR_NS); DUMP_REG(DC_WINBUF_START_ADDR_NS);
...@@ -690,13 +1048,17 @@ static int tegra_dc_drm_init(struct host1x_client *client, ...@@ -690,13 +1048,17 @@ static int tegra_dc_drm_init(struct host1x_client *client,
return err; return err;
} }
err = tegra_dc_add_planes(drm, dc);
if (err < 0)
return err;
if (IS_ENABLED(CONFIG_DEBUG_FS)) { if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_dc_debugfs_init(dc, drm->primary); err = tegra_dc_debugfs_init(dc, drm->primary);
if (err < 0) if (err < 0)
dev_err(dc->dev, "debugfs setup failed: %d\n", err); dev_err(dc->dev, "debugfs setup failed: %d\n", err);
} }
err = devm_request_irq(dc->dev, dc->irq, tegra_drm_irq, 0, err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
dev_name(dc->dev), dc); dev_name(dc->dev), dc);
if (err < 0) { if (err < 0) {
dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
...@@ -745,6 +1107,7 @@ static int tegra_dc_probe(struct platform_device *pdev) ...@@ -745,6 +1107,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
if (!dc) if (!dc)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&dc->lock);
INIT_LIST_HEAD(&dc->list); INIT_LIST_HEAD(&dc->list);
dc->dev = &pdev->dev; dc->dev = &pdev->dev;
......
...@@ -58,6 +58,8 @@ ...@@ -58,6 +58,8 @@
#define DC_CMD_SIGNAL_RAISE3 0x03e #define DC_CMD_SIGNAL_RAISE3 0x03e
#define DC_CMD_STATE_ACCESS 0x040 #define DC_CMD_STATE_ACCESS 0x040
#define READ_MUX (1 << 0)
#define WRITE_MUX (1 << 2)
#define DC_CMD_STATE_CONTROL 0x041 #define DC_CMD_STATE_CONTROL 0x041
#define GENERAL_ACT_REQ (1 << 0) #define GENERAL_ACT_REQ (1 << 0)
...@@ -290,8 +292,18 @@ ...@@ -290,8 +292,18 @@
#define DC_DISP_SD_HW_K_VALUES 0x4dd #define DC_DISP_SD_HW_K_VALUES 0x4dd
#define DC_DISP_SD_MAN_K_VALUES 0x4de #define DC_DISP_SD_MAN_K_VALUES 0x4de
#define DC_WIN_CSC_YOF 0x611
#define DC_WIN_CSC_KYRGB 0x612
#define DC_WIN_CSC_KUR 0x613
#define DC_WIN_CSC_KVR 0x614
#define DC_WIN_CSC_KUG 0x615
#define DC_WIN_CSC_KVG 0x616
#define DC_WIN_CSC_KUB 0x617
#define DC_WIN_CSC_KVB 0x618
#define DC_WIN_WIN_OPTIONS 0x700 #define DC_WIN_WIN_OPTIONS 0x700
#define COLOR_EXPAND (1 << 6) #define COLOR_EXPAND (1 << 6)
#define CSC_ENABLE (1 << 18)
#define WIN_ENABLE (1 << 30) #define WIN_ENABLE (1 << 30)
#define DC_WIN_BYTE_SWAP 0x701 #define DC_WIN_BYTE_SWAP 0x701
...@@ -359,7 +371,7 @@ ...@@ -359,7 +371,7 @@
#define DC_WIN_BLEND_1WIN 0x710 #define DC_WIN_BLEND_1WIN 0x710
#define DC_WIN_BLEND_2WIN_X 0x711 #define DC_WIN_BLEND_2WIN_X 0x711
#define DC_WIN_BLEND_2WIN_Y 0x712 #define DC_WIN_BLEND_2WIN_Y 0x712
#define DC_WIN_BLEND32WIN_XY 0x713 #define DC_WIN_BLEND_3WIN_XY 0x713
#define DC_WIN_HP_FETCH_CONTROL 0x714 #define DC_WIN_HP_FETCH_CONTROL 0x714
......
...@@ -40,6 +40,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ...@@ -40,6 +40,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (err < 0) if (err < 0)
return err; return err;
err = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (err < 0)
return err;
err = tegra_drm_fb_init(drm); err = tegra_drm_fb_init(drm);
if (err < 0) if (err < 0)
return err; return err;
...@@ -89,13 +93,112 @@ static const struct file_operations tegra_drm_fops = { ...@@ -89,13 +93,112 @@ static const struct file_operations tegra_drm_fops = {
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
struct tegra_dc *dc = to_tegra_dc(crtc);
if (dc->pipe == pipe)
return crtc;
}
return NULL;
}
static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
{
/* TODO: implement real hardware counter using syncpoints */
return drm_vblank_count(dev, crtc);
}
static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (!crtc)
return -ENODEV;
tegra_dc_enable_vblank(dc);
return 0;
}
static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (crtc)
tegra_dc_disable_vblank(dc);
}
static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
tegra_dc_cancel_page_flip(crtc, file);
}
#ifdef CONFIG_DEBUG_FS
static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
{
struct drm_info_node *node = (struct drm_info_node *)s->private;
struct drm_device *drm = node->minor->dev;
struct drm_framebuffer *fb;
mutex_lock(&drm->mode_config.fb_lock);
list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
fb->base.id, fb->width, fb->height, fb->depth,
fb->bits_per_pixel,
atomic_read(&fb->refcount.refcount));
}
mutex_unlock(&drm->mode_config.fb_lock);
return 0;
}
static struct drm_info_list tegra_debugfs_list[] = {
{ "framebuffers", tegra_debugfs_framebuffers, 0 },
};
static int tegra_debugfs_init(struct drm_minor *minor)
{
return drm_debugfs_create_files(tegra_debugfs_list,
ARRAY_SIZE(tegra_debugfs_list),
minor->debugfs_root, minor);
}
static void tegra_debugfs_cleanup(struct drm_minor *minor)
{
drm_debugfs_remove_files(tegra_debugfs_list,
ARRAY_SIZE(tegra_debugfs_list), minor);
}
#endif
struct drm_driver tegra_drm_driver = { struct drm_driver tegra_drm_driver = {
.driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM,
.load = tegra_drm_load, .load = tegra_drm_load,
.unload = tegra_drm_unload, .unload = tegra_drm_unload,
.open = tegra_drm_open, .open = tegra_drm_open,
.preclose = tegra_drm_preclose,
.lastclose = tegra_drm_lastclose, .lastclose = tegra_drm_lastclose,
.get_vblank_counter = tegra_drm_get_vblank_counter,
.enable_vblank = tegra_drm_enable_vblank,
.disable_vblank = tegra_drm_disable_vblank,
#if defined(CONFIG_DEBUG_FS)
.debugfs_init = tegra_debugfs_init,
.debugfs_cleanup = tegra_debugfs_cleanup,
#endif
.gem_free_object = drm_gem_cma_free_object, .gem_free_object = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops, .gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = drm_gem_cma_dumb_create, .dumb_create = drm_gem_cma_dumb_create,
......
...@@ -18,16 +18,6 @@ ...@@ -18,16 +18,6 @@
#include <drm/drm_fb_cma_helper.h> #include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fixed.h> #include <drm/drm_fixed.h>
struct tegra_framebuffer {
struct drm_framebuffer base;
struct drm_gem_cma_object *obj;
};
static inline struct tegra_framebuffer *to_tegra_fb(struct drm_framebuffer *fb)
{
return container_of(fb, struct tegra_framebuffer, base);
}
struct host1x { struct host1x {
struct drm_device *drm; struct drm_device *drm;
struct device *dev; struct device *dev;
...@@ -44,7 +34,6 @@ struct host1x { ...@@ -44,7 +34,6 @@ struct host1x {
struct list_head clients; struct list_head clients;
struct drm_fbdev_cma *fbdev; struct drm_fbdev_cma *fbdev;
struct tegra_framebuffer fb;
}; };
struct host1x_client; struct host1x_client;
...@@ -75,6 +64,7 @@ struct tegra_output; ...@@ -75,6 +64,7 @@ struct tegra_output;
struct tegra_dc { struct tegra_dc {
struct host1x_client client; struct host1x_client client;
spinlock_t lock;
struct host1x *host1x; struct host1x *host1x;
struct device *dev; struct device *dev;
...@@ -94,6 +84,9 @@ struct tegra_dc { ...@@ -94,6 +84,9 @@ struct tegra_dc {
struct drm_info_list *debugfs_files; struct drm_info_list *debugfs_files;
struct drm_minor *minor; struct drm_minor *minor;
struct dentry *debugfs; struct dentry *debugfs;
/* page-flip handling */
struct drm_pending_vblank_event *event;
}; };
static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client) static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client)
...@@ -118,6 +111,34 @@ static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, ...@@ -118,6 +111,34 @@ static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
return readl(dc->regs + (reg << 2)); return readl(dc->regs + (reg << 2));
} }
struct tegra_dc_window {
struct {
unsigned int x;
unsigned int y;
unsigned int w;
unsigned int h;
} src;
struct {
unsigned int x;
unsigned int y;
unsigned int w;
unsigned int h;
} dst;
unsigned int bits_per_pixel;
unsigned int format;
unsigned int stride[2];
unsigned long base[3];
};
/* from dc.c */
extern unsigned int tegra_dc_format(uint32_t format);
extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
const struct tegra_dc_window *window);
extern void tegra_dc_enable_vblank(struct tegra_dc *dc);
extern void tegra_dc_disable_vblank(struct tegra_dc *dc);
extern void tegra_dc_cancel_page_flip(struct drm_crtc *crtc,
struct drm_file *file);
struct tegra_output_ops { struct tegra_output_ops {
int (*enable)(struct tegra_output *output); int (*enable)(struct tegra_output *output);
int (*disable)(struct tegra_output *output); int (*disable)(struct tegra_output *output);
......
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