Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0654103 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm/tegra/for-3.19-rc1' of git://people.freedesktop.org/~tagr/linux into drm-next

drm/tegra: Changes for v3.19-rc1

The highlights in this pull request are:

  * IOMMU support: The Tegra DRM driver can now deal with discontiguous
    buffers if an IOMMU exists in the system. That means it can allocate
    using drm_gem_get_pages() and will map them into IOVA space via the
    IOMMU API. Similarly, non-contiguous PRIME buffers can be imported
    from a different driver, which allows better integration with gk20a
    (nouveau) and less hacks.

  * Universal planes: This is precursory work for atomic modesetting and
    will allow hardware cursor support to be implemented on pre-Tegra114
    where RGB cursors were not supported.

  * DSI ganged-mode support: The DSI controller can now gang up with a
    second DSI controller to drive high resolution DSI panels.

Besides those bigger changes there is a slew of fixes, cleanups, plugged
memory leaks and so on.

* tag 'drm/tegra/for-3.19-rc1' of git://people.freedesktop.org/~tagr/linux: (44 commits)
  drm/tegra: gem: Check before freeing CMA memory
  drm/tegra: fb: Add error codes to error messages
  drm/tegra: fb: Properly release GEM objects on failure
  drm/tegra: Detach panel when a connector is removed
  drm/tegra: Plug memory leak
  drm/tegra: gem: Use more consistent data types
  drm/tegra: fb: Do not destroy framebuffer
  drm/tegra: gem: dumb: pitch and size are outputs
  drm/tegra: Enable the hotplug interrupt only when necessary
  drm/tegra: dc: Universal plane support
  drm/tegra: dc: Registers are 32 bits wide
  drm/tegra: dc: Factor out DC, window and cursor commit
  drm/tegra: Add IOMMU support
  drm/tegra: Fix error handling cleanup
  drm/tegra: gem: Use dma_mmap_writecombine()
  drm/tegra: gem: Remove redundant drm_gem_free_mmap_offset()
  drm/tegra: gem: Cleanup tegra_bo_create_with_handle()
  drm/tegra: gem: Extract tegra_bo_alloc_object()
  drm/tegra: dsi: Set up PHY_TIMING & BTA_TIMING registers earlier
  drm/tegra: dsi: Replace 1000000 by USEC_PER_SEC
  ...
parents 4fb2ac6e 7e0180e3
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -191,6 +191,8 @@ of the following host1x client modules:
  - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
  - nvidia,edid: supplies a binary EDID blob
  - nvidia,panel: phandle of a display panel
  - nvidia,ganged-mode: contains a phandle to a second DSI controller to gang
    up with in order to support up to 8 data lanes

- sor: serial output resource

+1 −0
Original line number Diff line number Diff line
config DRM_TEGRA
	tristate "NVIDIA Tegra DRM"
	depends on ARCH_TEGRA || (ARM && COMPILE_TEST)
	depends on COMMON_CLK
	depends on DRM
	depends on RESET_CONTROLLER
	select DRM_KMS_HELPER
+413 −180
Original line number Diff line number Diff line
@@ -9,8 +9,11 @@

#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/iommu.h>
#include <linux/reset.h>

#include <soc/tegra/pmc.h>

#include "dc.h"
#include "drm.h"
#include "gem.h"
@@ -22,6 +25,7 @@ struct tegra_dc_soc_info {
	bool supports_cursor;
	bool supports_block_linear;
	unsigned int pitch_align;
	bool has_powergate;
};

struct tegra_plane {
@@ -34,6 +38,26 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
	return container_of(plane, struct tegra_plane, base);
}

static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index)
{
	u32 value = WIN_A_ACT_REQ << index;

	tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
}

static void tegra_dc_cursor_commit(struct tegra_dc *dc)
{
	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
}

static void tegra_dc_commit(struct tegra_dc *dc)
{
	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
}

static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap)
{
	/* assume no swapping of fetched data */
@@ -305,17 +329,260 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
		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);
	tegra_dc_window_commit(dc, index);

	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);
	u32 value;

	if (!plane->crtc)
		return 0;

	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);

	return 0;
}

static void tegra_plane_destroy(struct drm_plane *plane)
{
	struct tegra_plane *p = to_tegra_plane(plane);

	drm_plane_cleanup(plane);
	kfree(p);
}

static const u32 tegra_primary_plane_formats[] = {
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_XRGB8888,
	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)
{
	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
	struct tegra_plane *p = to_tegra_plane(plane);
	struct tegra_dc *dc = to_tegra_dc(crtc);
	struct tegra_dc_window window;
	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;

	window.base[0] = bo->paddr + fb->offsets[0];
	window.stride[0] = fb->pitches[0];

	err = tegra_dc_setup_window(dc, p->index, &window);
	if (err < 0)
		return err;

	return 0;
}

static void tegra_primary_plane_destroy(struct drm_plane *plane)
{
	tegra_window_plane_disable(plane);
	tegra_plane_destroy(plane);
}

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 struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
						       struct tegra_dc *dc)
{
	struct tegra_plane *plane;
	unsigned int num_formats;
	const u32 *formats;
	int err;

	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
	if (!plane)
		return ERR_PTR(-ENOMEM);

	num_formats = ARRAY_SIZE(tegra_primary_plane_formats);
	formats = tegra_primary_plane_formats;

	err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
				       &tegra_primary_plane_funcs, formats,
				       num_formats, DRM_PLANE_TYPE_PRIMARY);
	if (err < 0) {
		kfree(plane);
		return ERR_PTR(err);
	}

	return &plane->base;
}

static const u32 tegra_cursor_plane_formats[] = {
	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)
{
	struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
	struct tegra_dc *dc = to_tegra_dc(crtc);
	u32 value = CURSOR_CLIP_DISPLAY;

	/* scaling not supported for cursor */
	if ((src_w >> 16 != crtc_w) || (src_h >> 16 != crtc_h))
		return -EINVAL;

	/* only square cursors supported */
	if (src_w != src_h)
		return -EINVAL;

	switch (crtc_w) {
	case 32:
		value |= CURSOR_SIZE_32x32;
		break;

	case 64:
		value |= CURSOR_SIZE_64x64;
		break;

	case 128:
		value |= CURSOR_SIZE_128x128;
		break;

	case 256:
		value |= CURSOR_SIZE_256x256;
		break;

	default:
		return -EINVAL;
	}

	value |= (bo->paddr >> 10) & 0x3fffff;
	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
	value = (bo->paddr >> 32) & 0x3;
	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
#endif

	/* enable cursor and set blend mode */
	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
	value |= CURSOR_ENABLE;
	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);

	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
	value &= ~CURSOR_DST_BLEND_MASK;
	value &= ~CURSOR_SRC_BLEND_MASK;
	value |= CURSOR_MODE_NORMAL;
	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
	value |= CURSOR_ALPHA;
	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);

	/* position the cursor */
	value = (crtc_y & 0x3fff) << 16 | (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_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
static int tegra_cursor_plane_disable(struct drm_plane *plane)
{
	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
	u32 value;

	if (!plane->crtc)
		return 0;

	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
	value &= ~CURSOR_ENABLE;
	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);

	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,
	.destroy = tegra_plane_destroy,
};

static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
						      struct tegra_dc *dc)
{
	struct tegra_plane *plane;
	unsigned int num_formats;
	const u32 *formats;
	int err;

	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
	if (!plane)
		return ERR_PTR(-ENOMEM);

	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
	formats = tegra_cursor_plane_formats;

	err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
				       &tegra_cursor_plane_funcs, formats,
				       num_formats, DRM_PLANE_TYPE_CURSOR);
	if (err < 0) {
		kfree(plane);
		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)
				      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);
@@ -361,44 +628,19 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
	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;

	if (!plane->crtc)
		return 0;

	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)
static void tegra_overlay_plane_destroy(struct drm_plane *plane)
{
	struct tegra_plane *p = to_tegra_plane(plane);

	tegra_plane_disable(plane);
	drm_plane_cleanup(plane);
	kfree(p);
	tegra_window_plane_disable(plane);
	tegra_plane_destroy(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 struct drm_plane_funcs tegra_overlay_plane_funcs = {
	.update_plane = tegra_overlay_plane_update,
	.disable_plane = tegra_window_plane_disable,
	.destroy = tegra_overlay_plane_destroy,
};

static const uint32_t plane_formats[] = {
static const uint32_t tegra_overlay_plane_formats[] = {
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_RGB565,
@@ -408,27 +650,44 @@ static const uint32_t plane_formats[] = {
	DRM_FORMAT_YUV422,
};

static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
						       struct tegra_dc *dc,
						       unsigned int index)
{
	unsigned int i;
	int err = 0;

	for (i = 0; i < 2; i++) {
	struct tegra_plane *plane;
	unsigned int num_formats;
	const u32 *formats;
	int err;

	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
	if (!plane)
			return -ENOMEM;
		return ERR_PTR(-ENOMEM);

		plane->index = 1 + i;
	plane->index = index;

		err = drm_plane_init(drm, &plane->base, 1 << dc->pipe,
				     &tegra_plane_funcs, plane_formats,
				     ARRAY_SIZE(plane_formats), false);
	num_formats = ARRAY_SIZE(tegra_overlay_plane_formats);
	formats = tegra_overlay_plane_formats;

	err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
				       &tegra_overlay_plane_funcs, formats,
				       num_formats, DRM_PLANE_TYPE_OVERLAY);
	if (err < 0) {
		kfree(plane);
			return err;
		return ERR_PTR(err);
	}

	return &plane->base;
}

static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
{
	struct drm_plane *plane;
	unsigned int i;

	for (i = 0; i < 2; i++) {
		plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i);
		if (IS_ERR(plane))
			return PTR_ERR(plane);
	}

	return 0;
@@ -515,10 +774,8 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
	tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
	tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);

	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 << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);

	return 0;
@@ -550,109 +807,6 @@ void tegra_dc_disable_vblank(struct tegra_dc *dc)
	spin_unlock_irqrestore(&dc->lock, flags);
}

static int tegra_dc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file,
				uint32_t handle, uint32_t width,
				uint32_t height, int32_t hot_x, int32_t hot_y)
{
	unsigned long value = CURSOR_CLIP_DISPLAY;
	struct tegra_dc *dc = to_tegra_dc(crtc);
	struct drm_gem_object *gem;
	struct tegra_bo *bo = NULL;

	if (!dc->soc->supports_cursor)
		return -ENXIO;

	if (width != height)
		return -EINVAL;

	switch (width) {
	case 32:
		value |= CURSOR_SIZE_32x32;
		break;

	case 64:
		value |= CURSOR_SIZE_64x64;
		break;

	case 128:
		value |= CURSOR_SIZE_128x128;

	case 256:
		value |= CURSOR_SIZE_256x256;
		break;

	default:
		return -EINVAL;
	}

	if (handle) {
		gem = drm_gem_object_lookup(crtc->dev, file, handle);
		if (!gem)
			return -ENOENT;

		bo = to_tegra_bo(gem);
	}

	if (bo) {
		unsigned long addr = (bo->paddr & 0xfffffc00) >> 10;
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
		unsigned long high = (bo->paddr & 0xfffffffc) >> 32;
#endif

		tegra_dc_writel(dc, value | addr, DC_DISP_CURSOR_START_ADDR);

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
		tegra_dc_writel(dc, high, DC_DISP_CURSOR_START_ADDR_HI);
#endif

		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
		value |= CURSOR_ENABLE;
		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);

		value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
		value &= ~CURSOR_DST_BLEND_MASK;
		value &= ~CURSOR_SRC_BLEND_MASK;
		value |= CURSOR_MODE_NORMAL;
		value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
		value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
		value |= CURSOR_ALPHA;
		tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
	} else {
		value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
		value &= ~CURSOR_ENABLE;
		tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
	}

	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);

	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);

	return 0;
}

static int tegra_dc_cursor_move(struct drm_crtc *crtc, int x, int y)
{
	struct tegra_dc *dc = to_tegra_dc(crtc);
	unsigned long value;

	if (!dc->soc->supports_cursor)
		return -ENXIO;

	value = ((y & 0x3fff) << 16) | (x & 0x3fff);
	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);

	tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);

	/* XXX: only required on generations earlier than Tegra124? */
	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);

	return 0;
}

static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
{
	struct drm_device *drm = dc->base.dev;
@@ -729,8 +883,6 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
}

static const struct drm_crtc_funcs tegra_crtc_funcs = {
	.cursor_set2 = tegra_dc_cursor_set2,
	.cursor_move = tegra_dc_cursor_move,
	.page_flip = tegra_dc_page_flip,
	.set_config = drm_crtc_helper_set_config,
	.destroy = tegra_dc_destroy,
@@ -744,7 +896,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)

	drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) {
		if (plane->crtc == crtc) {
			tegra_plane_disable(plane);
			tegra_window_plane_disable(plane);
			plane->crtc = NULL;

			if (plane->fb) {
@@ -755,6 +907,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
	}

	drm_vblank_off(drm, dc->pipe);
	tegra_dc_commit(dc);
}

static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -937,15 +1090,9 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
static void tegra_crtc_commit(struct drm_crtc *crtc)
{
	struct tegra_dc *dc = to_tegra_dc(crtc);
	unsigned long value;

	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);

	drm_vblank_post_modeset(crtc->dev, dc->pipe);
	tegra_dc_commit(dc);
}

static void tegra_crtc_load_lut(struct drm_crtc *crtc)
@@ -999,7 +1146,7 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data)
	struct tegra_dc *dc = node->info_ent->data;

#define DUMP_REG(name)						\
	seq_printf(s, "%-40s %#05x %08lx\n", #name, name,	\
	seq_printf(s, "%-40s %#05x %08x\n", #name, name,	\
		   tegra_dc_readl(dc, name))

	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT);
@@ -1287,9 +1434,40 @@ static int tegra_dc_init(struct host1x_client *client)
	struct drm_device *drm = dev_get_drvdata(client->parent);
	struct tegra_dc *dc = host1x_client_to_dc(client);
	struct tegra_drm *tegra = drm->dev_private;
	struct drm_plane *primary = NULL;
	struct drm_plane *cursor = NULL;
	int err;

	drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs);
	if (tegra->domain) {
		err = iommu_attach_device(tegra->domain, dc->dev);
		if (err < 0) {
			dev_err(dc->dev, "failed to attach to domain: %d\n",
				err);
			return err;
		}

		dc->domain = tegra->domain;
	}

	primary = tegra_dc_primary_plane_create(drm, dc);
	if (IS_ERR(primary)) {
		err = PTR_ERR(primary);
		goto cleanup;
	}

	if (dc->soc->supports_cursor) {
		cursor = tegra_dc_cursor_plane_create(drm, dc);
		if (IS_ERR(cursor)) {
			err = PTR_ERR(cursor);
			goto cleanup;
		}
	}

	err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
					&tegra_crtc_funcs);
	if (err < 0)
		goto cleanup;

	drm_mode_crtc_set_gamma_size(&dc->base, 256);
	drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);

@@ -1303,12 +1481,12 @@ static int tegra_dc_init(struct host1x_client *client)
	err = tegra_dc_rgb_init(drm, dc);
	if (err < 0 && err != -ENODEV) {
		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
		return err;
		goto cleanup;
	}

	err = tegra_dc_add_planes(drm, dc);
	if (err < 0)
		return err;
		goto cleanup;

	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
		err = tegra_dc_debugfs_init(dc, drm->primary);
@@ -1321,10 +1499,24 @@ static int tegra_dc_init(struct host1x_client *client)
	if (err < 0) {
		dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
			err);
		return err;
		goto cleanup;
	}

	return 0;

cleanup:
	if (cursor)
		drm_plane_cleanup(cursor);

	if (primary)
		drm_plane_cleanup(primary);

	if (tegra->domain) {
		iommu_detach_device(tegra->domain, dc->dev);
		dc->domain = NULL;
	}

	return err;
}

static int tegra_dc_exit(struct host1x_client *client)
@@ -1346,6 +1538,11 @@ static int tegra_dc_exit(struct host1x_client *client)
		return err;
	}

	if (dc->domain) {
		iommu_detach_device(dc->domain, dc->dev);
		dc->domain = NULL;
	}

	return 0;
}

@@ -1359,6 +1556,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
	.supports_cursor = false,
	.supports_block_linear = false,
	.pitch_align = 8,
	.has_powergate = false,
};

static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
@@ -1366,6 +1564,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
	.supports_cursor = false,
	.supports_block_linear = false,
	.pitch_align = 8,
	.has_powergate = false,
};

static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
@@ -1373,6 +1572,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
	.supports_cursor = false,
	.supports_block_linear = false,
	.pitch_align = 64,
	.has_powergate = true,
};

static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
@@ -1380,12 +1580,16 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
	.supports_cursor = true,
	.supports_block_linear = true,
	.pitch_align = 64,
	.has_powergate = true,
};

static const struct of_device_id tegra_dc_of_match[] = {
	{
		.compatible = "nvidia,tegra124-dc",
		.data = &tegra124_dc_soc_info,
	}, {
		.compatible = "nvidia,tegra114-dc",
		.data = &tegra114_dc_soc_info,
	}, {
		.compatible = "nvidia,tegra30-dc",
		.data = &tegra30_dc_soc_info,
@@ -1469,9 +1673,34 @@ static int tegra_dc_probe(struct platform_device *pdev)
		return PTR_ERR(dc->rst);
	}

	if (dc->soc->has_powergate) {
		if (dc->pipe == 0)
			dc->powergate = TEGRA_POWERGATE_DIS;
		else
			dc->powergate = TEGRA_POWERGATE_DISB;

		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
							dc->rst);
		if (err < 0) {
			dev_err(&pdev->dev, "failed to power partition: %d\n",
				err);
			return err;
		}
	} else {
		err = clk_prepare_enable(dc->clk);
	if (err < 0)
		if (err < 0) {
			dev_err(&pdev->dev, "failed to enable clock: %d\n",
				err);
			return err;
		}

		err = reset_control_deassert(dc->rst);
		if (err < 0) {
			dev_err(&pdev->dev, "failed to deassert reset: %d\n",
				err);
			return err;
		}
	}

	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	dc->regs = devm_ioremap_resource(&pdev->dev, regs);
@@ -1525,6 +1754,10 @@ static int tegra_dc_remove(struct platform_device *pdev)
	}

	reset_control_assert(dc->rst);

	if (dc->soc->has_powergate)
		tegra_powergate_power_off(dc->powergate);

	clk_disable_unprepare(dc->clk);

	return 0;
+42 −4
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
 */

#include <linux/host1x.h>
#include <linux/iommu.h>

#include "drm.h"
#include "gem.h"
@@ -33,6 +34,17 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
	if (!tegra)
		return -ENOMEM;

	if (iommu_present(&platform_bus_type)) {
		tegra->domain = iommu_domain_alloc(&platform_bus_type);
		if (IS_ERR(tegra->domain)) {
			err = PTR_ERR(tegra->domain);
			goto free;
		}

		DRM_DEBUG("IOMMU context initialized\n");
		drm_mm_init(&tegra->mm, 0, SZ_2G);
	}

	mutex_init(&tegra->clients_lock);
	INIT_LIST_HEAD(&tegra->clients);
	drm->dev_private = tegra;
@@ -42,13 +54,13 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)

	err = tegra_drm_fb_prepare(drm);
	if (err < 0)
		return err;
		goto config;

	drm_kms_helper_poll_init(drm);

	err = host1x_device_init(device);
	if (err < 0)
		return err;
		goto fbdev;

	/*
	 * We don't use the drm_irq_install() helpers provided by the DRM
@@ -59,18 +71,37 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)

	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
	if (err < 0)
		return err;
		goto device;

	err = tegra_drm_fb_init(drm);
	if (err < 0)
		return err;
		goto vblank;

	return 0;

vblank:
	drm_vblank_cleanup(drm);
device:
	host1x_device_exit(device);
fbdev:
	drm_kms_helper_poll_fini(drm);
	tegra_drm_fb_free(drm);
config:
	drm_mode_config_cleanup(drm);

	if (tegra->domain) {
		iommu_domain_free(tegra->domain);
		drm_mm_takedown(&tegra->mm);
	}
free:
	kfree(tegra);
	return err;
}

static int tegra_drm_unload(struct drm_device *drm)
{
	struct host1x_device *device = to_host1x_device(drm->dev);
	struct tegra_drm *tegra = drm->dev_private;
	int err;

	drm_kms_helper_poll_fini(drm);
@@ -82,6 +113,13 @@ static int tegra_drm_unload(struct drm_device *drm)
	if (err < 0)
		return err;

	if (tegra->domain) {
		iommu_domain_free(tegra->domain);
		drm_mm_takedown(&tegra->mm);
	}

	kfree(tegra);

	return 0;
}

+12 −6
Original line number Diff line number Diff line
@@ -39,6 +39,9 @@ struct tegra_fbdev {
struct tegra_drm {
	struct drm_device *drm;

	struct iommu_domain *domain;
	struct drm_mm mm;

	struct mutex clients_lock;
	struct list_head clients;

@@ -101,6 +104,7 @@ struct tegra_dc {
	spinlock_t lock;

	struct drm_crtc base;
	int powergate;
	int pipe;

	struct clk *clk;
@@ -120,6 +124,8 @@ struct tegra_dc {
	struct drm_pending_vblank_event *event;

	const struct tegra_dc_soc_info *soc;

	struct iommu_domain *domain;
};

static inline struct tegra_dc *
@@ -133,16 +139,15 @@ static inline struct tegra_dc *to_tegra_dc(struct drm_crtc *crtc)
	return crtc ? container_of(crtc, struct tegra_dc, base) : NULL;
}

static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long value,
				   unsigned long reg)
static inline void tegra_dc_writel(struct tegra_dc *dc, u32 value,
				   unsigned long offset)
{
	writel(value, dc->regs + (reg << 2));
	writel(value, dc->regs + (offset << 2));
}

static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
					   unsigned long reg)
static inline u32 tegra_dc_readl(struct tegra_dc *dc, unsigned long offset)
{
	return readl(dc->regs + (reg << 2));
	return readl(dc->regs + (offset << 2));
}

struct tegra_dc_window {
@@ -287,6 +292,7 @@ bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
			struct tegra_bo_tiling *tiling);
int tegra_drm_fb_prepare(struct drm_device *drm);
void tegra_drm_fb_free(struct drm_device *drm);
int tegra_drm_fb_init(struct drm_device *drm);
void tegra_drm_fb_exit(struct drm_device *drm);
#ifdef CONFIG_DRM_TEGRA_FBDEV
Loading