diff options
Diffstat (limited to 'drivers/gpu/drm/tilcdc')
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 598 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.c | 214 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.h | 11 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_external.c | 260 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_external.h | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_panel.c | 10 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_plane.c | 7 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_regs.h | 15 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | 2 | 
9 files changed, 708 insertions, 414 deletions
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 52ebe8fc1784..9942b0577d6e 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -21,11 +21,15 @@  #include <drm/drm_flip_work.h>  #include <drm/drm_plane_helper.h>  #include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/dma-mapping.h>  #include "tilcdc_drv.h"  #include "tilcdc_regs.h" -#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 +#define TILCDC_VBLANK_SAFETY_THRESHOLD_US	1000 +#define TILCDC_PALETTE_SIZE			32 +#define TILCDC_PALETTE_FIRST_ENTRY		0x4000  struct tilcdc_crtc {  	struct drm_crtc base; @@ -33,7 +37,9 @@ struct tilcdc_crtc {  	struct drm_plane primary;  	const struct tilcdc_panel_info *info;  	struct drm_pending_vblank_event *event; +	struct mutex enable_lock;  	bool enabled; +	bool shutdown;  	wait_queue_head_t frame_done_wq;  	bool frame_done;  	spinlock_t irq_lock; @@ -53,6 +59,11 @@ struct tilcdc_crtc {  	int sync_lost_count;  	bool frame_intact; +	struct work_struct recover_work; + +	dma_addr_t palette_dma_handle; +	u16 *palette_base; +	struct completion palette_loaded;  };  #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) @@ -71,17 +82,16 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)  {  	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);  	struct drm_device *dev = crtc->dev; +	struct tilcdc_drm_private *priv = dev->dev_private;  	struct drm_gem_cma_object *gem; -	unsigned int depth, bpp;  	dma_addr_t start, end;  	u64 dma_base_and_ceiling; -	drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);  	gem = drm_fb_cma_get_gem_obj(fb, 0);  	start = gem->paddr + fb->offsets[0] +  		crtc->y * fb->pitches[0] + -		crtc->x * bpp / 8; +		crtc->x * drm_format_plane_cpp(fb->pixel_format, 0);  	end = start + (crtc->mode.vdisplay * fb->pitches[0]); @@ -90,7 +100,10 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)  	 * unlikely that LCDC would fetch the DMA addresses in the middle of  	 * an update.  	 */ -	dma_base_and_ceiling = (u64)(end - 1) << 32 | start; +	if (priv->rev == 1) +		end -= 1; + +	dma_base_and_ceiling = (u64)end << 32 | start;  	tilcdc_write64(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_base_and_ceiling);  	if (tilcdc_crtc->curr_fb) @@ -100,6 +113,56 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)  	tilcdc_crtc->curr_fb = fb;  } +/* + * The driver currently only supports only true color formats. For + * true color the palette block is bypassed, but a 32 byte palette + * should still be loaded. The first 16-bit entry must be 0x4000 while + * all other entries must be zeroed. + */ +static void tilcdc_crtc_load_palette(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 ret; + +	reinit_completion(&tilcdc_crtc->palette_loaded); + +	/* Tell the LCDC where the palette is located. */ +	tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, +		     tilcdc_crtc->palette_dma_handle); +	tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, +		     (u32) tilcdc_crtc->palette_dma_handle + +		     TILCDC_PALETTE_SIZE - 1); + +	/* Set dma load mode for palette loading only. */ +	tilcdc_write_mask(dev, LCDC_RASTER_CTRL_REG, +			  LCDC_PALETTE_LOAD_MODE(PALETTE_ONLY), +			  LCDC_PALETTE_LOAD_MODE_MASK); + +	/* Enable DMA Palette Loaded Interrupt */ +	if (priv->rev == 1) +		tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); +	else +		tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_PL_INT_ENA); + +	/* Enable LCDC DMA and wait for palette to be loaded. */ +	tilcdc_clear_irqstatus(dev, 0xffffffff); +	tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + +	ret = wait_for_completion_timeout(&tilcdc_crtc->palette_loaded, +					  msecs_to_jiffies(50)); +	if (ret == 0) +		dev_err(dev->dev, "%s: Palette loading timeout", __func__); + +	/* Disable LCDC DMA and DMA Palette Loaded Interrupt. */ +	tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); +	if (priv->rev == 1) +		tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_PL_INT_ENA); +	else +		tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, LCDC_V2_PL_INT_ENA); +} +  static void tilcdc_crtc_enable_irqs(struct drm_device *dev)  {  	struct tilcdc_drm_private *priv = dev->dev_private; @@ -108,6 +171,7 @@ static void tilcdc_crtc_enable_irqs(struct drm_device *dev)  	if (priv->rev == 1) {  		tilcdc_set(dev, LCDC_RASTER_CTRL_REG, +			LCDC_V1_SYNC_LOST_INT_ENA | LCDC_V1_FRAME_DONE_INT_ENA |  			LCDC_V1_UNDERFLOW_INT_ENA);  		tilcdc_set(dev, LCDC_DMA_CTRL_REG,  			LCDC_V1_END_OF_FRAME_INT_ENA); @@ -126,6 +190,7 @@ static void tilcdc_crtc_disable_irqs(struct drm_device *dev)  	/* disable irqs that we might have enabled: */  	if (priv->rev == 1) {  		tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, +			LCDC_V1_SYNC_LOST_INT_ENA | LCDC_V1_FRAME_DONE_INT_ENA |  			LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA);  		tilcdc_clear(dev, LCDC_DMA_CTRL_REG,  			LCDC_V1_END_OF_FRAME_INT_ENA); @@ -150,193 +215,68 @@ static void reset(struct drm_crtc *crtc)  	tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET);  } -static void tilcdc_crtc_enable(struct drm_crtc *crtc) +/* + * Calculate the percentage difference between the requested pixel clock rate + * and the effective rate resulting from calculating the clock divider value. + */ +static unsigned int tilcdc_pclk_diff(unsigned long rate, +				     unsigned long real_rate)  { -	struct drm_device *dev = crtc->dev; -	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - -	WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - -	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); +	int r = rate / 100, rr = real_rate / 100; -	tilcdc_crtc->enabled = true; +	return (unsigned int)(abs(((rr - r) * 100) / r));  } -void tilcdc_crtc_disable(struct drm_crtc *crtc) +static void tilcdc_crtc_set_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; - -	WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - -	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__); -	} - -	drm_crtc_vblank_off(crtc); - -	tilcdc_crtc_disable_irqs(dev); - -	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); -	tilcdc_crtc->last_vblank = ktime_set(0, 0); - -	tilcdc_crtc->enabled = false; -} - -static bool tilcdc_crtc_is_on(struct drm_crtc *crtc) -{ -	return crtc->state && crtc->state->enable && crtc->state->active; -} - -static void tilcdc_crtc_destroy(struct drm_crtc *crtc) -{ -	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); -	struct tilcdc_drm_private *priv = crtc->dev->dev_private; - -	drm_modeset_lock_crtc(crtc, NULL); -	tilcdc_crtc_disable(crtc); -	drm_modeset_unlock_crtc(crtc); - -	flush_workqueue(priv->wq); - -	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) -{ -	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); -	struct drm_device *dev = crtc->dev; -	unsigned long flags; - -	WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - -	if (tilcdc_crtc->event) { -		dev_err(dev->dev, "already pending page flip!\n"); -		return -EBUSY; -	} - -	drm_framebuffer_reference(fb); - -	crtc->primary->fb = fb; - -	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) -			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); - -	return 0; -} - -static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, -		const struct drm_display_mode *mode, -		struct drm_display_mode *adjusted_mode) -{  	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); +	unsigned long clk_rate, real_rate, req_rate; +	unsigned int clkdiv; +	int ret; -	if (!tilcdc_crtc->simulate_vesa_sync) -		return true; +	clkdiv = 2; /* first try using a standard divider of 2 */ -	/* -	 * tilcdc does not generate VESA-compliant sync but aligns -	 * VS on the second edge of HS instead of first edge. -	 * We use adjusted_mode, to fixup sync by aligning both rising -	 * edges and add HSKEW offset to fix the sync. -	 */ -	adjusted_mode->hskew = mode->hsync_end - mode->hsync_start; -	adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW; +	/* mode.clock is in KHz, set_rate wants parameter in Hz */ +	req_rate = crtc->mode.clock * 1000; -	if (mode->flags & DRM_MODE_FLAG_NHSYNC) { -		adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; -		adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC; -	} else { -		adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC; -		adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC; -	} +	ret = clk_set_rate(priv->clk, req_rate * clkdiv); +	clk_rate = clk_get_rate(priv->clk); +	if (ret < 0) { +		/* +		 * If we fail to set the clock rate (some architectures don't +		 * use the common clock framework yet and may not implement +		 * all the clk API calls for every clock), try the next best +		 * thing: adjusting the clock divider, unless clk_get_rate() +		 * failed as well. +		 */ +		if (!clk_rate) { +			/* Nothing more we can do. Just bail out. */ +			dev_err(dev->dev, +				"failed to set the pixel clock - unable to read current lcdc clock rate\n"); +			return; +		} -	return true; -} +		clkdiv = DIV_ROUND_CLOSEST(clk_rate, req_rate); -static void tilcdc_crtc_set_clk(struct drm_crtc *crtc) -{ -	struct drm_device *dev = crtc->dev; -	struct tilcdc_drm_private *priv = dev->dev_private; -	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); -	const unsigned clkdiv = 2; /* using a fixed divider of 2 */ -	int ret; +		/* +		 * Emit a warning if the real clock rate resulting from the +		 * calculated divider differs much from the requested rate. +		 * +		 * 5% is an arbitrary value - LCDs are usually quite tolerant +		 * about pixel clock rates. +		 */ +		real_rate = clkdiv * req_rate; -	/* mode.clock is in KHz, set_rate wants parameter in Hz */ -	ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv); -	if (ret < 0) { -		dev_err(dev->dev, "failed to set display clock rate to: %d\n", -			crtc->mode.clock); -		return; +		if (tilcdc_pclk_diff(clk_rate, real_rate) > 5) { +			dev_warn(dev->dev, +				 "effective pixel clock rate (%luHz) differs from the calculated rate (%luHz)\n", +				 clk_rate, real_rate); +		}  	} -	tilcdc_crtc->lcd_fck_rate = clk_get_rate(priv->clk); +	tilcdc_crtc->lcd_fck_rate = clk_rate;  	DBG("lcd_clk=%u, mode clock=%d, div=%u",  	    tilcdc_crtc->lcd_fck_rate, crtc->mode.clock, clkdiv); @@ -351,7 +291,7 @@ static void tilcdc_crtc_set_clk(struct drm_crtc *crtc)  				LCDC_V2_CORE_CLK_EN);  } -static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc) +static void tilcdc_crtc_set_mode(struct drm_crtc *crtc)  {  	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);  	struct drm_device *dev = crtc->dev; @@ -361,8 +301,6 @@ static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc)  	struct drm_display_mode *mode = &crtc->state->adjusted_mode;  	struct drm_framebuffer *fb = crtc->primary->state->fb; -	WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); -  	if (WARN_ON(!info))  		return; @@ -461,16 +399,16 @@ static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc)  	if (info->tft_alt_mode)  		reg |= LCDC_TFT_ALT_ENABLE;  	if (priv->rev == 2) { -		unsigned int depth, bpp; - -		drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); -		switch (bpp) { -		case 16: +		switch (fb->pixel_format) { +		case DRM_FORMAT_BGR565: +		case DRM_FORMAT_RGB565:  			break; -		case 32: +		case DRM_FORMAT_XBGR8888: +		case DRM_FORMAT_XRGB8888:  			reg |= LCDC_V2_TFT_24BPP_UNPACK;  			/* fallthrough */ -		case 24: +		case DRM_FORMAT_BGR888: +		case DRM_FORMAT_RGB888:  			reg |= LCDC_V2_TFT_24BPP_MODE;  			break;  		default: @@ -511,15 +449,226 @@ static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc)  	else  		tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); -	drm_framebuffer_reference(fb); +	tilcdc_crtc_set_clk(crtc); + +	tilcdc_crtc_load_palette(crtc);  	set_scanout(crtc, fb); -	tilcdc_crtc_set_clk(crtc); +	drm_framebuffer_reference(fb);  	crtc->hwmode = crtc->state->adjusted_mode;  } +static void tilcdc_crtc_enable(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; +	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + +	WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); +	mutex_lock(&tilcdc_crtc->enable_lock); +	if (tilcdc_crtc->enabled || tilcdc_crtc->shutdown) { +		mutex_unlock(&tilcdc_crtc->enable_lock); +		return; +	} + +	pm_runtime_get_sync(dev->dev); + +	reset(crtc); + +	tilcdc_crtc_set_mode(crtc); + +	tilcdc_crtc_enable_irqs(dev); + +	tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); +	tilcdc_write_mask(dev, LCDC_RASTER_CTRL_REG, +			  LCDC_PALETTE_LOAD_MODE(DATA_ONLY), +			  LCDC_PALETTE_LOAD_MODE_MASK); +	tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + +	drm_crtc_vblank_on(crtc); + +	tilcdc_crtc->enabled = true; +	mutex_unlock(&tilcdc_crtc->enable_lock); +} + +static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) +{ +	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	struct tilcdc_drm_private *priv = dev->dev_private; +	int ret; + +	mutex_lock(&tilcdc_crtc->enable_lock); +	if (shutdown) +		tilcdc_crtc->shutdown = true; +	if (!tilcdc_crtc->enabled) { +		mutex_unlock(&tilcdc_crtc->enable_lock); +		return; +	} +	tilcdc_crtc->frame_done = false; +	tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + +	/* +	 * Wait for framedone irq which will still come before putting +	 * things to sleep.. +	 */ +	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__); + +	drm_crtc_vblank_off(crtc); + +	tilcdc_crtc_disable_irqs(dev); + +	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); +	tilcdc_crtc->last_vblank = ktime_set(0, 0); + +	tilcdc_crtc->enabled = false; +	mutex_unlock(&tilcdc_crtc->enable_lock); +} + +static void tilcdc_crtc_disable(struct drm_crtc *crtc) +{ +	WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); +	tilcdc_crtc_off(crtc, false); +} + +void tilcdc_crtc_shutdown(struct drm_crtc *crtc) +{ +	tilcdc_crtc_off(crtc, true); +} + +static bool tilcdc_crtc_is_on(struct drm_crtc *crtc) +{ +	return crtc->state && crtc->state->enable && crtc->state->active; +} + +static void tilcdc_crtc_recover_work(struct work_struct *work) +{ +	struct tilcdc_crtc *tilcdc_crtc = +		container_of(work, struct tilcdc_crtc, recover_work); +	struct drm_crtc *crtc = &tilcdc_crtc->base; + +	dev_info(crtc->dev->dev, "%s: Reset CRTC", __func__); + +	drm_modeset_lock_crtc(crtc, NULL); + +	if (!tilcdc_crtc_is_on(crtc)) +		goto out; + +	tilcdc_crtc_disable(crtc); +	tilcdc_crtc_enable(crtc); +out: +	drm_modeset_unlock_crtc(crtc); +} + +static void tilcdc_crtc_destroy(struct drm_crtc *crtc) +{ +	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); +	struct tilcdc_drm_private *priv = crtc->dev->dev_private; + +	drm_modeset_lock_crtc(crtc, NULL); +	tilcdc_crtc_disable(crtc); +	drm_modeset_unlock_crtc(crtc); + +	flush_workqueue(priv->wq); + +	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) +{ +	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); +	struct drm_device *dev = crtc->dev; +	unsigned long flags; + +	WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + +	if (tilcdc_crtc->event) { +		dev_err(dev->dev, "already pending page flip!\n"); +		return -EBUSY; +	} + +	drm_framebuffer_reference(fb); + +	crtc->primary->fb = fb; + +	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) +			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); + +	return 0; +} + +static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, +		const struct drm_display_mode *mode, +		struct drm_display_mode *adjusted_mode) +{ +	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + +	if (!tilcdc_crtc->simulate_vesa_sync) +		return true; + +	/* +	 * tilcdc does not generate VESA-compliant sync but aligns +	 * VS on the second edge of HS instead of first edge. +	 * We use adjusted_mode, to fixup sync by aligning both rising +	 * edges and add HSKEW offset to fix the sync. +	 */ +	adjusted_mode->hskew = mode->hsync_end - mode->hsync_start; +	adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW; + +	if (mode->flags & DRM_MODE_FLAG_NHSYNC) { +		adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; +		adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC; +	} else { +		adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC; +		adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC; +	} + +	return true; +} +  static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc,  				    struct drm_crtc_state *state)  { @@ -560,7 +709,6 @@ static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = {  		.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) @@ -756,28 +904,48 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)  	}  	if (stat & LCDC_FIFO_UNDERFLOW) -		dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", +		dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underflow",  				    __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); -		} +	if (stat & LCDC_PL_LOAD_DONE) { +		complete(&tilcdc_crtc->palette_loaded); +		if (priv->rev == 1) +			tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, +				     LCDC_V1_PL_INT_ENA); +		else +			tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, +				     LCDC_V2_PL_INT_ENA); +	} -		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 (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, recovering", __func__, stat); +			queue_work(system_wq, &tilcdc_crtc->recover_work); +			if (priv->rev == 1) +				tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, +					     LCDC_V1_SYNC_LOST_INT_ENA); +			else  				tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,  					     LCDC_SYNC_LOST); -			} +			tilcdc_crtc->sync_lost_count = 0;  		} +	} + +	if (stat & LCDC_FRAME_DONE) { +		tilcdc_crtc->frame_done = true; +		wake_up(&tilcdc_crtc->frame_done_wq); +		/* rev 1 lcdc appears to hang if irq is not disbaled here */ +		if (priv->rev == 1) +			tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, +				     LCDC_V1_FRAME_DONE_INT_ENA); +	} +	/* For revision 2 only */ +	if (priv->rev == 2) {  		/* Indicate to LCDC that the interrupt service routine has  		 * completed, see 13.3.6.1.6 in AM335x TRM.  		 */ @@ -787,7 +955,7 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)  	return IRQ_HANDLED;  } -struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) +int tilcdc_crtc_create(struct drm_device *dev)  {  	struct tilcdc_drm_private *priv = dev->dev_private;  	struct tilcdc_crtc *tilcdc_crtc; @@ -797,21 +965,33 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)  	tilcdc_crtc = devm_kzalloc(dev->dev, sizeof(*tilcdc_crtc), GFP_KERNEL);  	if (!tilcdc_crtc) {  		dev_err(dev->dev, "allocation failed\n"); -		return NULL; +		return -ENOMEM;  	} +	init_completion(&tilcdc_crtc->palette_loaded); +	tilcdc_crtc->palette_base = dmam_alloc_coherent(dev->dev, +					TILCDC_PALETTE_SIZE, +					&tilcdc_crtc->palette_dma_handle, +					GFP_KERNEL | __GFP_ZERO); +	if (!tilcdc_crtc->palette_base) +		return -ENOMEM; +	*tilcdc_crtc->palette_base = TILCDC_PALETTE_FIRST_ENTRY; +  	crtc = &tilcdc_crtc->base;  	ret = tilcdc_plane_init(dev, &tilcdc_crtc->primary);  	if (ret < 0)  		goto fail; +	mutex_init(&tilcdc_crtc->enable_lock); +  	init_waitqueue_head(&tilcdc_crtc->frame_done_wq);  	drm_flip_work_init(&tilcdc_crtc->unref_work,  			"unref", unref_worker);  	spin_lock_init(&tilcdc_crtc->irq_lock); +	INIT_WORK(&tilcdc_crtc->recover_work, tilcdc_crtc_recover_work);  	ret = drm_crtc_init_with_planes(dev, crtc,  					&tilcdc_crtc->primary, @@ -837,13 +1017,15 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)  		if (!crtc->port) { /* This should never happen */  			dev_err(dev->dev, "Port node not found in %s\n",  				dev->dev->of_node->full_name); +			ret = -EINVAL;  			goto fail;  		}  	} -	return crtc; +	priv->crtc = crtc; +	return 0;  fail:  	tilcdc_crtc_destroy(crtc); -	return NULL; +	return -ENOMEM;  } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index a694977c32f4..bd0a3bd07167 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -127,24 +127,16 @@ static int tilcdc_commit(struct drm_device *dev,  	 * 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, 0);  	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;  } @@ -155,15 +147,11 @@ static const struct drm_mode_config_funcs mode_config_funcs = {  	.atomic_commit = tilcdc_commit,  }; -static int modeset_init(struct drm_device *dev) +static void modeset_init(struct drm_device *dev)  {  	struct tilcdc_drm_private *priv = dev->dev_private;  	struct tilcdc_module *mod; -	drm_mode_config_init(dev); - -	priv->crtc = tilcdc_crtc_create(dev); -  	list_for_each_entry(mod, &module_list, list) {  		DBG("loading module: %s", mod->name);  		mod->funcs->modeset_init(mod, dev); @@ -174,8 +162,6 @@ static int modeset_init(struct drm_device *dev)  	dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc);  	dev->mode_config.max_height = 2048;  	dev->mode_config.funcs = &mode_config_funcs; - -	return 0;  }  #ifdef CONFIG_CPU_FREQ @@ -196,22 +182,29 @@ static int cpufreq_transition(struct notifier_block *nb,   * DRM operations:   */ -static int tilcdc_unload(struct drm_device *dev) +static void tilcdc_fini(struct drm_device *dev)  {  	struct tilcdc_drm_private *priv = dev->dev_private; -	tilcdc_remove_external_encoders(dev); +	if (priv->crtc) +		tilcdc_crtc_shutdown(priv->crtc); + +	if (priv->is_registered) +		drm_dev_unregister(dev); -	drm_fbdev_cma_fini(priv->fbdev);  	drm_kms_helper_poll_fini(dev); -	drm_mode_config_cleanup(dev); -	drm_vblank_cleanup(dev); + +	if (priv->fbdev) +		drm_fbdev_cma_fini(priv->fbdev);  	drm_irq_uninstall(dev); +	drm_mode_config_cleanup(dev); +	tilcdc_remove_external_device(dev);  #ifdef CONFIG_CPU_FREQ -	cpufreq_unregister_notifier(&priv->freq_transition, -			CPUFREQ_TRANSITION_NOTIFIER); +	if (priv->freq_transition.notifier_call) +		cpufreq_unregister_notifier(&priv->freq_transition, +					    CPUFREQ_TRANSITION_NOTIFIER);  #endif  	if (priv->clk) @@ -220,61 +213,71 @@ static int tilcdc_unload(struct drm_device *dev)  	if (priv->mmio)  		iounmap(priv->mmio); -	flush_workqueue(priv->wq); -	destroy_workqueue(priv->wq); +	if (priv->wq) { +		flush_workqueue(priv->wq); +		destroy_workqueue(priv->wq); +	}  	dev->dev_private = NULL;  	pm_runtime_disable(dev->dev); -	return 0; +	drm_dev_unref(dev);  } -static int tilcdc_load(struct drm_device *dev, unsigned long flags) +static int tilcdc_init(struct drm_driver *ddrv, struct device *dev)  { -	struct platform_device *pdev = dev->platformdev; -	struct device_node *node = pdev->dev.of_node; +	struct drm_device *ddev; +	struct platform_device *pdev = to_platform_device(dev); +	struct device_node *node = dev->of_node;  	struct tilcdc_drm_private *priv;  	struct resource *res;  	u32 bpp = 0;  	int ret; -	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);  	if (!priv) { -		dev_err(dev->dev, "failed to allocate private data\n"); +		dev_err(dev, "failed to allocate private data\n");  		return -ENOMEM;  	} -	dev->dev_private = priv; +	ddev = drm_dev_alloc(ddrv, dev); +	if (IS_ERR(ddev)) +		return PTR_ERR(ddev); + +	ddev->platformdev = pdev; +	ddev->dev_private = priv; +	platform_set_drvdata(pdev, ddev); +	drm_mode_config_init(ddev);  	priv->is_componentized = -		tilcdc_get_external_components(dev->dev, NULL) > 0; +		tilcdc_get_external_components(dev, NULL) > 0;  	priv->wq = alloc_ordered_workqueue("tilcdc", 0);  	if (!priv->wq) {  		ret = -ENOMEM; -		goto fail_unset_priv; +		goto init_failed;  	}  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res) { -		dev_err(dev->dev, "failed to get memory resource\n"); +		dev_err(dev, "failed to get memory resource\n");  		ret = -EINVAL; -		goto fail_free_wq; +		goto init_failed;  	}  	priv->mmio = ioremap_nocache(res->start, resource_size(res));  	if (!priv->mmio) { -		dev_err(dev->dev, "failed to ioremap\n"); +		dev_err(dev, "failed to ioremap\n");  		ret = -ENOMEM; -		goto fail_free_wq; +		goto init_failed;  	} -	priv->clk = clk_get(dev->dev, "fck"); +	priv->clk = clk_get(dev, "fck");  	if (IS_ERR(priv->clk)) { -		dev_err(dev->dev, "failed to get functional clock\n"); +		dev_err(dev, "failed to get functional clock\n");  		ret = -ENODEV; -		goto fail_iounmap; +		goto init_failed;  	}  #ifdef CONFIG_CPU_FREQ @@ -282,8 +285,9 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  	ret = cpufreq_register_notifier(&priv->freq_transition,  			CPUFREQ_TRANSITION_NOTIFIER);  	if (ret) { -		dev_err(dev->dev, "failed to register cpufreq notifier\n"); -		goto fail_put_clk; +		dev_err(dev, "failed to register cpufreq notifier\n"); +		priv->freq_transition.notifier_call = NULL; +		goto init_failed;  	}  #endif @@ -292,22 +296,22 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  	DBG("Maximum Bandwidth Value %d", priv->max_bandwidth); -	if (of_property_read_u32(node, "ti,max-width", &priv->max_width)) +	if (of_property_read_u32(node, "max-width", &priv->max_width))  		priv->max_width = TILCDC_DEFAULT_MAX_WIDTH;  	DBG("Maximum Horizontal Pixel Width Value %dpixels", priv->max_width); -	if (of_property_read_u32(node, "ti,max-pixelclock", +	if (of_property_read_u32(node, "max-pixelclock",  					&priv->max_pixelclock))  		priv->max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK;  	DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock); -	pm_runtime_enable(dev->dev); +	pm_runtime_enable(dev);  	/* Determine LCD IP Version */ -	pm_runtime_get_sync(dev->dev); -	switch (tilcdc_read(dev, LCDC_PID_REG)) { +	pm_runtime_get_sync(dev); +	switch (tilcdc_read(ddev, LCDC_PID_REG)) {  	case 0x4c100102:  		priv->rev = 1;  		break; @@ -316,14 +320,14 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  		priv->rev = 2;  		break;  	default: -		dev_warn(dev->dev, "Unknown PID Reg value 0x%08x, " -				"defaulting to LCD revision 1\n", -				tilcdc_read(dev, LCDC_PID_REG)); +		dev_warn(dev, "Unknown PID Reg value 0x%08x, " +			"defaulting to LCD revision 1\n", +			tilcdc_read(ddev, LCDC_PID_REG));  		priv->rev = 1;  		break;  	} -	pm_runtime_put_sync(dev->dev); +	pm_runtime_put_sync(dev);  	if (priv->rev == 1) {  		DBG("Revision 1 LCDC supports only RGB565 format"); @@ -356,91 +360,67 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  		}  	} -	ret = modeset_init(dev); +	ret = tilcdc_crtc_create(ddev);  	if (ret < 0) { -		dev_err(dev->dev, "failed to initialize mode setting\n"); -		goto fail_cpufreq_unregister; +		dev_err(dev, "failed to create crtc\n"); +		goto init_failed;  	} - -	platform_set_drvdata(pdev, dev); +	modeset_init(ddev);  	if (priv->is_componentized) { -		ret = component_bind_all(dev->dev, dev); +		ret = component_bind_all(dev, ddev);  		if (ret < 0) -			goto fail_mode_config_cleanup; +			goto init_failed; -		ret = tilcdc_add_external_encoders(dev); +		ret = tilcdc_add_component_encoder(ddev);  		if (ret < 0) -			goto fail_component_cleanup; +			goto init_failed; +	} else { +		ret = tilcdc_attach_external_device(ddev); +		if (ret) +			goto init_failed;  	} -	if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) { -		dev_err(dev->dev, "no encoders/connectors found\n"); +	if (!priv->external_connector && +	    ((priv->num_encoders == 0) || (priv->num_connectors == 0))) { +		dev_err(dev, "no encoders/connectors found\n");  		ret = -ENXIO; -		goto fail_external_cleanup; +		goto init_failed;  	} -	ret = drm_vblank_init(dev, 1); +	ret = drm_vblank_init(ddev, 1);  	if (ret < 0) { -		dev_err(dev->dev, "failed to initialize vblank\n"); -		goto fail_external_cleanup; +		dev_err(dev, "failed to initialize vblank\n"); +		goto init_failed;  	} -	ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); +	ret = drm_irq_install(ddev, platform_get_irq(pdev, 0));  	if (ret < 0) { -		dev_err(dev->dev, "failed to install IRQ handler\n"); -		goto fail_vblank_cleanup; +		dev_err(dev, "failed to install IRQ handler\n"); +		goto init_failed;  	} -	drm_mode_config_reset(dev); +	drm_mode_config_reset(ddev); -	priv->fbdev = drm_fbdev_cma_init(dev, bpp, -			dev->mode_config.num_crtc, -			dev->mode_config.num_connector); +	priv->fbdev = drm_fbdev_cma_init(ddev, bpp, +			ddev->mode_config.num_crtc, +			ddev->mode_config.num_connector);  	if (IS_ERR(priv->fbdev)) {  		ret = PTR_ERR(priv->fbdev); -		goto fail_irq_uninstall; +		goto init_failed;  	} -	drm_kms_helper_poll_init(dev); +	drm_kms_helper_poll_init(ddev); -	return 0; - -fail_irq_uninstall: -	drm_irq_uninstall(dev); - -fail_vblank_cleanup: -	drm_vblank_cleanup(dev); - -fail_component_cleanup: -	if (priv->is_componentized) -		component_unbind_all(dev->dev, dev); - -fail_mode_config_cleanup: -	drm_mode_config_cleanup(dev); - -fail_external_cleanup: -	tilcdc_remove_external_encoders(dev); - -fail_cpufreq_unregister: -	pm_runtime_disable(dev->dev); -#ifdef CONFIG_CPU_FREQ -	cpufreq_unregister_notifier(&priv->freq_transition, -			CPUFREQ_TRANSITION_NOTIFIER); - -fail_put_clk: -#endif -	clk_put(priv->clk); - -fail_iounmap: -	iounmap(priv->mmio); +	ret = drm_dev_register(ddev, 0); +	if (ret) +		goto init_failed; -fail_free_wq: -	flush_workqueue(priv->wq); -	destroy_workqueue(priv->wq); +	priv->is_registered = true; +	return 0; -fail_unset_priv: -	dev->dev_private = NULL; +init_failed: +	tilcdc_fini(ddev);  	return ret;  } @@ -575,9 +555,7 @@ static const struct file_operations fops = {  	.open               = drm_open,  	.release            = drm_release,  	.unlocked_ioctl     = drm_ioctl, -#ifdef CONFIG_COMPAT  	.compat_ioctl       = drm_compat_ioctl, -#endif  	.poll               = drm_poll,  	.read               = drm_read,  	.llseek             = no_llseek, @@ -587,8 +565,6 @@ static const struct file_operations fops = {  static struct drm_driver tilcdc_driver = {  	.driver_features    = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET |  			       DRIVER_PRIME | DRIVER_ATOMIC), -	.load               = tilcdc_load, -	.unload             = tilcdc_unload,  	.lastclose          = tilcdc_lastclose,  	.irq_handler        = tilcdc_irq,  	.get_vblank_counter = drm_vblank_no_hw_counter, @@ -662,10 +638,9 @@ static const struct dev_pm_ops tilcdc_pm_ops = {  /*   * Platform driver:   */ -  static int tilcdc_bind(struct device *dev)  { -	return drm_platform_init(&tilcdc_driver, to_platform_device(dev)); +	return tilcdc_init(&tilcdc_driver, dev);  }  static void tilcdc_unbind(struct device *dev) @@ -676,7 +651,7 @@ static void tilcdc_unbind(struct device *dev)  	if (!ddev->dev_private)  		return; -	drm_put_dev(dev_get_drvdata(dev)); +	tilcdc_fini(dev_get_drvdata(dev));  }  static const struct component_master_ops tilcdc_comp_ops = { @@ -699,7 +674,7 @@ static int tilcdc_pdev_probe(struct platform_device *pdev)  	if (ret < 0)  		return ret;  	else if (ret == 0) -		return drm_platform_init(&tilcdc_driver, pdev); +		return tilcdc_init(&tilcdc_driver, &pdev->dev);  	else  		return component_master_add_with_match(&pdev->dev,  						       &tilcdc_comp_ops, @@ -714,7 +689,7 @@ static int tilcdc_pdev_remove(struct platform_device *pdev)  	if (ret < 0)  		return ret;  	else if (ret == 0) -		drm_put_dev(platform_get_drvdata(pdev)); +		tilcdc_fini(platform_get_drvdata(pdev));  	else  		component_master_del(&pdev->dev, &tilcdc_comp_ops); @@ -723,6 +698,7 @@ static int tilcdc_pdev_remove(struct platform_device *pdev)  static struct of_device_id tilcdc_of_match[] = {  		{ .compatible = "ti,am33xx-tilcdc", }, +		{ .compatible = "ti,da850-tilcdc", },  		{ },  };  MODULE_DEVICE_TABLE(of, tilcdc_of_match); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 9780c37ec4cd..0e71daf5b5cb 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -33,6 +33,7 @@  #include <drm/drm_crtc_helper.h>  #include <drm/drm_gem_cma_helper.h>  #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_bridge.h>  /* Defaulting to pixel clock defined on AM335x */  #define TILCDC_DEFAULT_MAX_PIXELCLOCK  126000 @@ -87,8 +88,12 @@ struct tilcdc_drm_private {  	unsigned int num_connectors;  	struct drm_connector *connectors[8]; -	const struct drm_connector_helper_funcs *connector_funcs[8]; +	struct drm_encoder *external_encoder; +	struct drm_connector *external_connector; +	const struct drm_connector_helper_funcs *connector_funcs; + +	bool is_registered;  	bool is_componentized;  }; @@ -163,7 +168,7 @@ struct tilcdc_panel_info {  #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) -struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev); +int tilcdc_crtc_create(struct drm_device *dev);  irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc);  void tilcdc_crtc_update_clk(struct drm_crtc *crtc);  void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, @@ -172,7 +177,7 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc,  					bool simulate_vesa_sync);  int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode);  int tilcdc_crtc_max_width(struct drm_crtc *crtc); -void tilcdc_crtc_disable(struct drm_crtc *crtc); +void tilcdc_crtc_shutdown(struct drm_crtc *crtc);  int tilcdc_crtc_update_fb(struct drm_crtc *crtc,  		struct drm_framebuffer *fb,  		struct drm_pending_vblank_event *event); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index 68e895021005..c67d7cd7d57e 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -10,6 +10,7 @@  #include <linux/component.h>  #include <linux/of_graph.h> +#include <drm/drm_of.h>  #include "tilcdc_drv.h"  #include "tilcdc_external.h" @@ -27,44 +28,50 @@ static const struct tilcdc_panel_info panel_info_tda998x = {  		.raster_order           = 0,  }; +static const struct tilcdc_panel_info panel_info_default = { +		.ac_bias                = 255, +		.ac_bias_intrpt         = 0, +		.dma_burst_sz           = 16, +		.bpp                    = 16, +		.fdd                    = 0x80, +		.tft_alt_mode           = 0, +		.sync_edge              = 0, +		.sync_ctrl              = 1, +		.raster_order           = 0, +}; +  static int tilcdc_external_mode_valid(struct drm_connector *connector,  				      struct drm_display_mode *mode)  {  	struct tilcdc_drm_private *priv = connector->dev->dev_private; -	int ret, i; +	int ret;  	ret = tilcdc_crtc_mode_valid(priv->crtc, mode);  	if (ret != MODE_OK)  		return ret; -	for (i = 0; i < priv->num_connectors && -		     priv->connectors[i] != connector; i++) -		; - -	BUG_ON(priv->connectors[i] != connector); -	BUG_ON(!priv->connector_funcs[i]); +	BUG_ON(priv->external_connector != connector); +	BUG_ON(!priv->connector_funcs);  	/* If the connector has its own mode_valid call it. */ -	if (!IS_ERR(priv->connector_funcs[i]) && -	    priv->connector_funcs[i]->mode_valid) -		return priv->connector_funcs[i]->mode_valid(connector, mode); +	if (!IS_ERR(priv->connector_funcs) && +	    priv->connector_funcs->mode_valid) +		return priv->connector_funcs->mode_valid(connector, mode);  	return MODE_OK;  } -static int tilcdc_add_external_encoder(struct drm_device *dev, -				       struct drm_connector *connector) +static int tilcdc_add_external_connector(struct drm_device *dev, +					 struct drm_connector *connector)  {  	struct tilcdc_drm_private *priv = dev->dev_private;  	struct drm_connector_helper_funcs *connector_funcs; -	priv->connectors[priv->num_connectors] = connector; -	priv->encoders[priv->num_encoders++] = connector->encoder; - -	/* Only tda998x is supported at the moment. */ -	tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); -	tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); +	/* There should never be more than one connector */ +	if (WARN_ON(priv->external_connector)) +		return -EINVAL; +	priv->external_connector = connector;  	connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs),  				       GFP_KERNEL);  	if (!connector_funcs) @@ -77,56 +84,177 @@ static int tilcdc_add_external_encoder(struct drm_device *dev,  	 * everything else but use our own mode_valid() (above).  	 */  	if (connector->helper_private) { -		priv->connector_funcs[priv->num_connectors] = -			connector->helper_private; -		*connector_funcs = *priv->connector_funcs[priv->num_connectors]; +		priv->connector_funcs =	connector->helper_private; +		*connector_funcs = *priv->connector_funcs;  	} else { -		priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT); +		priv->connector_funcs = ERR_PTR(-ENOENT);  	}  	connector_funcs->mode_valid = tilcdc_external_mode_valid;  	drm_connector_helper_add(connector, connector_funcs); -	priv->num_connectors++; -	dev_dbg(dev->dev, "External encoder '%s' connected\n", -		connector->encoder->name); +	dev_dbg(dev->dev, "External connector '%s' connected\n", +		connector->name);  	return 0;  } -int tilcdc_add_external_encoders(struct drm_device *dev) +static +struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev, +						    struct drm_encoder *encoder)  { -	struct tilcdc_drm_private *priv = dev->dev_private;  	struct drm_connector *connector; -	int num_internal_connectors = priv->num_connectors; - -	list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -		bool found = false; -		int i, ret; - -		for (i = 0; i < num_internal_connectors; i++) -			if (connector == priv->connectors[i]) -				found = true; -		if (!found) { -			ret = tilcdc_add_external_encoder(dev, connector); -			if (ret) -				return ret; -		} +	int i; + +	list_for_each_entry(connector, &ddev->mode_config.connector_list, head) +		for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) +			if (connector->encoder_ids[i] == encoder->base.id) +				return connector; + +	dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n", +		encoder->name, encoder->base.id); + +	return NULL; +} + +int tilcdc_add_component_encoder(struct drm_device *ddev) +{ +	struct tilcdc_drm_private *priv = ddev->dev_private; +	struct drm_connector *connector; +	struct drm_encoder *encoder; + +	list_for_each_entry(encoder, &ddev->mode_config.encoder_list, head) +		if (encoder->possible_crtcs & (1 << priv->crtc->index)) +			break; + +	if (!encoder) { +		dev_err(ddev->dev, "%s: No suitable encoder found\n", __func__); +		return -ENODEV;  	} -	return 0; + +	connector = tilcdc_encoder_find_connector(ddev, encoder); + +	if (!connector) +		return -ENODEV; + +	/* Only tda998x is supported at the moment. */ +	tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); +	tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); + +	return tilcdc_add_external_connector(ddev, connector);  } -void tilcdc_remove_external_encoders(struct drm_device *dev) +void tilcdc_remove_external_device(struct drm_device *dev)  {  	struct tilcdc_drm_private *priv = dev->dev_private; -	int i;  	/* Restore the original helper functions, if any. */ -	for (i = 0; i < priv->num_connectors; i++) -		if (IS_ERR(priv->connector_funcs[i])) -			drm_connector_helper_add(priv->connectors[i], NULL); -		else if (priv->connector_funcs[i]) -			drm_connector_helper_add(priv->connectors[i], -						 priv->connector_funcs[i]); +	if (IS_ERR(priv->connector_funcs)) +		drm_connector_helper_add(priv->external_connector, NULL); +	else if (priv->connector_funcs) +		drm_connector_helper_add(priv->external_connector, +					 priv->connector_funcs); +} + +static const struct drm_encoder_funcs tilcdc_external_encoder_funcs = { +	.destroy	= drm_encoder_cleanup, +}; + +static +int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) +{ +	struct tilcdc_drm_private *priv = ddev->dev_private; +	struct drm_connector *connector; +	int ret; + +	priv->external_encoder->possible_crtcs = BIT(0); +	priv->external_encoder->bridge = bridge; +	bridge->encoder = priv->external_encoder; + +	ret = drm_bridge_attach(ddev, bridge); +	if (ret) { +		dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret); +		return ret; +	} + +	tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_default); + +	connector = tilcdc_encoder_find_connector(ddev, priv->external_encoder); +	if (!connector) +		return -ENODEV; + +	ret = tilcdc_add_external_connector(ddev, connector); + +	return ret; +} + +static int tilcdc_node_has_port(struct device_node *dev_node) +{ +	struct device_node *node; + +	node = of_get_child_by_name(dev_node, "ports"); +	if (!node) +		node = of_get_child_by_name(dev_node, "port"); +	if (!node) +		return 0; +	of_node_put(node); + +	return 1; +} + +static +struct device_node *tilcdc_get_remote_node(struct device_node *node) +{ +	struct device_node *ep; +	struct device_node *parent; + +	if (!tilcdc_node_has_port(node)) +		return NULL; + +	ep = of_graph_get_next_endpoint(node, NULL); +	if (!ep) +		return NULL; + +	parent = of_graph_get_remote_port_parent(ep); +	of_node_put(ep); + +	return parent; +} + +int tilcdc_attach_external_device(struct drm_device *ddev) +{ +	struct tilcdc_drm_private *priv = ddev->dev_private; +	struct device_node *remote_node; +	struct drm_bridge *bridge; +	int ret; + +	remote_node = tilcdc_get_remote_node(ddev->dev->of_node); +	if (!remote_node) +		return 0; + +	bridge = of_drm_find_bridge(remote_node); +	of_node_put(remote_node); +	if (!bridge) +		return -EPROBE_DEFER; + +	priv->external_encoder = devm_kzalloc(ddev->dev, +					      sizeof(*priv->external_encoder), +					      GFP_KERNEL); +	if (!priv->external_encoder) +		return -ENOMEM; + +	ret = drm_encoder_init(ddev, priv->external_encoder, +			       &tilcdc_external_encoder_funcs, +			       DRM_MODE_ENCODER_NONE, NULL); +	if (ret) { +		dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret); +		return ret; +	} + +	ret = tilcdc_attach_bridge(ddev, bridge); +	if (ret) +		drm_encoder_cleanup(priv->external_encoder); + +	return ret;  }  static int dev_match_of(struct device *dev, void *data) @@ -140,16 +268,10 @@ int tilcdc_get_external_components(struct device *dev,  	struct device_node *node;  	struct device_node *ep = NULL;  	int count = 0; +	int ret = 0; -	/* Avoid error print by of_graph_get_next_endpoint() if there -	 * is no ports present. -	 */ -	node = of_get_child_by_name(dev->of_node, "ports"); -	if (!node) -		node = of_get_child_by_name(dev->of_node, "port"); -	if (!node) +	if (!tilcdc_node_has_port(dev->of_node))  		return 0; -	of_node_put(node);  	while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) {  		node = of_graph_get_remote_port_parent(ep); @@ -159,16 +281,20 @@ int tilcdc_get_external_components(struct device *dev,  		}  		dev_dbg(dev, "Subdevice node '%s' found\n", node->name); -		if (match) -			component_match_add(dev, match, dev_match_of, node); -		of_node_put(node); -		count++; -	} -	if (count > 1) { -		dev_err(dev, "Only one external encoder is supported\n"); -		return -EINVAL; +		if (of_device_is_compatible(node, "nxp,tda998x")) { +			if (match) +				drm_of_component_match_add(dev, match, +							   dev_match_of, node); +			ret = 1; +		} + +		of_node_put(node); +		if (count++ > 1) { +			dev_err(dev, "Only one port is supported\n"); +			return -EINVAL; +		}  	} -	return count; +	return ret;  } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.h b/drivers/gpu/drm/tilcdc/tilcdc_external.h index c700e0c1623e..763d18f006c7 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.h @@ -18,8 +18,9 @@  #ifndef __TILCDC_EXTERNAL_H__  #define __TILCDC_EXTERNAL_H__ -int tilcdc_add_external_encoders(struct drm_device *dev); -void tilcdc_remove_external_encoders(struct drm_device *dev); +int tilcdc_add_component_encoder(struct drm_device *dev); +void tilcdc_remove_external_device(struct drm_device *dev);  int tilcdc_get_external_components(struct device *dev,  				   struct component_match **match); +int tilcdc_attach_external_device(struct drm_device *ddev);  #endif /* __TILCDC_SLAVE_H__ */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 2134bb20fbe9..28c3e2f44f64 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -144,13 +144,6 @@ static void panel_connector_destroy(struct drm_connector *connector)  	drm_connector_cleanup(connector);  } -static enum drm_connector_status panel_connector_detect( -		struct drm_connector *connector, -		bool force) -{ -	return connector_status_connected; -} -  static int panel_connector_get_modes(struct drm_connector *connector)  {  	struct drm_device *dev = connector->dev; @@ -197,7 +190,6 @@ static struct drm_encoder *panel_connector_best_encoder(  static const struct drm_connector_funcs panel_connector_funcs = {  	.destroy            = panel_connector_destroy,  	.dpms               = drm_atomic_helper_connector_dpms, -	.detect             = panel_connector_detect,  	.fill_modes         = drm_helper_probe_single_connector_modes,  	.reset              = drm_atomic_helper_connector_reset,  	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -240,8 +232,6 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev,  	if (ret)  		goto fail; -	drm_connector_register(connector); -  	return connector;  fail: diff --git a/drivers/gpu/drm/tilcdc/tilcdc_plane.c b/drivers/gpu/drm/tilcdc/tilcdc_plane.c index 74c65fa859b2..8a6a50d74aff 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_plane.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_plane.c @@ -39,7 +39,7 @@ static int tilcdc_plane_atomic_check(struct drm_plane *plane,  {  	struct drm_crtc_state *crtc_state;  	struct drm_plane_state *old_state = plane->state; -	unsigned int depth, bpp; +	unsigned int pitch;  	if (!state->crtc)  		return 0; @@ -68,8 +68,9 @@ static int tilcdc_plane_atomic_check(struct drm_plane *plane,  		return -EINVAL;  	} -	drm_fb_get_bpp_depth(state->fb->pixel_format, &depth, &bpp); -	if (state->fb->pitches[0] != crtc_state->mode.hdisplay * bpp / 8) { +	pitch = crtc_state->mode.hdisplay * +		drm_format_plane_cpp(state->fb->pixel_format, 0); +	if (state->fb->pitches[0] != pitch) {  		dev_err(plane->dev->dev,  			"Invalid pitch: fb and crtc widths must be the same");  		return -EINVAL; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h index f57c0d62c76a..9d528c0a67a4 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_regs.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -34,11 +34,14 @@  /* LCDC DMA Control Register */  #define LCDC_DMA_BURST_SIZE(x)                   ((x) << 4) +#define LCDC_DMA_BURST_SIZE_MASK                 ((0x7) << 4)  #define LCDC_DMA_BURST_1                         0x0  #define LCDC_DMA_BURST_2                         0x1  #define LCDC_DMA_BURST_4                         0x2  #define LCDC_DMA_BURST_8                         0x3  #define LCDC_DMA_BURST_16                        0x4 +#define LCDC_DMA_FIFO_THRESHOLD(x)               ((x) << 8) +#define LCDC_DMA_FIFO_THRESHOLD_MASK             ((0x3) << 8)  #define LCDC_V1_END_OF_FRAME_INT_ENA             BIT(2)  #define LCDC_V2_END_OF_FRAME0_INT_ENA            BIT(8)  #define LCDC_V2_END_OF_FRAME1_INT_ENA            BIT(9) @@ -46,10 +49,12 @@  /* LCDC Control Register */  #define LCDC_CLK_DIVISOR(x)                      ((x) << 8) +#define LCDC_CLK_DIVISOR_MASK                    ((0xFF) << 8)  #define LCDC_RASTER_MODE                         0x01  /* LCDC Raster Control Register */  #define LCDC_PALETTE_LOAD_MODE(x)                ((x) << 20) +#define LCDC_PALETTE_LOAD_MODE_MASK              ((0x3) << 20)  #define PALETTE_AND_DATA                         0x00  #define PALETTE_ONLY                             0x01  #define DATA_ONLY                                0x02 @@ -61,6 +66,8 @@  #define LCDC_V2_UNDERFLOW_INT_ENA                BIT(5)  #define LCDC_V1_PL_INT_ENA                       BIT(4)  #define LCDC_V2_PL_INT_ENA                       BIT(6) +#define LCDC_V1_SYNC_LOST_INT_ENA                BIT(5) +#define LCDC_V1_FRAME_DONE_INT_ENA               BIT(3)  #define LCDC_MONOCHROME_MODE                     BIT(1)  #define LCDC_RASTER_ENABLE                       BIT(0)  #define LCDC_TFT_ALT_ENABLE                      BIT(23) @@ -74,7 +81,9 @@  /* LCDC Raster Timing 2 Register */  #define LCDC_AC_BIAS_TRANSITIONS_PER_INT(x)      ((x) << 16) +#define LCDC_AC_BIAS_TRANSITIONS_PER_INT_MASK    ((0xF) << 16)  #define LCDC_AC_BIAS_FREQUENCY(x)                ((x) << 8) +#define LCDC_AC_BIAS_FREQUENCY_MASK              ((0xFF) << 8)  #define LCDC_SYNC_CTRL                           BIT(25)  #define LCDC_SYNC_EDGE                           BIT(24)  #define LCDC_INVERT_PIXEL_CLOCK                  BIT(22) @@ -139,6 +148,12 @@ static inline u32 tilcdc_read(struct drm_device *dev, u32 reg)  	return ioread32(priv->mmio + reg);  } +static inline void tilcdc_write_mask(struct drm_device *dev, u32 reg, +				     u32 val, u32 mask) +{ +	tilcdc_write(dev, reg, (tilcdc_read(dev, reg) & ~mask) | (val & mask)); +} +  static inline void tilcdc_set(struct drm_device *dev, u32 reg, u32 mask)  {  	tilcdc_write(dev, reg, tilcdc_read(dev, reg) | mask); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 458043a53995..aabfad882e23 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -249,8 +249,6 @@ static struct drm_connector *tfp410_connector_create(struct drm_device *dev,  	if (ret)  		goto fail; -	drm_connector_register(connector); -  	return connector;  fail:  |