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

Commit 6572c8c6 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'for-upstream/hdlcd' of git://linux-arm.org/linux-ld into drm-fixes

"I have accumulated some cleanup patches for HDLCD, partly triggered by
Daniel Vetter's work on non-blocking atomic operations, that I would like
to integrate into v4.7. My first patch is important for the newly enabled
hibernate option for AArch64 on Juno, the others are fixing behaviour in
HDLCD and adding a debugfs entry to help track the underlying framebuffer
usage. I'm also taking one of Daniel's patches from his non-blocking series
to help with the integration of his patches later."

* 'for-upstream/hdlcd' of git://linux-arm.org/linux-ld:
  drm: hdlcd: Add information about the underlying framebuffers in debugfs
  drm: hdlcd: Cleanup the atomic plane operations
  drm/hdlcd: Fix up crtc_state->event handling
  drm: hdlcd: Revamp runtime power management
parents a9bf7d60 f6c68b4b
Loading
Loading
Loading
Loading
+48 −38
Original line number Diff line number Diff line
@@ -33,8 +33,17 @@
 *
 */

static void hdlcd_crtc_cleanup(struct drm_crtc *crtc)
{
	struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);

	/* stop the controller on cleanup */
	hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0);
	drm_crtc_cleanup(crtc);
}

static const struct drm_crtc_funcs hdlcd_crtc_funcs = {
	.destroy = drm_crtc_cleanup,
	.destroy = hdlcd_crtc_cleanup,
	.set_config = drm_atomic_helper_set_config,
	.page_flip = drm_atomic_helper_page_flip,
	.reset = drm_atomic_helper_crtc_reset,
@@ -97,7 +106,7 @@ static void hdlcd_crtc_mode_set_nofb(struct drm_crtc *crtc)
	struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
	struct drm_display_mode *m = &crtc->state->adjusted_mode;
	struct videomode vm;
	unsigned int polarities, line_length, err;
	unsigned int polarities, err;

	vm.vfront_porch = m->crtc_vsync_start - m->crtc_vdisplay;
	vm.vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
@@ -113,23 +122,18 @@ static void hdlcd_crtc_mode_set_nofb(struct drm_crtc *crtc)
	if (m->flags & DRM_MODE_FLAG_PVSYNC)
		polarities |= HDLCD_POLARITY_VSYNC;

	line_length = crtc->primary->state->fb->pitches[0];

	/* Allow max number of outstanding requests and largest burst size */
	hdlcd_write(hdlcd, HDLCD_REG_BUS_OPTIONS,
		    HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);

	hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_LENGTH, line_length);
	hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_PITCH, line_length);
	hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_COUNT, m->crtc_vdisplay - 1);
	hdlcd_write(hdlcd, HDLCD_REG_V_DATA, m->crtc_vdisplay - 1);
	hdlcd_write(hdlcd, HDLCD_REG_V_BACK_PORCH, vm.vback_porch - 1);
	hdlcd_write(hdlcd, HDLCD_REG_V_FRONT_PORCH, vm.vfront_porch - 1);
	hdlcd_write(hdlcd, HDLCD_REG_V_SYNC, vm.vsync_len - 1);
	hdlcd_write(hdlcd, HDLCD_REG_H_DATA, m->crtc_hdisplay - 1);
	hdlcd_write(hdlcd, HDLCD_REG_H_BACK_PORCH, vm.hback_porch - 1);
	hdlcd_write(hdlcd, HDLCD_REG_H_FRONT_PORCH, vm.hfront_porch - 1);
	hdlcd_write(hdlcd, HDLCD_REG_H_SYNC, vm.hsync_len - 1);
	hdlcd_write(hdlcd, HDLCD_REG_H_DATA, m->crtc_hdisplay - 1);
	hdlcd_write(hdlcd, HDLCD_REG_POLARITIES, polarities);

	err = hdlcd_set_pxl_fmt(crtc);
@@ -144,20 +148,19 @@ static void hdlcd_crtc_enable(struct drm_crtc *crtc)
	struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);

	clk_prepare_enable(hdlcd->clk);
	hdlcd_crtc_mode_set_nofb(crtc);
	hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 1);
	drm_crtc_vblank_on(crtc);
}

static void hdlcd_crtc_disable(struct drm_crtc *crtc)
{
	struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);

	if (!crtc->primary->fb)
	if (!crtc->state->active)
		return;

	clk_disable_unprepare(hdlcd->clk);
	hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0);
	drm_crtc_vblank_off(crtc);
	clk_disable_unprepare(hdlcd->clk);
}

static int hdlcd_crtc_atomic_check(struct drm_crtc *crtc,
@@ -179,20 +182,17 @@ static int hdlcd_crtc_atomic_check(struct drm_crtc *crtc,
static void hdlcd_crtc_atomic_begin(struct drm_crtc *crtc,
				    struct drm_crtc_state *state)
{
	struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);
	unsigned long flags;

	if (crtc->state->event) {
	struct drm_pending_vblank_event *event = crtc->state->event;

	if (event) {
		crtc->state->event = NULL;
		event->pipe = drm_crtc_index(crtc);

		WARN_ON(drm_crtc_vblank_get(crtc) != 0);

		spin_lock_irqsave(&crtc->dev->event_lock, flags);
		list_add_tail(&event->base.link, &hdlcd->event_list);
		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
		spin_lock_irq(&crtc->dev->event_lock);
		if (drm_crtc_vblank_get(crtc) == 0)
			drm_crtc_arm_vblank_event(crtc, event);
		else
			drm_crtc_send_vblank_event(crtc, event);
		spin_unlock_irq(&crtc->dev->event_lock);
	}
}

@@ -225,6 +225,15 @@ static const struct drm_crtc_helper_funcs hdlcd_crtc_helper_funcs = {
static int hdlcd_plane_atomic_check(struct drm_plane *plane,
				    struct drm_plane_state *state)
{
	u32 src_w, src_h;

	src_w = state->src_w >> 16;
	src_h = state->src_h >> 16;

	/* we can't do any scaling of the plane source */
	if ((src_w != state->crtc_w) || (src_h != state->crtc_h))
		return -EINVAL;

	return 0;
}

@@ -233,20 +242,31 @@ static void hdlcd_plane_atomic_update(struct drm_plane *plane,
{
	struct hdlcd_drm_private *hdlcd;
	struct drm_gem_cma_object *gem;
	unsigned int depth, bpp;
	u32 src_w, src_h, dest_w, dest_h;
	dma_addr_t scanout_start;

	if (!plane->state->crtc || !plane->state->fb)
	if (!plane->state->fb)
		return;

	hdlcd = crtc_to_hdlcd_priv(plane->state->crtc);
	drm_fb_get_bpp_depth(plane->state->fb->pixel_format, &depth, &bpp);
	src_w = plane->state->src_w >> 16;
	src_h = plane->state->src_h >> 16;
	dest_w = plane->state->crtc_w;
	dest_h = plane->state->crtc_h;
	gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
	scanout_start = gem->paddr;
	scanout_start = gem->paddr + plane->state->fb->offsets[0] +
		plane->state->crtc_y * plane->state->fb->pitches[0] +
		plane->state->crtc_x * bpp / 8;

	hdlcd = plane->dev->dev_private;
	hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_LENGTH, plane->state->fb->pitches[0]);
	hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_PITCH, plane->state->fb->pitches[0]);
	hdlcd_write(hdlcd, HDLCD_REG_FB_LINE_COUNT, dest_h - 1);
	hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start);
}

static const struct drm_plane_helper_funcs hdlcd_plane_helper_funcs = {
	.prepare_fb = NULL,
	.cleanup_fb = NULL,
	.atomic_check = hdlcd_plane_atomic_check,
	.atomic_update = hdlcd_plane_atomic_update,
};
@@ -294,16 +314,6 @@ static struct drm_plane *hdlcd_plane_init(struct drm_device *drm)
	return plane;
}

void hdlcd_crtc_suspend(struct drm_crtc *crtc)
{
	hdlcd_crtc_disable(crtc);
}

void hdlcd_crtc_resume(struct drm_crtc *crtc)
{
	hdlcd_crtc_enable(crtc);
}

int hdlcd_setup_crtc(struct drm_device *drm)
{
	struct hdlcd_drm_private *hdlcd = drm->dev_private;
+29 −39
Original line number Diff line number Diff line
@@ -49,8 +49,6 @@ static int hdlcd_load(struct drm_device *drm, unsigned long flags)
	atomic_set(&hdlcd->dma_end_count, 0);
#endif

	INIT_LIST_HEAD(&hdlcd->event_list);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	hdlcd->mmio = devm_ioremap_resource(drm->dev, res);
	if (IS_ERR(hdlcd->mmio)) {
@@ -84,11 +82,7 @@ static int hdlcd_load(struct drm_device *drm, unsigned long flags)
		goto setup_fail;
	}

	pm_runtime_enable(drm->dev);

	pm_runtime_get_sync(drm->dev);
	ret = drm_irq_install(drm, platform_get_irq(pdev, 0));
	pm_runtime_put_sync(drm->dev);
	if (ret < 0) {
		DRM_ERROR("failed to install IRQ handler\n");
		goto irq_fail;
@@ -164,24 +158,9 @@ static irqreturn_t hdlcd_irq(int irq, void *arg)
		atomic_inc(&hdlcd->vsync_count);

#endif
	if (irq_status & HDLCD_INTERRUPT_VSYNC) {
		bool events_sent = false;
		unsigned long flags;
		struct drm_pending_vblank_event	*e, *t;

	if (irq_status & HDLCD_INTERRUPT_VSYNC)
		drm_crtc_handle_vblank(&hdlcd->crtc);

		spin_lock_irqsave(&drm->event_lock, flags);
		list_for_each_entry_safe(e, t, &hdlcd->event_list, base.link) {
			list_del(&e->base.link);
			drm_crtc_send_vblank_event(&hdlcd->crtc, e);
			events_sent = true;
		}
		if (events_sent)
			drm_crtc_vblank_put(&hdlcd->crtc);
		spin_unlock_irqrestore(&drm->event_lock, flags);
	}

	/* acknowledge interrupt(s) */
	hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, irq_status);

@@ -275,6 +254,7 @@ static int hdlcd_show_pxlclock(struct seq_file *m, void *arg)
static struct drm_info_list hdlcd_debugfs_list[] = {
	{ "interrupt_count", hdlcd_show_underrun_count, 0 },
	{ "clocks", hdlcd_show_pxlclock, 0 },
	{ "fb", drm_fb_cma_debugfs_show, 0 },
};

static int hdlcd_debugfs_init(struct drm_minor *minor)
@@ -357,6 +337,8 @@ static int hdlcd_drm_bind(struct device *dev)
		return -ENOMEM;

	drm->dev_private = hdlcd;
	dev_set_drvdata(dev, drm);

	hdlcd_setup_mode_config(drm);
	ret = hdlcd_load(drm, 0);
	if (ret)
@@ -366,14 +348,18 @@ static int hdlcd_drm_bind(struct device *dev)
	if (ret)
		goto err_unload;

	dev_set_drvdata(dev, drm);

	ret = component_bind_all(dev, drm);
	if (ret) {
		DRM_ERROR("Failed to bind all components\n");
		goto err_unregister;
	}

	ret = pm_runtime_set_active(dev);
	if (ret)
		goto err_pm_active;

	pm_runtime_enable(dev);

	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
	if (ret < 0) {
		DRM_ERROR("failed to initialise vblank\n");
@@ -399,16 +385,16 @@ static int hdlcd_drm_bind(struct device *dev)
	drm_mode_config_cleanup(drm);
	drm_vblank_cleanup(drm);
err_vblank:
	pm_runtime_disable(drm->dev);
err_pm_active:
	component_unbind_all(dev, drm);
err_unregister:
	drm_dev_unregister(drm);
err_unload:
	pm_runtime_get_sync(drm->dev);
	drm_irq_uninstall(drm);
	pm_runtime_put_sync(drm->dev);
	pm_runtime_disable(drm->dev);
	of_reserved_mem_device_release(drm->dev);
err_free:
	dev_set_drvdata(dev, NULL);
	drm_dev_unref(drm);

	return ret;
@@ -495,30 +481,34 @@ MODULE_DEVICE_TABLE(of, hdlcd_of_match);
static int __maybe_unused hdlcd_pm_suspend(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct drm_crtc *crtc;
	struct hdlcd_drm_private *hdlcd = drm ? drm->dev_private : NULL;

	if (pm_runtime_suspended(dev))
	if (!hdlcd)
		return 0;

	drm_modeset_lock_all(drm);
	list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
		hdlcd_crtc_suspend(crtc);
	drm_modeset_unlock_all(drm);
	drm_kms_helper_poll_disable(drm);

	hdlcd->state = drm_atomic_helper_suspend(drm);
	if (IS_ERR(hdlcd->state)) {
		drm_kms_helper_poll_enable(drm);
		return PTR_ERR(hdlcd->state);
	}

	return 0;
}

static int __maybe_unused hdlcd_pm_resume(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
	struct drm_crtc *crtc;
	struct hdlcd_drm_private *hdlcd = drm ? drm->dev_private : NULL;

	if (!pm_runtime_suspended(dev))
	if (!hdlcd)
		return 0;

	drm_modeset_lock_all(drm);
	list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
		hdlcd_crtc_resume(crtc);
	drm_modeset_unlock_all(drm);
	drm_atomic_helper_resume(drm, hdlcd->state);
	drm_kms_helper_poll_enable(drm);
	pm_runtime_set_active(dev);

	return 0;
}

+1 −4
Original line number Diff line number Diff line
@@ -9,10 +9,9 @@ struct hdlcd_drm_private {
	void __iomem			*mmio;
	struct clk			*clk;
	struct drm_fbdev_cma		*fbdev;
	struct drm_framebuffer		*fb;
	struct list_head		event_list;
	struct drm_crtc			crtc;
	struct drm_plane		*plane;
	struct drm_atomic_state		*state;
#ifdef CONFIG_DEBUG_FS
	atomic_t buffer_underrun_count;
	atomic_t bus_error_count;
@@ -36,7 +35,5 @@ static inline u32 hdlcd_read(struct hdlcd_drm_private *hdlcd, unsigned int reg)

int hdlcd_setup_crtc(struct drm_device *dev);
void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd);
void hdlcd_crtc_suspend(struct drm_crtc *crtc);
void hdlcd_crtc_resume(struct drm_crtc *crtc);

#endif /* __HDLCD_DRV_H__ */