diff options
Diffstat (limited to 'drivers/gpu/drm/tilcdc')
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 331 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.c | 138 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.h | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_panel.c | 29 | ||||
| -rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | 33 | 
5 files changed, 299 insertions, 238 deletions
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 7d07733bdc86..051e5e1b7ad6 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -21,25 +21,31 @@  #include "tilcdc_drv.h"  #include "tilcdc_regs.h" +#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 +  struct tilcdc_crtc {  	struct drm_crtc base;  	const struct tilcdc_panel_info *info; -	uint32_t dirty; -	dma_addr_t start, end;  	struct drm_pending_vblank_event *event;  	int dpms;  	wait_queue_head_t frame_done_wq;  	bool frame_done; +	spinlock_t irq_lock; -	/* fb currently set to scanout 0/1: */ -	struct drm_framebuffer *scanout[2]; +	ktime_t last_vblank; + +	struct drm_framebuffer *curr_fb; +	struct drm_framebuffer *next_fb;  	/* for deferred fb unref's: */  	struct drm_flip_work unref_work;  	/* Only set if an external encoder is connected */  	bool simulate_vesa_sync; + +	int sync_lost_count; +	bool frame_intact;  };  #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) @@ -54,79 +60,53 @@ static void unref_worker(struct drm_flip_work *work, void *val)  	mutex_unlock(&dev->mode_config.mutex);  } -static void set_scanout(struct drm_crtc *crtc, int n) -{ -	static const uint32_t base_reg[] = { -			LCDC_DMA_FB_BASE_ADDR_0_REG, -			LCDC_DMA_FB_BASE_ADDR_1_REG, -	}; -	static const uint32_t ceil_reg[] = { -			LCDC_DMA_FB_CEILING_ADDR_0_REG, -			LCDC_DMA_FB_CEILING_ADDR_1_REG, -	}; -	static const uint32_t stat[] = { -			LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1, -	}; -	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); -	struct drm_device *dev = crtc->dev; -	struct tilcdc_drm_private *priv = dev->dev_private; - -	pm_runtime_get_sync(dev->dev); -	tilcdc_write(dev, base_reg[n], tilcdc_crtc->start); -	tilcdc_write(dev, ceil_reg[n], tilcdc_crtc->end); -	if (tilcdc_crtc->scanout[n]) { -		drm_flip_work_queue(&tilcdc_crtc->unref_work, tilcdc_crtc->scanout[n]); -		drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); -	} -	tilcdc_crtc->scanout[n] = crtc->primary->fb; -	drm_framebuffer_reference(tilcdc_crtc->scanout[n]); -	tilcdc_crtc->dirty &= ~stat[n]; -	pm_runtime_put_sync(dev->dev); -} - -static void update_scanout(struct drm_crtc *crtc) +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 drm_framebuffer *fb = crtc->primary->fb;  	struct drm_gem_cma_object *gem;  	unsigned int depth, bpp; +	dma_addr_t start, end;  	drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);  	gem = drm_fb_cma_get_gem_obj(fb, 0); -	tilcdc_crtc->start = gem->paddr + fb->offsets[0] + -			(crtc->y * fb->pitches[0]) + (crtc->x * bpp/8); +	start = gem->paddr + fb->offsets[0] + +		crtc->y * fb->pitches[0] + +		crtc->x * bpp / 8; -	tilcdc_crtc->end = tilcdc_crtc->start + -			(crtc->mode.vdisplay * fb->pitches[0]); +	end = start + (crtc->mode.vdisplay * fb->pitches[0]); -	if (tilcdc_crtc->dpms == DRM_MODE_DPMS_ON) { -		/* already enabled, so just mark the frames that need -		 * updating and they will be updated on vblank: -		 */ -		tilcdc_crtc->dirty |= LCDC_END_OF_FRAME0 | LCDC_END_OF_FRAME1; -		drm_vblank_get(dev, 0); -	} else { -		/* not enabled yet, so update registers immediately: */ -		set_scanout(crtc, 0); -		set_scanout(crtc, 1); -	} +	tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, start); +	tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, end); + +	if (tilcdc_crtc->curr_fb) +		drm_flip_work_queue(&tilcdc_crtc->unref_work, +			tilcdc_crtc->curr_fb); + +	tilcdc_crtc->curr_fb = fb;  } -static void start(struct drm_crtc *crtc) +static void reset(struct drm_crtc *crtc)  {  	struct drm_device *dev = crtc->dev;  	struct tilcdc_drm_private *priv = dev->dev_private; -	if (priv->rev == 2) { -		tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); -		msleep(1); -		tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); -		msleep(1); -	} +	if (priv->rev != 2) +		return; -	tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); +	tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); +	usleep_range(250, 1000); +	tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); +} + +static void start(struct drm_crtc *crtc) +{ +	struct drm_device *dev = crtc->dev; + +	reset(crtc); + +	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);  } @@ -138,17 +118,31 @@ static void stop(struct drm_crtc *crtc)  	tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);  } -static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode);  static void tilcdc_crtc_destroy(struct drm_crtc *crtc)  {  	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);  	tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +	of_node_put(crtc->port);  	drm_crtc_cleanup(crtc);  	drm_flip_work_cleanup(&tilcdc_crtc->unref_work); +} + +static int tilcdc_verify_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{ +	struct drm_device *dev = crtc->dev; +	unsigned int depth, bpp; + +	drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); -	kfree(tilcdc_crtc); +	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; +	} + +	return 0;  }  static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, @@ -158,20 +152,48 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,  {  	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");  		return -EBUSY;  	} +	drm_framebuffer_reference(fb); +  	crtc->primary->fb = fb; + +	pm_runtime_get_sync(dev->dev); + +	spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); + +	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 +		tilcdc_crtc->next_fb = fb; +  	tilcdc_crtc->event = event; -	update_scanout(crtc); + +	spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); + +	pm_runtime_put_sync(dev->dev);  	return 0;  } -static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) +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; @@ -186,10 +208,8 @@ static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)  	tilcdc_crtc->dpms = mode; -	pm_runtime_get_sync(dev->dev); -  	if (mode == DRM_MODE_DPMS_ON) { -		pm_runtime_forbid(dev->dev); +		pm_runtime_get_sync(dev->dev);  		start(crtc);  	} else {  		tilcdc_crtc->frame_done = false; @@ -207,10 +227,23 @@ static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)  			if (ret == 0)  				dev_err(dev->dev, "timeout waiting for framedone\n");  		} -		pm_runtime_allow(dev->dev); -	} -	pm_runtime_put_sync(dev->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); +	}  }  static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, @@ -272,6 +305,10 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,  	if (WARN_ON(!info))  		return -EINVAL; +	ret = tilcdc_verify_fb(crtc, crtc->primary->fb); +	if (ret) +		return ret; +  	pm_runtime_get_sync(dev->dev);  	/* Configure the Burst Size and fifo threshold of DMA: */ @@ -419,8 +456,10 @@ 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); + +	set_scanout(crtc, crtc->primary->fb); -	update_scanout(crtc);  	tilcdc_crtc_update_clk(crtc);  	pm_runtime_put_sync(dev->dev); @@ -431,7 +470,21 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,  static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,  		struct drm_framebuffer *old_fb)  { -	update_scanout(crtc); +	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); + +	pm_runtime_get_sync(dev->dev); + +	set_scanout(crtc, crtc->primary->fb); + +	pm_runtime_put_sync(dev->dev); +  	return 0;  } @@ -573,7 +626,8 @@ void tilcdc_crtc_update_clk(struct drm_crtc *crtc)  	struct drm_device *dev = crtc->dev;  	struct tilcdc_drm_private *priv = dev->dev_private;  	int dpms = tilcdc_crtc->dpms; -	unsigned int lcd_clk, div; +	unsigned long lcd_clk; +	const unsigned clkdiv = 2; /* using a fixed divider of 2 */  	int ret;  	pm_runtime_get_sync(dev->dev); @@ -581,22 +635,21 @@ void tilcdc_crtc_update_clk(struct drm_crtc *crtc)  	if (dpms == DRM_MODE_DPMS_ON)  		tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); -	/* in raster mode, minimum divisor is 2: */ -	ret = clk_set_rate(priv->disp_clk, crtc->mode.clock * 1000 * 2); -	if (ret) { +	/* 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);  		goto out;  	}  	lcd_clk = clk_get_rate(priv->clk); -	div = lcd_clk / (crtc->mode.clock * 1000); -	DBG("lcd_clk=%u, mode clock=%d, div=%u", lcd_clk, crtc->mode.clock, div); -	DBG("fck=%lu, dpll_disp_ck=%lu", clk_get_rate(priv->clk), clk_get_rate(priv->disp_clk)); +	DBG("lcd_clk=%lu, mode clock=%d, div=%u", +		lcd_clk, crtc->mode.clock, clkdiv);  	/* Configure the LCD clock divisor. */ -	tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(div) | +	tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) |  			LCDC_RASTER_MODE);  	if (priv->rev == 2) @@ -611,44 +664,58 @@ out:  	pm_runtime_put_sync(dev->dev);  } +#define SYNC_LOST_COUNT_LIMIT 50 +  irqreturn_t tilcdc_crtc_irq(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; -	uint32_t stat = tilcdc_read_irqstatus(dev); +	uint32_t stat; -	if ((stat & LCDC_SYNC_LOST) && (stat & LCDC_FIFO_UNDERFLOW)) { -		stop(crtc); -		dev_err(dev->dev, "error: %08x\n", stat); -		tilcdc_clear_irqstatus(dev, stat); -		start(crtc); -	} else if (stat & LCDC_PL_LOAD_DONE) { -		tilcdc_clear_irqstatus(dev, stat); -	} else { -		struct drm_pending_vblank_event *event; +	stat = tilcdc_read_irqstatus(dev); +	tilcdc_clear_irqstatus(dev, stat); + +	if (stat & LCDC_END_OF_FRAME0) {  		unsigned long flags; -		uint32_t dirty = tilcdc_crtc->dirty & stat; +		bool skip_event = false; +		ktime_t now; -		tilcdc_clear_irqstatus(dev, stat); +		now = ktime_get(); -		if (dirty & LCDC_END_OF_FRAME0) -			set_scanout(crtc, 0); +		drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); -		if (dirty & LCDC_END_OF_FRAME1) -			set_scanout(crtc, 1); +		spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); + +		tilcdc_crtc->last_vblank = now; + +		if (tilcdc_crtc->next_fb) { +			set_scanout(crtc, tilcdc_crtc->next_fb); +			tilcdc_crtc->next_fb = NULL; +			skip_event = true; +		} + +		spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);  		drm_handle_vblank(dev, 0); -		spin_lock_irqsave(&dev->event_lock, flags); -		event = tilcdc_crtc->event; -		tilcdc_crtc->event = NULL; -		if (event) -			drm_send_vblank_event(dev, 0, event); -		spin_unlock_irqrestore(&dev->event_lock, flags); +		if (!skip_event) { +			struct drm_pending_vblank_event *event; + +			spin_lock_irqsave(&dev->event_lock, flags); + +			event = tilcdc_crtc->event; +			tilcdc_crtc->event = NULL; +			if (event) +				drm_send_vblank_event(dev, 0, event); + +			spin_unlock_irqrestore(&dev->event_lock, flags); +		} -		if (dirty && !tilcdc_crtc->dirty) -			drm_vblank_put(dev, 0); +		if (tilcdc_crtc->frame_intact) +			tilcdc_crtc->sync_lost_count = 0; +		else +			tilcdc_crtc->frame_intact = true;  	}  	if (priv->rev == 2) { @@ -659,36 +726,34 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)  		tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0);  	} -	return IRQ_HANDLED; -} +	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); +			tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, +				     LCDC_SYNC_LOST); +		} +	} -void tilcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) -{ -	struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); -	struct drm_pending_vblank_event *event; -	struct drm_device *dev = crtc->dev; -	unsigned long flags; +	if (stat & LCDC_FIFO_UNDERFLOW) +		dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", +				    __func__, stat); -	/* Destroy the pending vertical blanking event associated with the -	 * pending page flip, if any, and disable vertical blanking interrupts. -	 */ -	spin_lock_irqsave(&dev->event_lock, flags); -	event = tilcdc_crtc->event; -	if (event && event->base.file_priv == file) { -		tilcdc_crtc->event = NULL; -		event->base.destroy(&event->base); -		drm_vblank_put(dev, 0); -	} -	spin_unlock_irqrestore(&dev->event_lock, flags); +	return IRQ_HANDLED;  }  struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)  { +	struct tilcdc_drm_private *priv = dev->dev_private;  	struct tilcdc_crtc *tilcdc_crtc;  	struct drm_crtc *crtc;  	int ret; -	tilcdc_crtc = kzalloc(sizeof(*tilcdc_crtc), GFP_KERNEL); +	tilcdc_crtc = devm_kzalloc(dev->dev, sizeof(*tilcdc_crtc), GFP_KERNEL);  	if (!tilcdc_crtc) {  		dev_err(dev->dev, "allocation failed\n");  		return NULL; @@ -702,12 +767,32 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)  	drm_flip_work_init(&tilcdc_crtc->unref_work,  			"unref", unref_worker); +	spin_lock_init(&tilcdc_crtc->irq_lock); +  	ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs);  	if (ret < 0)  		goto fail;  	drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); +	if (priv->is_componentized) { +		struct device_node *ports = +			of_get_child_by_name(dev->dev->of_node, "ports"); + +		if (ports) { +			crtc->port = of_get_child_by_name(ports, "port"); +			of_node_put(ports); +		} else { +			crtc->port = +				of_get_child_by_name(dev->dev->of_node, "port"); +		} +		if (!crtc->port) { /* This should never happen */ +			dev_err(dev->dev, "Port node not found in %s\n", +				dev->dev->of_node->full_name); +			goto fail; +		} +	} +  	return crtc;  fail: diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index d7f5b897c6c5..709bc903524d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -18,6 +18,8 @@  /* LCDC DRM driver, based on da8xx-fb */  #include <linux/component.h> +#include <linux/pinctrl/consumer.h> +#include <linux/suspend.h>  #include "tilcdc_drv.h"  #include "tilcdc_regs.h" @@ -110,6 +112,8 @@ 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_remove_external_encoders(dev);  	drm_fbdev_cma_fini(priv->fbdev); @@ -139,11 +143,11 @@ static int tilcdc_unload(struct drm_device *dev)  	pm_runtime_disable(dev->dev); -	kfree(priv); -  	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; @@ -154,8 +158,12 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  	u32 bpp = 0;  	int ret; -	priv = kzalloc(sizeof(*priv), GFP_KERNEL); -	if (!priv) { +	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) {  		dev_err(dev->dev, "failed to allocate private data\n");  		return -ENOMEM;  	} @@ -168,7 +176,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  	priv->wq = alloc_ordered_workqueue("tilcdc", 0);  	if (!priv->wq) {  		ret = -ENOMEM; -		goto fail_free_priv; +		goto fail_unset_priv;  	}  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -192,13 +200,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  		goto fail_iounmap;  	} -	priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); -	if (IS_ERR(priv->clk)) { -		dev_err(dev->dev, "failed to get display clock\n"); -		ret = -ENODEV; -		goto fail_put_clk; -	} -  #ifdef CONFIG_CPU_FREQ  	priv->lcd_fck_rate = clk_get_rate(priv->clk);  	priv->freq_transition.notifier_call = cpufreq_transition; @@ -206,7 +207,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  			CPUFREQ_TRANSITION_NOTIFIER);  	if (ret) {  		dev_err(dev->dev, "failed to register cpufreq notifier\n"); -		goto fail_put_disp_clk; +		goto fail_put_clk;  	}  #endif @@ -227,7 +228,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)  	DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock);  	pm_runtime_enable(dev->dev); -	pm_runtime_irq_safe(dev->dev);  	/* Determine LCD IP Version */  	pm_runtime_get_sync(dev->dev); @@ -330,11 +330,9 @@ fail_cpufreq_unregister:  #ifdef CONFIG_CPU_FREQ  	cpufreq_unregister_notifier(&priv->freq_transition,  			CPUFREQ_TRANSITION_NOTIFIER); -fail_put_disp_clk: -	clk_put(priv->disp_clk); -#endif  fail_put_clk: +#endif  	clk_put(priv->clk);  fail_iounmap: @@ -344,17 +342,10 @@ fail_free_wq:  	flush_workqueue(priv->wq);  	destroy_workqueue(priv->wq); -fail_free_priv: +fail_unset_priv:  	dev->dev_private = NULL; -	kfree(priv); -	return ret; -} -static void tilcdc_preclose(struct drm_device *dev, struct drm_file *file) -{ -	struct tilcdc_drm_private *priv = dev->dev_private; - -	tilcdc_crtc_cancel_page_flip(priv->crtc, file); +	return ret;  }  static void tilcdc_lastclose(struct drm_device *dev) @@ -380,10 +371,14 @@ 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) +	if (priv->rev == 1) {  		tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); -	else -		tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_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;  } @@ -398,43 +393,21 @@ static void tilcdc_irq_uninstall(struct drm_device *dev)  				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_clear(dev, LCDC_INT_ENABLE_SET_REG, +		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_V2_END_OF_FRAME1_INT_ENA | -			LCDC_FRAME_DONE); +			LCDC_V2_END_OF_FRAME0_INT_ENA | +			LCDC_FRAME_DONE | LCDC_SYNC_LOST);  	} - -} - -static void enable_vblank(struct drm_device *dev, bool enable) -{ -	struct tilcdc_drm_private *priv = dev->dev_private; -	u32 reg, mask; - -	if (priv->rev == 1) { -		reg = LCDC_DMA_CTRL_REG; -		mask = LCDC_V1_END_OF_FRAME_INT_ENA; -	} else { -		reg = LCDC_INT_ENABLE_SET_REG; -		mask = LCDC_V2_END_OF_FRAME0_INT_ENA | -			LCDC_V2_END_OF_FRAME1_INT_ENA | LCDC_FRAME_DONE; -	} - -	if (enable) -		tilcdc_set(dev, reg, mask); -	else -		tilcdc_clear(dev, reg, mask);  }  static int tilcdc_enable_vblank(struct drm_device *dev, unsigned int pipe)  { -	enable_vblank(dev, true);  	return 0;  }  static void tilcdc_disable_vblank(struct drm_device *dev, unsigned int pipe)  { -	enable_vblank(dev, false); +	return;  }  #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP) @@ -461,13 +434,22 @@ static const struct {  		/* new in revision 2: */  		REG(2, false, LCDC_RAW_STAT_REG),  		REG(2, false, LCDC_MASKED_STAT_REG), -		REG(2, false, LCDC_INT_ENABLE_SET_REG), +		REG(2, true, LCDC_INT_ENABLE_SET_REG),  		REG(2, false, LCDC_INT_ENABLE_CLR_REG),  		REG(2, false, LCDC_END_OF_INT_IND_REG),  		REG(2, true,  LCDC_CLK_ENABLE_REG), -		REG(2, true,  LCDC_INT_ENABLE_SET_REG),  #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 @@ -554,10 +536,10 @@ static const struct file_operations fops = {  };  static struct drm_driver tilcdc_driver = { -	.driver_features    = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, +	.driver_features    = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | +			       DRIVER_PRIME),  	.load               = tilcdc_load,  	.unload             = tilcdc_unload, -	.preclose           = tilcdc_preclose,  	.lastclose          = tilcdc_lastclose,  	.set_busid          = drm_platform_set_busid,  	.irq_handler        = tilcdc_irq, @@ -572,6 +554,16 @@ static struct drm_driver tilcdc_driver = {  	.dumb_create        = drm_gem_cma_dumb_create,  	.dumb_map_offset    = drm_gem_cma_dumb_map_offset,  	.dumb_destroy       = drm_gem_dumb_destroy, + +	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd, +	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle, +	.gem_prime_import	= drm_gem_prime_import, +	.gem_prime_export	= drm_gem_prime_export, +	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table, +	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, +	.gem_prime_vmap		= drm_gem_cma_prime_vmap, +	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap, +	.gem_prime_mmap		= drm_gem_cma_prime_mmap,  #ifdef CONFIG_DEBUG_FS  	.debugfs_init       = tilcdc_debugfs_init,  	.debugfs_cleanup    = tilcdc_debugfs_cleanup, @@ -597,11 +589,24 @@ static int tilcdc_pm_suspend(struct device *dev)  	drm_kms_helper_poll_disable(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;  } @@ -611,10 +616,17 @@ static int tilcdc_pm_resume(struct device *dev)  	struct tilcdc_drm_private *priv = ddev->dev_private;  	unsigned i, n = 0; -	/* 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++]); +	/* 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); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index e863ad0d26fe..c1de18bae415 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -49,7 +49,6 @@  struct tilcdc_drm_private {  	void __iomem *mmio; -	struct clk *disp_clk;    /* display dpll */  	struct clk *clk;         /* functional clock */  	int rev;                 /* IP revision */ @@ -67,7 +66,8 @@ struct tilcdc_drm_private {  	uint32_t max_width;  	/* register contents saved across suspend/resume: */ -	u32 saved_register[12]; +	u32 *saved_register; +	bool ctx_valid;  #ifdef CONFIG_CPU_FREQ  	struct notifier_block freq_transition; @@ -163,7 +163,6 @@ struct tilcdc_panel_info {  #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)  struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev); -void tilcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);  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,5 +171,6 @@ 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_dpms(struct drm_crtc *crtc, int mode);  #endif /* __TILCDC_DRV_H__ */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 4dda6e2f464b..ff7774c17d7c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -45,14 +45,6 @@ struct panel_encoder {  };  #define to_panel_encoder(x) container_of(x, struct panel_encoder, base) - -static void panel_encoder_destroy(struct drm_encoder *encoder) -{ -	struct panel_encoder *panel_encoder = to_panel_encoder(encoder); -	drm_encoder_cleanup(encoder); -	kfree(panel_encoder); -} -  static void panel_encoder_dpms(struct drm_encoder *encoder, int mode)  {  	struct panel_encoder *panel_encoder = to_panel_encoder(encoder); @@ -70,14 +62,6 @@ static void panel_encoder_dpms(struct drm_encoder *encoder, int mode)  					 mode == DRM_MODE_DPMS_ON ? 1 : 0);  } -static bool panel_encoder_mode_fixup(struct drm_encoder *encoder, -		const struct drm_display_mode *mode, -		struct drm_display_mode *adjusted_mode) -{ -	/* nothing needed */ -	return true; -} -  static void panel_encoder_prepare(struct drm_encoder *encoder)  {  	struct panel_encoder *panel_encoder = to_panel_encoder(encoder); @@ -98,12 +82,11 @@ static void panel_encoder_mode_set(struct drm_encoder *encoder,  }  static const struct drm_encoder_funcs panel_encoder_funcs = { -		.destroy        = panel_encoder_destroy, +		.destroy        = drm_encoder_cleanup,  };  static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = {  		.dpms           = panel_encoder_dpms, -		.mode_fixup     = panel_encoder_mode_fixup,  		.prepare        = panel_encoder_prepare,  		.commit         = panel_encoder_commit,  		.mode_set       = panel_encoder_mode_set, @@ -116,7 +99,8 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev,  	struct drm_encoder *encoder;  	int ret; -	panel_encoder = kzalloc(sizeof(*panel_encoder), GFP_KERNEL); +	panel_encoder = devm_kzalloc(dev->dev, sizeof(*panel_encoder), +				     GFP_KERNEL);  	if (!panel_encoder) {  		dev_err(dev->dev, "allocation failed\n");  		return NULL; @@ -137,7 +121,7 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev,  	return encoder;  fail: -	panel_encoder_destroy(encoder); +	drm_encoder_cleanup(encoder);  	return NULL;  } @@ -156,10 +140,8 @@ struct panel_connector {  static void panel_connector_destroy(struct drm_connector *connector)  { -	struct panel_connector *panel_connector = to_panel_connector(connector);  	drm_connector_unregister(connector);  	drm_connector_cleanup(connector); -	kfree(panel_connector);  }  static enum drm_connector_status panel_connector_detect( @@ -232,7 +214,8 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev,  	struct drm_connector *connector;  	int ret; -	panel_connector = kzalloc(sizeof(*panel_connector), GFP_KERNEL); +	panel_connector = devm_kzalloc(dev->dev, sizeof(*panel_connector), +				       GFP_KERNEL);  	if (!panel_connector) {  		dev_err(dev->dev, "allocation failed\n");  		return NULL; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 5052a8af7ecb..7716f42f8aab 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -54,14 +54,6 @@ struct tfp410_encoder {  };  #define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base) - -static void tfp410_encoder_destroy(struct drm_encoder *encoder) -{ -	struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); -	drm_encoder_cleanup(encoder); -	kfree(tfp410_encoder); -} -  static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode)  {  	struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); @@ -80,14 +72,6 @@ static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode)  	tfp410_encoder->dpms = mode;  } -static bool tfp410_encoder_mode_fixup(struct drm_encoder *encoder, -		const struct drm_display_mode *mode, -		struct drm_display_mode *adjusted_mode) -{ -	/* nothing needed */ -	return true; -} -  static void tfp410_encoder_prepare(struct drm_encoder *encoder)  {  	tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); @@ -107,12 +91,11 @@ static void tfp410_encoder_mode_set(struct drm_encoder *encoder,  }  static const struct drm_encoder_funcs tfp410_encoder_funcs = { -		.destroy        = tfp410_encoder_destroy, +		.destroy        = drm_encoder_cleanup,  };  static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = {  		.dpms           = tfp410_encoder_dpms, -		.mode_fixup     = tfp410_encoder_mode_fixup,  		.prepare        = tfp410_encoder_prepare,  		.commit         = tfp410_encoder_commit,  		.mode_set       = tfp410_encoder_mode_set, @@ -125,7 +108,8 @@ static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev,  	struct drm_encoder *encoder;  	int ret; -	tfp410_encoder = kzalloc(sizeof(*tfp410_encoder), GFP_KERNEL); +	tfp410_encoder = devm_kzalloc(dev->dev, sizeof(*tfp410_encoder), +				      GFP_KERNEL);  	if (!tfp410_encoder) {  		dev_err(dev->dev, "allocation failed\n");  		return NULL; @@ -147,7 +131,7 @@ static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev,  	return encoder;  fail: -	tfp410_encoder_destroy(encoder); +	drm_encoder_cleanup(encoder);  	return NULL;  } @@ -166,10 +150,8 @@ struct tfp410_connector {  static void tfp410_connector_destroy(struct drm_connector *connector)  { -	struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);  	drm_connector_unregister(connector);  	drm_connector_cleanup(connector); -	kfree(tfp410_connector);  }  static enum drm_connector_status tfp410_connector_detect( @@ -237,7 +219,8 @@ static struct drm_connector *tfp410_connector_create(struct drm_device *dev,  	struct drm_connector *connector;  	int ret; -	tfp410_connector = kzalloc(sizeof(*tfp410_connector), GFP_KERNEL); +	tfp410_connector = devm_kzalloc(dev->dev, sizeof(*tfp410_connector), +					GFP_KERNEL);  	if (!tfp410_connector) {  		dev_err(dev->dev, "allocation failed\n");  		return NULL; @@ -322,7 +305,7 @@ static int tfp410_probe(struct platform_device *pdev)  		return -ENXIO;  	} -	tfp410_mod = kzalloc(sizeof(*tfp410_mod), GFP_KERNEL); +	tfp410_mod = devm_kzalloc(&pdev->dev, sizeof(*tfp410_mod), GFP_KERNEL);  	if (!tfp410_mod)  		return -ENOMEM; @@ -375,7 +358,6 @@ fail_adapter:  	i2c_put_adapter(tfp410_mod->i2c);  fail: -	kfree(tfp410_mod);  	tilcdc_module_cleanup(mod);  	return ret;  } @@ -389,7 +371,6 @@ static int tfp410_remove(struct platform_device *pdev)  	gpio_free(tfp410_mod->gpio);  	tilcdc_module_cleanup(mod); -	kfree(tfp410_mod);  	return 0;  }  |