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

Commit 4244cb48 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux

* 'drm-fixes' of git://people.freedesktop.org/~airlied/linux:
  drm/exynos: fixed wrong err ptr usage and destroy call in exeception
  drm/exynos: Add disable of manager
  drm/exynos: include linux/module.h
  drm/exynos: fix vblank bug.
  drm/exynos: changed buffer structure.
  drm/exynos: removed unnecessary variable.
  drm/exynos: use gem create function generically
  drm/exynos: checked for null pointer
  drm/exynos: added crtc dpms for disable crtc
  drm/exynos: removed meaningless parameter from fbdev update
  drm/exynos: restored kernel_fb_list when reiniting fb_helper
  drm/exynos: changed exynos_drm_display to exynos_drm_display_ops
  drm/exynos: added manager object to connector
  drm/exynos: fixed converting between display mode and timing
  drm/exynos: fixed connector flag with hpd and interlace scan for hdmi
  drm/exynos: added kms poll for handling hpd event
parents f02877ce b7b996da
Loading
Loading
Loading
Loading
+32 −30
Original line number Diff line number Diff line
@@ -27,82 +27,84 @@
#include "drm.h"

#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h"

static DEFINE_MUTEX(exynos_drm_buf_lock);

static int lowlevel_buffer_allocate(struct drm_device *dev,
		struct exynos_drm_buf_entry *entry)
		struct exynos_drm_gem_buf *buffer)
{
	DRM_DEBUG_KMS("%s\n", __FILE__);

	entry->vaddr = dma_alloc_writecombine(dev->dev, entry->size,
			(dma_addr_t *)&entry->paddr, GFP_KERNEL);
	if (!entry->paddr) {
	buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size,
			&buffer->dma_addr, GFP_KERNEL);
	if (!buffer->kvaddr) {
		DRM_ERROR("failed to allocate buffer.\n");
		return -ENOMEM;
	}

	DRM_DEBUG_KMS("allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
			(unsigned int)entry->vaddr, entry->paddr, entry->size);
	DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
			(unsigned long)buffer->kvaddr,
			(unsigned long)buffer->dma_addr,
			buffer->size);

	return 0;
}

static void lowlevel_buffer_deallocate(struct drm_device *dev,
		struct exynos_drm_buf_entry *entry)
		struct exynos_drm_gem_buf *buffer)
{
	DRM_DEBUG_KMS("%s.\n", __FILE__);

	if (entry->paddr && entry->vaddr && entry->size)
		dma_free_writecombine(dev->dev, entry->size, entry->vaddr,
				entry->paddr);
	if (buffer->dma_addr && buffer->size)
		dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr,
				(dma_addr_t)buffer->dma_addr);
	else
		DRM_DEBUG_KMS("entry data is null.\n");
		DRM_DEBUG_KMS("buffer data are invalid.\n");
}

struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev,
struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
		unsigned int size)
{
	struct exynos_drm_buf_entry *entry;
	struct exynos_drm_gem_buf *buffer;

	DRM_DEBUG_KMS("%s.\n", __FILE__);
	DRM_DEBUG_KMS("desired size = 0x%x\n", size);

	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
	if (!entry) {
		DRM_ERROR("failed to allocate exynos_drm_buf_entry.\n");
	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
	if (!buffer) {
		DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n");
		return ERR_PTR(-ENOMEM);
	}

	entry->size = size;
	buffer->size = size;

	/*
	 * allocate memory region with size and set the memory information
	 * to vaddr and paddr of a entry object.
	 * to vaddr and dma_addr of a buffer object.
	 */
	if (lowlevel_buffer_allocate(dev, entry) < 0) {
		kfree(entry);
		entry = NULL;
	if (lowlevel_buffer_allocate(dev, buffer) < 0) {
		kfree(buffer);
		buffer = NULL;
		return ERR_PTR(-ENOMEM);
	}

	return entry;
	return buffer;
}

void exynos_drm_buf_destroy(struct drm_device *dev,
		struct exynos_drm_buf_entry *entry)
		struct exynos_drm_gem_buf *buffer)
{
	DRM_DEBUG_KMS("%s.\n", __FILE__);

	if (!entry) {
		DRM_DEBUG_KMS("entry is null.\n");
	if (!buffer) {
		DRM_DEBUG_KMS("buffer is null.\n");
		return;
	}

	lowlevel_buffer_deallocate(dev, entry);
	lowlevel_buffer_deallocate(dev, buffer);

	kfree(entry);
	entry = NULL;
	kfree(buffer);
	buffer = NULL;
}

MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+4 −17
Original line number Diff line number Diff line
@@ -26,28 +26,15 @@
#ifndef _EXYNOS_DRM_BUF_H_
#define _EXYNOS_DRM_BUF_H_

/*
 * exynos drm buffer entry structure.
 *
 * @paddr: physical address of allocated memory.
 * @vaddr: kernel virtual address of allocated memory.
 * @size: size of allocated memory.
 */
struct exynos_drm_buf_entry {
	dma_addr_t paddr;
	void __iomem *vaddr;
	unsigned int size;
};

/* allocate physical memory. */
struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev,
struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
		unsigned int size);

/* get physical memory information of a drm framebuffer. */
struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb);
/* get memory information of a drm framebuffer. */
struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb);

/* remove allocated physical memory. */
void exynos_drm_buf_destroy(struct drm_device *dev,
		struct exynos_drm_buf_entry *entry);
		struct exynos_drm_gem_buf *buffer);

#endif
+56 −22
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@

struct exynos_drm_connector {
	struct drm_connector	drm_connector;
	uint32_t		encoder_id;
	struct exynos_drm_manager *manager;
};

/* convert exynos_video_timings to drm_display_mode */
@@ -47,6 +49,7 @@ convert_to_display_mode(struct drm_display_mode *mode,
	DRM_DEBUG_KMS("%s\n", __FILE__);

	mode->clock = timing->pixclock / 1000;
	mode->vrefresh = timing->refresh;

	mode->hdisplay = timing->xres;
	mode->hsync_start = mode->hdisplay + timing->left_margin;
@@ -57,6 +60,12 @@ convert_to_display_mode(struct drm_display_mode *mode,
	mode->vsync_start = mode->vdisplay + timing->upper_margin;
	mode->vsync_end = mode->vsync_start + timing->vsync_len;
	mode->vtotal = mode->vsync_end + timing->lower_margin;

	if (timing->vmode & FB_VMODE_INTERLACED)
		mode->flags |= DRM_MODE_FLAG_INTERLACE;

	if (timing->vmode & FB_VMODE_DOUBLE)
		mode->flags |= DRM_MODE_FLAG_DBLSCAN;
}

/* convert drm_display_mode to exynos_video_timings */
@@ -69,7 +78,7 @@ convert_to_video_timing(struct fb_videomode *timing,
	memset(timing, 0, sizeof(*timing));

	timing->pixclock = mode->clock * 1000;
	timing->refresh = mode->vrefresh;
	timing->refresh = drm_mode_vrefresh(mode);

	timing->xres = mode->hdisplay;
	timing->left_margin = mode->hsync_start - mode->hdisplay;
@@ -92,15 +101,16 @@ convert_to_video_timing(struct fb_videomode *timing,

static int exynos_drm_connector_get_modes(struct drm_connector *connector)
{
	struct exynos_drm_manager *manager =
				exynos_drm_get_manager(connector->encoder);
	struct exynos_drm_display *display = manager->display;
	struct exynos_drm_connector *exynos_connector =
					to_exynos_connector(connector);
	struct exynos_drm_manager *manager = exynos_connector->manager;
	struct exynos_drm_display_ops *display_ops = manager->display_ops;
	unsigned int count;

	DRM_DEBUG_KMS("%s\n", __FILE__);

	if (!display) {
		DRM_DEBUG_KMS("display is null.\n");
	if (!display_ops) {
		DRM_DEBUG_KMS("display_ops is null.\n");
		return 0;
	}

@@ -112,7 +122,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
	 * P.S. in case of lcd panel, count is always 1 if success
	 * because lcd panel has only one mode.
	 */
	if (display->get_edid) {
	if (display_ops->get_edid) {
		int ret;
		void *edid;

@@ -122,7 +132,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
			return 0;
		}

		ret = display->get_edid(manager->dev, connector,
		ret = display_ops->get_edid(manager->dev, connector,
						edid, MAX_EDID);
		if (ret < 0) {
			DRM_ERROR("failed to get edid data.\n");
@@ -140,8 +150,8 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
		struct drm_display_mode *mode = drm_mode_create(connector->dev);
		struct fb_videomode *timing;

		if (display->get_timing)
			timing = display->get_timing(manager->dev);
		if (display_ops->get_timing)
			timing = display_ops->get_timing(manager->dev);
		else {
			drm_mode_destroy(connector->dev, mode);
			return 0;
@@ -162,9 +172,10 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
					    struct drm_display_mode *mode)
{
	struct exynos_drm_manager *manager =
				exynos_drm_get_manager(connector->encoder);
	struct exynos_drm_display *display = manager->display;
	struct exynos_drm_connector *exynos_connector =
					to_exynos_connector(connector);
	struct exynos_drm_manager *manager = exynos_connector->manager;
	struct exynos_drm_display_ops *display_ops = manager->display_ops;
	struct fb_videomode timing;
	int ret = MODE_BAD;

@@ -172,8 +183,8 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,

	convert_to_video_timing(&timing, mode);

	if (display && display->check_timing)
		if (!display->check_timing(manager->dev, (void *)&timing))
	if (display_ops && display_ops->check_timing)
		if (!display_ops->check_timing(manager->dev, (void *)&timing))
			ret = MODE_OK;

	return ret;
@@ -181,9 +192,25 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector,

struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
{
	struct drm_device *dev = connector->dev;
	struct exynos_drm_connector *exynos_connector =
					to_exynos_connector(connector);
	struct drm_mode_object *obj;
	struct drm_encoder *encoder;

	DRM_DEBUG_KMS("%s\n", __FILE__);

	return connector->encoder;
	obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
				   DRM_MODE_OBJECT_ENCODER);
	if (!obj) {
		DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
				exynos_connector->encoder_id);
		return NULL;
	}

	encoder = obj_to_encoder(obj);

	return encoder;
}

static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
@@ -196,15 +223,17 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
static enum drm_connector_status
exynos_drm_connector_detect(struct drm_connector *connector, bool force)
{
	struct exynos_drm_manager *manager =
				exynos_drm_get_manager(connector->encoder);
	struct exynos_drm_display *display = manager->display;
	struct exynos_drm_connector *exynos_connector =
					to_exynos_connector(connector);
	struct exynos_drm_manager *manager = exynos_connector->manager;
	struct exynos_drm_display_ops *display_ops =
					manager->display_ops;
	enum drm_connector_status status = connector_status_disconnected;

	DRM_DEBUG_KMS("%s\n", __FILE__);

	if (display && display->is_connected) {
		if (display->is_connected(manager->dev))
	if (display_ops && display_ops->is_connected) {
		if (display_ops->is_connected(manager->dev))
			status = connector_status_connected;
		else
			status = connector_status_disconnected;
@@ -251,9 +280,11 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,

	connector = &exynos_connector->drm_connector;

	switch (manager->display->type) {
	switch (manager->display_ops->type) {
	case EXYNOS_DISPLAY_TYPE_HDMI:
		type = DRM_MODE_CONNECTOR_HDMIA;
		connector->interlace_allowed = true;
		connector->polled = DRM_CONNECTOR_POLL_HPD;
		break;
	default:
		type = DRM_MODE_CONNECTOR_Unknown;
@@ -267,7 +298,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
	if (err)
		goto err_connector;

	exynos_connector->encoder_id = encoder->base.id;
	exynos_connector->manager = manager;
	connector->encoder = encoder;

	err = drm_mode_connector_attach_encoder(connector, encoder);
	if (err) {
		DRM_ERROR("failed to attach a connector to a encoder\n");
+39 −37
Original line number Diff line number Diff line
@@ -29,35 +29,16 @@
#include "drmP.h"
#include "drm_crtc_helper.h"

#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h"

#define to_exynos_crtc(x)	container_of(x, struct exynos_drm_crtc,\
				drm_crtc)

/*
 * Exynos specific crtc postion structure.
 *
 * @fb_x: offset x on a framebuffer to be displyed
 *	- the unit is screen coordinates.
 * @fb_y: offset y on a framebuffer to be displayed
 *	- the unit is screen coordinates.
 * @crtc_x: offset x on hardware screen.
 * @crtc_y: offset y on hardware screen.
 * @crtc_w: width of hardware screen.
 * @crtc_h: height of hardware screen.
 */
struct exynos_drm_crtc_pos {
	unsigned int fb_x;
	unsigned int fb_y;
	unsigned int crtc_x;
	unsigned int crtc_y;
	unsigned int crtc_w;
	unsigned int crtc_h;
};

/*
 * Exynos specific crtc structure.
 *
@@ -85,30 +66,31 @@ static void exynos_drm_crtc_apply(struct drm_crtc *crtc)

	exynos_drm_fn_encoder(crtc, overlay,
			exynos_drm_encoder_crtc_mode_set);
	exynos_drm_fn_encoder(crtc, NULL, exynos_drm_encoder_crtc_commit);
	exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
			exynos_drm_encoder_crtc_commit);
}

static int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
			      struct drm_framebuffer *fb,
			      struct drm_display_mode *mode,
			      struct exynos_drm_crtc_pos *pos)
{
	struct exynos_drm_buf_entry *entry;
	struct exynos_drm_gem_buf *buffer;
	unsigned int actual_w;
	unsigned int actual_h;

	entry = exynos_drm_fb_get_buf(fb);
	if (!entry) {
		DRM_LOG_KMS("entry is null.\n");
	buffer = exynos_drm_fb_get_buf(fb);
	if (!buffer) {
		DRM_LOG_KMS("buffer is null.\n");
		return -EFAULT;
	}

	overlay->paddr = entry->paddr;
	overlay->vaddr = entry->vaddr;
	overlay->dma_addr = buffer->dma_addr;
	overlay->vaddr = buffer->kvaddr;

	DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n",
	DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n",
			(unsigned long)overlay->vaddr,
			(unsigned long)overlay->paddr);
			(unsigned long)overlay->dma_addr);

	actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
	actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
@@ -171,9 +153,26 @@ static int exynos_drm_crtc_update(struct drm_crtc *crtc)

static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
	DRM_DEBUG_KMS("%s\n", __FILE__);
	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);

	DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);

	switch (mode) {
	case DRM_MODE_DPMS_ON:
		exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
				exynos_drm_encoder_crtc_commit);
		break;
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
	case DRM_MODE_DPMS_OFF:
		/* TODO */
		exynos_drm_fn_encoder(crtc, NULL,
				exynos_drm_encoder_crtc_disable);
		break;
	default:
		DRM_DEBUG_KMS("unspecified mode %d\n", mode);
		break;
	}
}

static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
@@ -185,9 +184,12 @@ static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)

static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
{
	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);

	DRM_DEBUG_KMS("%s\n", __FILE__);

	/* drm framework doesn't check NULL. */
	exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
			exynos_drm_encoder_crtc_commit);
}

static bool
+25 −0
Original line number Diff line number Diff line
@@ -35,4 +35,29 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);

/*
 * Exynos specific crtc postion structure.
 *
 * @fb_x: offset x on a framebuffer to be displyed
 *	- the unit is screen coordinates.
 * @fb_y: offset y on a framebuffer to be displayed
 *	- the unit is screen coordinates.
 * @crtc_x: offset x on hardware screen.
 * @crtc_y: offset y on hardware screen.
 * @crtc_w: width of hardware screen.
 * @crtc_h: height of hardware screen.
 */
struct exynos_drm_crtc_pos {
	unsigned int fb_x;
	unsigned int fb_y;
	unsigned int crtc_x;
	unsigned int crtc_y;
	unsigned int crtc_w;
	unsigned int crtc_h;
};

int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
			      struct drm_framebuffer *fb,
			      struct drm_display_mode *mode,
			      struct exynos_drm_crtc_pos *pos);
#endif
Loading