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

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

Merge branch 'drm-next-tilcdc-atomic' of https://github.com/jsarha/linux into drm-next

Please pull tilcdc atomic modeset support and some non critical fixes.

* 'drm-next-tilcdc-atomic' of https://github.com/jsarha/linux: (29 commits)
  drm/tilcdc: Change tilcdc_crtc_page_flip() to tilcdc_crtc_update_fb()
  drm/tilcdc: Remove unnecessary pm_runtime_get() and *_put() calls
  drm/tilcdc: Get rid of legacy dpms mechanism
  drm/tilcdc: Use drm_atomic_helper_resume/suspend()
  drm/tilcdc: Enable and disable interrupts in crtc start() and stop()
  drm/tilcdc: tfp410: Add atomic modeset helpers to connector funcs
  drm/tilcdc: tfp410: Set crtc panel info at init phase
  drm/tilcdc: panel: Add atomic modeset helpers to connector funcs
  drm/tilcdc: panel: Set crtc panel info at init phase
  drm/tilcdc: Remove tilcdc_verify_fb()
  drm/tilcdc: Remove obsolete crtc helper functions
  drm/tilcdc: Set DRIVER_ATOMIC and use atomic crtc helpers
  drm/tilcdc: Add drm_mode_config_reset() call to tilcdc_load()
  drm/tilcdc: Add atomic mode config funcs
  drm/tilcdc: Add tilcdc_crtc_atomic_check()
  drm/tilcdc: Add tilcdc_crtc_mode_set_nofb()
  drm/tilcdc: Initialize dummy primary plane from crtc init
  drm/tilcdc: Add dummy primary plane implementation
  drm/tilcdc: Make tilcdc_crtc_page_flip() work if crtc is not yet on
  drm/tilcdc: Make tilcdc_crtc_page_flip() public
  ...
parents 694d0d0b e0e344e6
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@ Optional nodes:
   binding follows Documentation/devicetree/bindings/graph.txt and
   suppors a single port with a single endpoint.

 - See also Documentation/devicetree/bindings/display/tilcdc/panel.txt and
   Documentation/devicetree/bindings/display/tilcdc/tfp410.txt for connecting
   tfp410 DVI encoder or lcd panel to lcdc

Example:

	fb: fb@4830e000 {
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ obj-$(CONFIG_DRM_TILCDC_SLAVE_COMPAT) += tilcdc_slave_compat.o \
					 tilcdc_slave_compat.dtb.o

tilcdc-y := \
	tilcdc_plane.o \
	tilcdc_crtc.o \
	tilcdc_tfp410.o \
	tilcdc_panel.o \
+193 −182
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

#include "drm_flip_work.h"
#include <drm/drm_plane_helper.h>
#include <drm/drm_atomic_helper.h>

#include "tilcdc_drv.h"
#include "tilcdc_regs.h"
@@ -26,9 +27,10 @@
struct tilcdc_crtc {
	struct drm_crtc base;

	struct drm_plane primary;
	const struct tilcdc_panel_info *info;
	struct drm_pending_vblank_event *event;
	int dpms;
	bool enabled;
	wait_queue_head_t frame_done_wq;
	bool frame_done;
	spinlock_t irq_lock;
@@ -87,6 +89,41 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
	tilcdc_crtc->curr_fb = fb;
}

static void tilcdc_crtc_enable_irqs(struct drm_device *dev)
{
	struct tilcdc_drm_private *priv = dev->dev_private;

	tilcdc_clear_irqstatus(dev, 0xffffffff);

	if (priv->rev == 1) {
		tilcdc_set(dev, LCDC_RASTER_CTRL_REG,
			LCDC_V1_UNDERFLOW_INT_ENA);
	} else {
		tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG,
			LCDC_V2_UNDERFLOW_INT_ENA |
			LCDC_V2_END_OF_FRAME0_INT_ENA |
			LCDC_FRAME_DONE | LCDC_SYNC_LOST);
	}
}

static void tilcdc_crtc_disable_irqs(struct drm_device *dev)
{
	struct tilcdc_drm_private *priv = dev->dev_private;

	/* disable irqs that we might have enabled: */
	if (priv->rev == 1) {
		tilcdc_clear(dev, LCDC_RASTER_CTRL_REG,
			LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA);
		tilcdc_clear(dev, LCDC_DMA_CTRL_REG,
			LCDC_V1_END_OF_FRAME_INT_ENA);
	} else {
		tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
			LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA |
			LCDC_V2_END_OF_FRAME0_INT_ENA |
			LCDC_FRAME_DONE | LCDC_SYNC_LOST);
	}
}

static void reset(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
@@ -100,66 +137,101 @@ static void reset(struct drm_crtc *crtc)
	tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET);
}

static void start(struct drm_crtc *crtc)
static void tilcdc_crtc_enable(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);

	if (tilcdc_crtc->enabled)
		return;

	pm_runtime_get_sync(dev->dev);

	reset(crtc);

	tilcdc_crtc_enable_irqs(dev);

	tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE);
	tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY));
	tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);

	drm_crtc_vblank_on(crtc);

	tilcdc_crtc->enabled = true;
}

static void stop(struct drm_crtc *crtc)
void tilcdc_crtc_disable(struct drm_crtc *crtc)
{
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	struct tilcdc_drm_private *priv = dev->dev_private;

	if (!tilcdc_crtc->enabled)
		return;

	tilcdc_crtc->frame_done = false;
	tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);

	/*
	 * if necessary wait for framedone irq which will still come
	 * before putting things to sleep..
	 */
	if (priv->rev == 2) {
		int ret = wait_event_timeout(tilcdc_crtc->frame_done_wq,
					     tilcdc_crtc->frame_done,
					     msecs_to_jiffies(500));
		if (ret == 0)
			dev_err(dev->dev, "%s: timeout waiting for framedone\n",
				__func__);
	}

static void tilcdc_crtc_destroy(struct drm_crtc *crtc)
{
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
	drm_crtc_vblank_off(crtc);

	tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
	tilcdc_crtc_disable_irqs(dev);

	of_node_put(crtc->port);
	drm_crtc_cleanup(crtc);
	drm_flip_work_cleanup(&tilcdc_crtc->unref_work);
	pm_runtime_put_sync(dev->dev);

	if (tilcdc_crtc->next_fb) {
		drm_flip_work_queue(&tilcdc_crtc->unref_work,
				    tilcdc_crtc->next_fb);
		tilcdc_crtc->next_fb = NULL;
	}

static int tilcdc_verify_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb)
{
	struct drm_device *dev = crtc->dev;
	unsigned int depth, bpp;
	if (tilcdc_crtc->curr_fb) {
		drm_flip_work_queue(&tilcdc_crtc->unref_work,
				    tilcdc_crtc->curr_fb);
		tilcdc_crtc->curr_fb = NULL;
	}

	drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
	drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
	tilcdc_crtc->last_vblank = ktime_set(0, 0);

	if (fb->pitches[0] != crtc->mode.hdisplay * bpp / 8) {
		dev_err(dev->dev,
			"Invalid pitch: fb and crtc widths must be the same");
		return -EINVAL;
	tilcdc_crtc->enabled = false;
}

	return 0;
static bool tilcdc_crtc_is_on(struct drm_crtc *crtc)
{
	return crtc->state && crtc->state->enable && crtc->state->active;
}

static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
static void tilcdc_crtc_destroy(struct drm_crtc *crtc)
{
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);

	tilcdc_crtc_disable(crtc);

	of_node_put(crtc->port);
	drm_crtc_cleanup(crtc);
	drm_flip_work_cleanup(&tilcdc_crtc->unref_work);
}

int tilcdc_crtc_update_fb(struct drm_crtc *crtc,
		struct drm_framebuffer *fb,
		struct drm_pending_vblank_event *event,
		uint32_t page_flip_flags)
		struct drm_pending_vblank_event *event)
{
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	int r;
	unsigned long flags;
	s64 tdiff;
	ktime_t next_vblank;

	r = tilcdc_verify_fb(crtc, fb);
	if (r)
		return r;

	if (tilcdc_crtc->event) {
		dev_err(dev->dev, "already pending page flip!\n");
@@ -170,82 +242,31 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,

	crtc->primary->fb = fb;

	pm_runtime_get_sync(dev->dev);

	spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);

	if (crtc->hwmode.vrefresh && ktime_to_ns(tilcdc_crtc->last_vblank)) {
		ktime_t next_vblank;
		s64 tdiff;

		next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
			1000000 / crtc->hwmode.vrefresh);

		tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get()));

	if (tdiff >= TILCDC_VBLANK_SAFETY_THRESHOLD_US)
		set_scanout(crtc, fb);
	else
		if (tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US)
			tilcdc_crtc->next_fb = fb;
	}

	if (tilcdc_crtc->next_fb != fb)
		set_scanout(crtc, fb);

	tilcdc_crtc->event = event;

	spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);

	pm_runtime_put_sync(dev->dev);

	return 0;
}

void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)
{
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	struct tilcdc_drm_private *priv = dev->dev_private;

	/* we really only care about on or off: */
	if (mode != DRM_MODE_DPMS_ON)
		mode = DRM_MODE_DPMS_OFF;

	if (tilcdc_crtc->dpms == mode)
		return;

	tilcdc_crtc->dpms = mode;

	if (mode == DRM_MODE_DPMS_ON) {
		pm_runtime_get_sync(dev->dev);
		start(crtc);
	} else {
		tilcdc_crtc->frame_done = false;
		stop(crtc);

		/*
		 * if necessary wait for framedone irq which will still come
		 * before putting things to sleep..
		 */
		if (priv->rev == 2) {
			int ret = wait_event_timeout(
					tilcdc_crtc->frame_done_wq,
					tilcdc_crtc->frame_done,
					msecs_to_jiffies(50));
			if (ret == 0)
				dev_err(dev->dev, "timeout waiting for framedone\n");
		}

		pm_runtime_put_sync(dev->dev);

		if (tilcdc_crtc->next_fb) {
			drm_flip_work_queue(&tilcdc_crtc->unref_work,
					    tilcdc_crtc->next_fb);
			tilcdc_crtc->next_fb = NULL;
		}

		if (tilcdc_crtc->curr_fb) {
			drm_flip_work_queue(&tilcdc_crtc->unref_work,
					    tilcdc_crtc->curr_fb);
			tilcdc_crtc->curr_fb = NULL;
		}

		drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
	}
}

static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc,
		const struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode)
@@ -275,41 +296,21 @@ static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc,
	return true;
}

static void tilcdc_crtc_prepare(struct drm_crtc *crtc)
{
	tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}

static void tilcdc_crtc_commit(struct drm_crtc *crtc)
{
	tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
}

static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
		struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode,
		int x, int y,
		struct drm_framebuffer *old_fb)
static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
{
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	struct tilcdc_drm_private *priv = dev->dev_private;
	const struct tilcdc_panel_info *info = tilcdc_crtc->info;
	uint32_t reg, hbp, hfp, hsw, vbp, vfp, vsw;
	int ret;

	ret = tilcdc_crtc_mode_valid(crtc, mode);
	if (WARN_ON(ret))
		return ret;
	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
	struct drm_framebuffer *fb = crtc->primary->state->fb;

	if (WARN_ON(!info))
		return -EINVAL;

	ret = tilcdc_verify_fb(crtc, crtc->primary->fb);
	if (ret)
		return ret;
		return;

	pm_runtime_get_sync(dev->dev);
	if (WARN_ON(!fb))
		return;

	/* Configure the Burst Size and fifo threshold of DMA: */
	reg = tilcdc_read(dev, LCDC_DMA_CTRL_REG) & ~0x00000770;
@@ -330,7 +331,8 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
		reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_16);
		break;
	default:
		return -EINVAL;
		dev_err(dev->dev, "invalid burst size\n");
		return;
	}
	reg |= (info->fifo_th << 8);
	tilcdc_write(dev, LCDC_DMA_CTRL_REG, reg);
@@ -346,7 +348,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
	DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u",
	    mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw);

	/* Configure the AC Bias Period and Number of Transitions per Interrupt: */
	/* Set AC Bias Period and Number of Transitions per Interrupt: */
	reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00;
	reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) |
		LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt);
@@ -381,7 +383,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
	/*
	 * be sure to set Bit 10 for the V2 LCDC controller,
	 * otherwise limited to 1024 pixels width, stopping
	 * 1920x1080 being suppoted.
	 * 1920x1080 being supported.
	 */
	if (priv->rev == 2) {
		if ((mode->vdisplay - 1) & 0x400) {
@@ -396,14 +398,15 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
	/* Configure display type: */
	reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) &
		~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE |
			LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | 0x000ff000);
		  LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK |
		  0x000ff000 /* Palette Loading Delay bits */);
	reg |= LCDC_TFT_MODE; /* no monochrome/passive support */
	if (info->tft_alt_mode)
		reg |= LCDC_TFT_ALT_ENABLE;
	if (priv->rev == 2) {
		unsigned int depth, bpp;

		drm_fb_get_bpp_depth(crtc->primary->fb->pixel_format, &depth, &bpp);
		drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
		switch (bpp) {
		case 16:
			break;
@@ -415,7 +418,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
			break;
		default:
			dev_err(dev->dev, "invalid pixel format\n");
			return -EINVAL;
			return;
		}
	}
	reg |= info->fdd < 12;
@@ -436,12 +439,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
	else
		tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE);

	/*
	 * use value from adjusted_mode here as this might have been
	 * changed as part of the fixup for slave encoders to solve the
	 * issue where tilcdc timings are not VESA compliant
	 */
	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
		tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC);
	else
		tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC);
@@ -456,51 +454,56 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
	else
		tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER);

	drm_framebuffer_reference(crtc->primary->fb);
	drm_framebuffer_reference(fb);

	set_scanout(crtc, crtc->primary->fb);
	set_scanout(crtc, fb);

	tilcdc_crtc_update_clk(crtc);

	pm_runtime_put_sync(dev->dev);

	return 0;
	crtc->hwmode = crtc->state->adjusted_mode;
}

static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
		struct drm_framebuffer *old_fb)
static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc,
				    struct drm_crtc_state *state)
{
	struct drm_device *dev = crtc->dev;
	int r;

	r = tilcdc_verify_fb(crtc, crtc->primary->fb);
	if (r)
		return r;

	drm_framebuffer_reference(crtc->primary->fb);
	struct drm_display_mode *mode = &state->mode;
	int ret;

	pm_runtime_get_sync(dev->dev);
	/* If we are not active we don't care */
	if (!state->active)
		return 0;

	set_scanout(crtc, crtc->primary->fb);
	if (state->state->planes[0].ptr != crtc->primary ||
	    state->state->planes[0].state == NULL ||
	    state->state->planes[0].state->crtc != crtc) {
		dev_dbg(crtc->dev->dev, "CRTC primary plane must be present");
		return -EINVAL;
	}

	pm_runtime_put_sync(dev->dev);
	ret = tilcdc_crtc_mode_valid(crtc, mode);
	if (ret) {
		dev_dbg(crtc->dev->dev, "Mode \"%s\" not valid", mode->name);
		return -EINVAL;
	}

	return 0;
}

static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
	.destroy        = tilcdc_crtc_destroy,
		.set_config     = drm_crtc_helper_set_config,
		.page_flip      = tilcdc_crtc_page_flip,
	.set_config     = drm_atomic_helper_set_config,
	.page_flip      = drm_atomic_helper_page_flip,
	.reset		= drm_atomic_helper_crtc_reset,
	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};

static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = {
		.dpms           = tilcdc_crtc_dpms,
		.mode_fixup     = tilcdc_crtc_mode_fixup,
		.prepare        = tilcdc_crtc_prepare,
		.commit         = tilcdc_crtc_commit,
		.mode_set       = tilcdc_crtc_mode_set,
		.mode_set_base  = tilcdc_crtc_mode_set_base,
		.enable		= tilcdc_crtc_enable,
		.disable	= tilcdc_crtc_disable,
		.atomic_check	= tilcdc_crtc_atomic_check,
		.mode_set_nofb	= tilcdc_crtc_mode_set_nofb,
};

int tilcdc_crtc_max_width(struct drm_crtc *crtc)
@@ -622,18 +625,15 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc,

void tilcdc_crtc_update_clk(struct drm_crtc *crtc)
{
	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
	struct drm_device *dev = crtc->dev;
	struct tilcdc_drm_private *priv = dev->dev_private;
	int dpms = tilcdc_crtc->dpms;
	unsigned long lcd_clk;
	const unsigned clkdiv = 2; /* using a fixed divider of 2 */
	int ret;

	pm_runtime_get_sync(dev->dev);

	if (dpms == DRM_MODE_DPMS_ON)
		tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
	tilcdc_crtc_disable(crtc);

	/* mode.clock is in KHz, set_rate wants parameter in Hz */
	ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv);
@@ -657,8 +657,8 @@ void tilcdc_crtc_update_clk(struct drm_crtc *crtc)
				LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN |
				LCDC_V2_CORE_CLK_EN);

	if (dpms == DRM_MODE_DPMS_ON)
		tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
	if (tilcdc_crtc_is_on(crtc))
		tilcdc_crtc_enable(crtc);

out:
	pm_runtime_put_sync(dev->dev);
@@ -718,30 +718,34 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)
			tilcdc_crtc->frame_intact = true;
	}

	if (stat & LCDC_FIFO_UNDERFLOW)
		dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow",
				    __func__, stat);

	/* For revision 2 only */
	if (priv->rev == 2) {
		if (stat & LCDC_FRAME_DONE) {
			tilcdc_crtc->frame_done = true;
			wake_up(&tilcdc_crtc->frame_done_wq);
		}
		tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0);
	}

		if (stat & LCDC_SYNC_LOST) {
			dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost",
					    __func__, stat);
			tilcdc_crtc->frame_intact = false;
		if (tilcdc_crtc->sync_lost_count++ > SYNC_LOST_COUNT_LIMIT) {
			dev_err(dev->dev,
				"%s(0x%08x): Sync lost flood detected, disabling the interrupt",
				__func__, stat);
			if (tilcdc_crtc->sync_lost_count++ >
			    SYNC_LOST_COUNT_LIMIT) {
				dev_err(dev->dev, "%s(0x%08x): Sync lost flood detected, disabling the interrupt", __func__, stat);
				tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
					     LCDC_SYNC_LOST);
			}
		}

	if (stat & LCDC_FIFO_UNDERFLOW)
		dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow",
				    __func__, stat);
		/* Indicate to LCDC that the interrupt service routine has
		 * completed, see 13.3.6.1.6 in AM335x TRM.
		 */
		tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0);
	}

	return IRQ_HANDLED;
}
@@ -761,7 +765,10 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)

	crtc = &tilcdc_crtc->base;

	tilcdc_crtc->dpms = DRM_MODE_DPMS_OFF;
	ret = tilcdc_plane_init(dev, &tilcdc_crtc->primary);
	if (ret < 0)
		goto fail;

	init_waitqueue_head(&tilcdc_crtc->frame_done_wq);

	drm_flip_work_init(&tilcdc_crtc->unref_work,
@@ -769,7 +776,11 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)

	spin_lock_init(&tilcdc_crtc->irq_lock);

	ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs);
	ret = drm_crtc_init_with_planes(dev, crtc,
					&tilcdc_crtc->primary,
					NULL,
					&tilcdc_crtc_funcs,
					"tilcdc crtc");
	if (ret < 0)
		goto fail;

+102 −105
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@
#include <linux/component.h>
#include <linux/pinctrl/consumer.h>
#include <linux/suspend.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>

#include "tilcdc_drv.h"
#include "tilcdc_regs.h"
@@ -59,9 +61,84 @@ static void tilcdc_fb_output_poll_changed(struct drm_device *dev)
	drm_fbdev_cma_hotplug_event(priv->fbdev);
}

int tilcdc_atomic_check(struct drm_device *dev,
			struct drm_atomic_state *state)
{
	int ret;

	ret = drm_atomic_helper_check_modeset(dev, state);
	if (ret)
		return ret;

	ret = drm_atomic_helper_check_planes(dev, state);
	if (ret)
		return ret;

	/*
	 * tilcdc ->atomic_check can update ->mode_changed if pixel format
	 * changes, hence will we check modeset changes again.
	 */
	ret = drm_atomic_helper_check_modeset(dev, state);
	if (ret)
		return ret;

	return ret;
}

static int tilcdc_commit(struct drm_device *dev,
		  struct drm_atomic_state *state,
		  bool async)
{
	int ret;

	ret = drm_atomic_helper_prepare_planes(dev, state);
	if (ret)
		return ret;

	drm_atomic_helper_swap_state(state, true);

	/*
	 * Everything below can be run asynchronously without the need to grab
	 * any modeset locks at all under one condition: It must be guaranteed
	 * that the asynchronous work has either been cancelled (if the driver
	 * supports it, which at least requires that the framebuffers get
	 * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
	 * before the new state gets committed on the software side with
	 * drm_atomic_helper_swap_state().
	 *
	 * This scheme allows new atomic state updates to be prepared and
	 * checked in parallel to the asynchronous completion of the previous
	 * update. Which is important since compositors need to figure out the
	 * composition of the next frame right after having submitted the
	 * current layout.
	 */

	/* Keep HW on while we commit the state. */
	pm_runtime_get_sync(dev->dev);

	drm_atomic_helper_commit_modeset_disables(dev, state);

	drm_atomic_helper_commit_planes(dev, state, false);

	drm_atomic_helper_commit_modeset_enables(dev, state);

	/* Now HW should remain on if need becase the crtc is enabled */
	pm_runtime_put_sync(dev->dev);

	drm_atomic_helper_wait_for_vblanks(dev, state);

	drm_atomic_helper_cleanup_planes(dev, state);

	drm_atomic_state_free(state);

	return 0;
}

static const struct drm_mode_config_funcs mode_config_funcs = {
	.fb_create = tilcdc_fb_create,
	.output_poll_changed = tilcdc_fb_output_poll_changed,
	.atomic_check = tilcdc_atomic_check,
	.atomic_commit = tilcdc_commit,
};

static int modeset_init(struct drm_device *dev)
@@ -112,7 +189,7 @@ static int tilcdc_unload(struct drm_device *dev)
{
	struct tilcdc_drm_private *priv = dev->dev_private;

	tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF);
	tilcdc_crtc_disable(priv->crtc);

	tilcdc_remove_external_encoders(dev);

@@ -121,9 +198,7 @@ static int tilcdc_unload(struct drm_device *dev)
	drm_mode_config_cleanup(dev);
	drm_vblank_cleanup(dev);

	pm_runtime_get_sync(dev->dev);
	drm_irq_uninstall(dev);
	pm_runtime_put_sync(dev->dev);

#ifdef CONFIG_CPU_FREQ
	cpufreq_unregister_notifier(&priv->freq_transition,
@@ -146,8 +221,6 @@ static int tilcdc_unload(struct drm_device *dev)
	return 0;
}

static size_t tilcdc_num_regs(void);

static int tilcdc_load(struct drm_device *dev, unsigned long flags)
{
	struct platform_device *pdev = dev->platformdev;
@@ -159,11 +232,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
	int ret;

	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
	if (priv)
		priv->saved_register =
			devm_kcalloc(dev->dev, tilcdc_num_regs(),
				     sizeof(*priv->saved_register), GFP_KERNEL);
	if (!priv || !priv->saved_register) {
	if (!priv) {
		dev_err(dev->dev, "failed to allocate private data\n");
		return -ENOMEM;
	}
@@ -279,9 +348,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
		goto fail_external_cleanup;
	}

	pm_runtime_get_sync(dev->dev);
	ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
	pm_runtime_put_sync(dev->dev);
	if (ret < 0) {
		dev_err(dev->dev, "failed to install IRQ handler\n");
		goto fail_vblank_cleanup;
@@ -295,6 +362,9 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
	}

	drm_helper_disable_unused_functions(dev);

	drm_mode_config_reset(dev);

	priv->fbdev = drm_fbdev_cma_init(dev, bpp,
			dev->mode_config.num_crtc,
			dev->mode_config.num_connector);
@@ -308,9 +378,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
	return 0;

fail_irq_uninstall:
	pm_runtime_get_sync(dev->dev);
	drm_irq_uninstall(dev);
	pm_runtime_put_sync(dev->dev);

fail_vblank_cleanup:
	drm_vblank_cleanup(dev);
@@ -361,45 +429,6 @@ static irqreturn_t tilcdc_irq(int irq, void *arg)
	return tilcdc_crtc_irq(priv->crtc);
}

static void tilcdc_irq_preinstall(struct drm_device *dev)
{
	tilcdc_clear_irqstatus(dev, 0xffffffff);
}

static int tilcdc_irq_postinstall(struct drm_device *dev)
{
	struct tilcdc_drm_private *priv = dev->dev_private;

	/* enable FIFO underflow irq: */
	if (priv->rev == 1) {
		tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA);
	} else {
		tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG,
			   LCDC_V2_UNDERFLOW_INT_ENA |
			   LCDC_V2_END_OF_FRAME0_INT_ENA |
			   LCDC_FRAME_DONE | LCDC_SYNC_LOST);
	}

	return 0;
}

static void tilcdc_irq_uninstall(struct drm_device *dev)
{
	struct tilcdc_drm_private *priv = dev->dev_private;

	/* disable irqs that we might have enabled: */
	if (priv->rev == 1) {
		tilcdc_clear(dev, LCDC_RASTER_CTRL_REG,
				LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA);
		tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA);
	} else {
		tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
			LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA |
			LCDC_V2_END_OF_FRAME0_INT_ENA |
			LCDC_FRAME_DONE | LCDC_SYNC_LOST);
	}
}

static int tilcdc_enable_vblank(struct drm_device *dev, unsigned int pipe)
{
	return 0;
@@ -410,7 +439,7 @@ static void tilcdc_disable_vblank(struct drm_device *dev, unsigned int pipe)
	return;
}

#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP)
#if defined(CONFIG_DEBUG_FS)
static const struct {
	const char *name;
	uint8_t  rev;
@@ -441,15 +470,6 @@ static const struct {
#undef REG
};

static size_t tilcdc_num_regs(void)
{
	return ARRAY_SIZE(registers);
}
#else
static size_t tilcdc_num_regs(void)
{
	return 0;
}
#endif

#ifdef CONFIG_DEBUG_FS
@@ -537,14 +557,11 @@ static const struct file_operations fops = {

static struct drm_driver tilcdc_driver = {
	.driver_features    = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET |
			       DRIVER_PRIME),
			       DRIVER_PRIME | DRIVER_ATOMIC),
	.load               = tilcdc_load,
	.unload             = tilcdc_unload,
	.lastclose          = tilcdc_lastclose,
	.irq_handler        = tilcdc_irq,
	.irq_preinstall     = tilcdc_irq_preinstall,
	.irq_postinstall    = tilcdc_irq_postinstall,
	.irq_uninstall      = tilcdc_irq_uninstall,
	.get_vblank_counter = drm_vblank_no_hw_counter,
	.enable_vblank      = tilcdc_enable_vblank,
	.disable_vblank     = tilcdc_disable_vblank,
@@ -584,28 +601,12 @@ static int tilcdc_pm_suspend(struct device *dev)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct tilcdc_drm_private *priv = ddev->dev_private;
	unsigned i, n = 0;

	drm_kms_helper_poll_disable(ddev);
	priv->saved_state = drm_atomic_helper_suspend(ddev);

	/* Select sleep pin state */
	pinctrl_pm_select_sleep_state(dev);

	if (pm_runtime_suspended(dev)) {
		priv->ctx_valid = false;
		return 0;
	}

	/* Disable the LCDC controller, to avoid locking up the PRCM */
	tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF);

	/* Save register state: */
	for (i = 0; i < ARRAY_SIZE(registers); i++)
		if (registers[i].save && (priv->rev >= registers[i].rev))
			priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg);

	priv->ctx_valid = true;

	return 0;
}

@@ -613,23 +614,15 @@ static int tilcdc_pm_resume(struct device *dev)
{
	struct drm_device *ddev = dev_get_drvdata(dev);
	struct tilcdc_drm_private *priv = ddev->dev_private;
	unsigned i, n = 0;
	int ret = 0;

	/* Select default pin state */
	pinctrl_pm_select_default_state(dev);

	if (priv->ctx_valid == true) {
		/* Restore register state: */
		for (i = 0; i < ARRAY_SIZE(registers); i++)
			if (registers[i].save &&
			    (priv->rev >= registers[i].rev))
				tilcdc_write(ddev, registers[i].reg,
					     priv->saved_register[n++]);
	}

	drm_kms_helper_poll_enable(ddev);
	if (priv->saved_state)
		ret = drm_atomic_helper_resume(ddev, priv->saved_state);

	return 0;
	return ret;
}
#endif

@@ -648,6 +641,12 @@ static int tilcdc_bind(struct device *dev)

static void tilcdc_unbind(struct device *dev)
{
	struct drm_device *ddev = dev_get_drvdata(dev);

	/* Check if a subcomponent has already triggered the unloading. */
	if (!ddev->dev_private)
		return;

	drm_put_dev(dev_get_drvdata(dev));
}

@@ -680,17 +679,15 @@ static int tilcdc_pdev_probe(struct platform_device *pdev)

static int tilcdc_pdev_remove(struct platform_device *pdev)
{
	struct drm_device *ddev = dev_get_drvdata(&pdev->dev);
	struct tilcdc_drm_private *priv = ddev->dev_private;

	/* Check if a subcomponent has already triggered the unloading. */
	if (!priv)
		return 0;
	int ret;

	if (priv->is_componentized)
		component_master_del(&pdev->dev, &tilcdc_comp_ops);
	else
	ret = tilcdc_get_external_components(&pdev->dev, NULL);
	if (ret < 0)
		return ret;
	else if (ret == 0)
		drm_put_dev(platform_get_drvdata(pdev));
	else
		component_master_del(&pdev->dev, &tilcdc_comp_ops);

	return 0;
}
+8 −4

File changed.

Preview size limit exceeded, changes collapsed.

Loading